diff --git a/fern/apis/accounts/generators.yaml b/fern/apis/accounts/generators.yaml
new file mode 100644
index 000000000..196e6ee73
--- /dev/null
+++ b/fern/apis/accounts/generators.yaml
@@ -0,0 +1,3 @@
+api:
+ specs:
+ - openapi: ../../api-specs/alchemy/rest/accounts.json
diff --git a/fern/apis/admin/generators.yaml b/fern/apis/admin/generators.yaml
new file mode 100644
index 000000000..cc86a6788
--- /dev/null
+++ b/fern/apis/admin/generators.yaml
@@ -0,0 +1,3 @@
+api:
+ specs:
+ - openapi: ../../api-specs/alchemy/rest/admin.json
diff --git a/fern/apis/bundler/generators.yaml b/fern/apis/bundler/generators.yaml
new file mode 100644
index 000000000..3b52cedb4
--- /dev/null
+++ b/fern/apis/bundler/generators.yaml
@@ -0,0 +1,3 @@
+api:
+ specs:
+ - openrpc: ../../api-specs/alchemy/json-rpc/bundler.json
diff --git a/fern/apis/gas-manager-coverage/generators.yaml b/fern/apis/gas-manager-coverage/generators.yaml
new file mode 100644
index 000000000..5b5027aca
--- /dev/null
+++ b/fern/apis/gas-manager-coverage/generators.yaml
@@ -0,0 +1,3 @@
+api:
+ specs:
+ - openrpc: ../../api-specs/alchemy/json-rpc/gas-manager-coverage.json
diff --git a/fern/apis/userop-sim/generators.yaml b/fern/apis/userop-sim/generators.yaml
new file mode 100644
index 000000000..445759157
--- /dev/null
+++ b/fern/apis/userop-sim/generators.yaml
@@ -0,0 +1,3 @@
+api:
+ specs:
+ - openrpc: ../../api-specs/alchemy/json-rpc/userop-sim.json
diff --git a/fern/apis/wallet-api/generators.yml b/fern/apis/wallet-api/generators.yml
new file mode 100644
index 000000000..d0d03b711
--- /dev/null
+++ b/fern/apis/wallet-api/generators.yml
@@ -0,0 +1,3 @@
+api:
+ specs:
+ - openrpc: ../../api-specs/alchemy/json-rpc/wallet-api.json
diff --git a/fern/docs.yml b/fern/docs.yml
index e53bc6c53..64d5512cd 100644
--- a/fern/docs.yml
+++ b/fern/docs.yml
@@ -82,8 +82,8 @@ experimental:
openapi-parser-v3: true
mdx-components:
- ./components
+ - wallets/components
-# TODO: is it possible to add a Font Awesome icon to a navbar-link?
navbar-links:
- type: filled
text: Go to Dashboard
@@ -901,11 +901,389 @@ navigation:
- tab: wallets
layout:
- - page: Wallets Placeholder
- path: wallets/README.md
+ - section: Overview
+ skip-slug: true
+ contents:
+ - page: Overview
+ slug: /
+ path: wallets/pages/index.mdx
+ - page: Quickstart
+ path: wallets/pages/smart-wallets/quickstart/sdk.mdx
+ - section: Get Started
+ contents:
+ - section: React
+ contents:
+ - page: Quickstart
+ path: wallets/pages/react/getting-started/quickstart-in-section.mdx
+ - section: From scratch
+ contents:
+ - page: Initialization
+ path: wallets/pages/react/getting-started/initialization.mdx
+ - page: Environment setup
+ path: wallets/pages/react/getting-started/setup.mdx
+ - page: UI Customization
+ path: wallets/pages/react/getting-started/ui-customization.mdx
+ - page: App Integration
+ path: wallets/pages/react/getting-started/existing-project.mdx
+ - section: React Native
+ contents:
+ - page: Overview
+ path: wallets/pages/react-native/overview.mdx
+ - page: Quickstart with Expo
+ path: wallets/pages/react-native/getting-started/getting-started-quickstart.mdx
+ - section: From scratch
+ contents:
+ - page: Using Expo
+ path: wallets/pages/react-native/getting-started/getting-started-expo.mdx
+ - page: Using Bare React Native
+ path: wallets/pages/react-native/getting-started/getting-started-rn-bare.mdx
+ - page: App integration
+ path: wallets/pages/react-native/getting-started/app-integration.mdx
+ - section: Other JavaScript frameworks
+ contents:
+ - page: Overview
+ path: wallets/pages/core/overview.mdx
+ - page: Quickstart
+ path: wallets/pages/core/quickstart.mdx
+ - section: Wallet APIs
+ contents:
+ - page: Quickstart
+ path: wallets/pages/smart-wallets/quickstart/index.mdx
+ - page: Using SDK
+ path: wallets/pages/smart-wallets/quickstart/sdk.mdx
+ - page: Using API
+ path: wallets/pages/smart-wallets/quickstart/api.mdx
+ - section: Recipes
+ contents:
+ - page: Overview
+ slug: recipes
+ path: wallets/pages/recipes/overview.mdx
+ - page: Send USDC
+ slug: recipes/send-usdc
+ path: wallets/pages/recipes/send-usdc.mdx
+ - page: Programmatic Wallet Creation
+ path: wallets/pages/recipes/programmatic-wallet-creation.mdx
+ - page: Onramp Funds
+ slug: recipes/onramp-funds
+ path: wallets/pages/recipes/onramp-funds.mdx
+ - page: Session Keys with Smart Wallets
+ slug: recipes/wallet-session-keys-app
+ path: wallets/pages/recipes/wallet-session-keys-app.mdx
+ - page: Hyperliquid Transactions
+ slug: recipes/hyperliquid-wallets
+ path: wallets/pages/recipes/hyperliquid-wallets.mdx
+ - page: Smart Wallets with Aave
+ slug: wallets/recipes/smart-wallets-aave
+ path: wallets/pages/recipes/smart-wallets-aave.mdx
+ - page: Upgrade to Smart Accounts
+ slug: recipes/upgrade-to-smart-accounts
+ path: wallets/pages/recipes/upgrade-to-smart-accounts.mdx
+ - page: Multi-chain setup
+ slug: recipes/multi-chain-setup
+ path: wallets/pages/recipes/multi-chain-setup.mdx
+ - page: Social Payments and Defi
+ slug: recipes/social-payments-and-defi
+ path: wallets/pages/recipes/social-payments-and-defi.mdx
+ - link: Multi-chain Balances
+ href: https://www.alchemy.com/docs/how-to-get-crosschain-token-balances
+ - page: Supported Chains
+ path: wallets/pages/overview/supported-chains.mdx
- # Account Kit docs are auto-generated here
+ - section: Transactions
+ contents:
+ - page: Overview
+ path: wallets/pages/transactions/overview.mdx
+ - section: Send transactions
+ collapsed: true
+ contents:
+ - page: Single transactions
+ path: wallets/pages/transactions/send-transactions/index.mdx
+ - page: Batch transactions
+ path: wallets/pages/transactions/send-batch-transactions/index.mdx
+ - page: Parallel transactions
+ path: wallets/pages/transactions/send-parallel-transactions/index.mdx
+ - page: EIP-7702 transactions
+ path: wallets/pages/transactions/using-eip-7702/index.mdx
+ - section: Sponsor gas
+ path: wallets/pages/transactions/sponsor-gas/overview.mdx
+ collapsed: true
+ contents:
+ - page: Full sponsorship
+ path: wallets/pages/transactions/sponsor-gas/index.mdx
+ - page: Solana sponsorship
+ path: wallets/pages/transactions/solana/sponsor-gas-solana.mdx
+ - page: Pay gas with any token
+ path: wallets/pages/transactions/pay-gas-with-any-token/index.mdx
+ - section: Swap tokens
+ collapsed: true
+ contents:
+ - page: Same-chain swaps
+ path: wallets/pages/transactions/swap-tokens/index.mdx
+ - page: Cross-chain swaps
+ path: wallets/pages/transactions/cross-chain-swap-tokens/index.mdx
+ - section: Grant session keys
+ collapsed: true
+ contents:
+ - page: Overview
+ path: wallets/pages/smart-wallets/session-keys/index.mdx
+ - page: Using SDK
+ path: wallets/pages/smart-wallets/session-keys/sdk.mdx
+ - page: Using API
+ path: wallets/pages/smart-wallets/session-keys/api.mdx
+ - page: Retry transactions
+ path: wallets/pages/transactions/retry-transactions/index.mdx
+ - section: Sign
+ collapsed: true
+ contents:
+ - page: Sign messages
+ path: wallets/pages/transactions/signing/sign-messages/index.mdx
+ - page: Sign typed data
+ path: wallets/pages/transactions/signing/sign-typed-data/index.mdx
+ - page: Configure client
+ path: wallets/pages/concepts/smart-account-client.mdx
+
+ - section: Wallet integration
+ contents:
+ - page: Overview
+ path: wallets/pages/authentication/overview.mdx
+ - section: Embedded wallets
+ collapsed: true
+ contents:
+ - section: Login methods
+ collapsed: true
+ contents:
+ - page: Email OTP
+ path: wallets/pages/authentication/login-methods/email-otp.mdx
+ - page: Email magic-link
+ path: wallets/pages/authentication/login-methods/email-magic-link.mdx
+ - page: Social login
+ path: wallets/pages/authentication/login-methods/social-login.mdx
+ - page: Custom social providers
+ path: wallets/pages/react/login-methods/social-providers.mdx
+ - page: Bring your own authentication
+ path: wallets/pages/authentication/login-methods/bring-your-own-auth.mdx
+ - page: Passkey signup
+ path: wallets/pages/authentication/login-methods/passkey-signup.mdx
+ - page: Passkey login
+ path: wallets/pages/react/login-methods/passkey-login.mdx
+ - page: Add passkey
+ path: wallets/pages/react/add-passkey.mdx
+ - page: SMS login
+ path: wallets/pages/authentication/login-methods/sms-login.mdx
+ - page: "On-chain passkeys"
+ path: wallets/pages/react/login-methods/onchain-passkeys.mdx
+ - page: Adding and removing login methods
+ path: wallets/pages/react/login-methods/adding-and-removing-login-methods.mdx
+ - section: UI components
+ collapsed: true
+ contents:
+ - page: Using UI components
+ path: wallets/pages/react/ui-components.mdx
+ - page: Theme & branding
+ path: wallets/pages/react/customization/theme.mdx
+ - page: Tailwind setup
+ path: wallets/pages/react/customization/tailwind-setup.mdx
+ - section: Whitelabel
+ collapsed: true
+ contents:
+ - page: React hooks
+ path: wallets/pages/react/react-hooks.mdx
+ - page: Other JS initialization
+ path: wallets/pages/signer/quickstart.mdx
+ - section: Connectors
+ collapsed: true
+ contents:
+ - page: Connect external wallets
+ path: wallets/pages/react/login-methods/eoa-login.mdx
+ - page: Customize
+ path: wallets/pages/react/connectors/customization.mdx
+ - section: MFA
+ collapsed: true
+ contents:
+ - page: Set up MFA
+ path: wallets/pages/react/mfa/setup-mfa.mdx
+ - page: Email OTP
+ path: wallets/pages/react/mfa/email-otp.mdx
+ - page: Email magic-link
+ path: wallets/pages/react/mfa/email-magic-link.mdx
+ - page: Social login
+ path: wallets/pages/react/mfa/social-login.mdx
+ - page: Solana wallets
+ path: wallets/pages/react/solana-wallets/get-started.mdx
+ - page: Server wallets
+ path: wallets/pages/signer/authentication/server-wallets.mdx
+ - section: Account management
+ contents:
+ - page: Pregenerate wallets
+ path: wallets/pages/react/pregenerate-wallets.mdx
+ - page: User sessions
+ path: wallets/pages/signer/user-sessions.mdx
+ - page: Manage account ownership
+ path: wallets/pages/smart-contracts/modular-account-v2/managing-ownership.mdx
+ - page: Export private key
+ path: wallets/pages/signer/export-private-key.mdx
+ - section: Third-party signers
+ contents:
+ - page: Privy
+ path: wallets/pages/third-party/signers/privy.mdx
+ - page: Turnkey
+ path: wallets/pages/third-party/signers/turnkey.mdx
+ - page: Custom integration
+ path: wallets/pages/third-party/signers/custom-integration.mdx
+ - page: What is a signer?
+ path: wallets/pages/authentication/what-is-a-signer.mdx
+
+ - section: Low-Level Infra
+ contents:
+ - page: Overview
+ path: wallets/pages/low-level-infra/overview.mdx
+ - page: Quickstart
+ path: wallets/pages/low-level-infra/quickstart.mdx
+ - section: Gas Manager API
+ contents:
+ - section: Policy Management
+ contents:
+ - page: API Endpoints
+ path: wallets/pages/low-level-infra/gas-manager/policy-management/api-endpoints.mdx
+ - section: Gas Sponsorship
+ contents:
+ - page: API Endpoints
+ path: wallets/pages/low-level-infra/gas-manager/gas-sponsorship/api-endpoints.mdx
+ - section: Using SDK
+ contents:
+ - page: Basic Gas Sponsorship
+ path: wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/basic-gas-sponsorship.mdx
+ - page: Conditional Gas Sponsorship
+ path: wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/conditional-gas-sponsorship.mdx
+ - page: Pay Gas with Any ERC20 Token
+ path: wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/pay-gas-with-any-erc20-token.mdx
+ - section: Bundler API
+ path: wallets/pages/low-level-infra/bundler/overview.mdx
+ contents:
+ - page: API Endpoints
+ path: wallets/pages/low-level-infra/bundler/api-endpoints.mdx
+ - page: Using SDK
+ path: wallets/pages/low-level-infra/bundler/sdk.mdx
+ - page: Bundler FAQs
+ path: wallets/pages/bundler-api/bundler-faqs.mdx
+ - section: Smart Account Types
+ contents:
+ - page: Choosing a Smart Account
+ path: wallets/pages/authorization/smart-account-types/choosing-a-smart-account.mdx
+ - section: Modular Account V2
+ contents:
+ - page: Overview
+ path: wallets/pages/authorization/smart-account-types/modular-account-v2/overview.mdx
+ - page: Getting started
+ path: wallets/pages/smart-contracts/modular-account-v2/getting-started.mdx
+ - page: Upgrading to MAv2
+ path: wallets/pages/smart-contracts/modular-account-v2/upgrading-to-MAv2.mdx
+ - section: Light Account
+ contents:
+ - page: Overview
+ path: wallets/pages/smart-contracts/other-accounts/light-account/index.mdx
+ - page: Getting started
+ path: wallets/pages/smart-contracts/other-accounts/light-account/getting-started.mdx
+ - page: Transfer ownership
+ path: wallets/pages/smart-contracts/other-accounts/light-account/transfer-ownership-light-account.mdx
+ - page: Manage multiple owners
+ path: wallets/pages/smart-contracts/other-accounts/light-account/multi-owner-light-account.mdx
+ - page: Third party
+ path: wallets/pages/third-party/smart-contracts.mdx
+ - page: Deployed addresses
+ path: wallets/pages/smart-contracts/deployed-addresses.mdx
+
+ - section: API Reference
+ contents:
+ - section: Smart Wallets
+ contents:
+ - section: Wallet API Endpoints
+ contents:
+ - api: Wallet API Endpoints
+ api-name: wallet-api
+ flattened: true
+ - section: Signer API Endpoints
+ contents:
+ - api: Signer API Endpoints
+ api-name: accounts
+ flattened: true
+ - page: How to stamp requests
+ path: wallets/pages/smart-wallets/how-to-stamp-requests.mdx
+ slug: smart-wallets
+ - section: Gas Manager
+ contents:
+ - api: Gas Abstraction API Endpoints
+ api-name: gas-manager-coverage
+ - api: Admin API Endpoints
+ api-name: admin
+ slug: gas-manager-admin-api
+ - section: Bundler
+ contents:
+ - api: Bundler API Endpoints
+ api-name: bundler
+ slug: bundler-api-endpoints
+ - api: UserOp Simulation Endpoints
+ api-name: userop-sim
+ slug: useroperation-simulation-endpoints
+ - section: EntryPoint Revert Codes
+ contents:
+ - page: EntryPoint v0.7 Revert Codes
+ path: >-
+ wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v07-revert-codes.mdx
+ - page: EntryPoint v0.6 Revert Codes
+ path: >-
+ wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v06-revert-codes.mdx
+ slug: entrypoint-revert-codes
+ slug: bundler-api
+ - section: Resources
+ skip-slug: true
+ contents:
+ - page: Features
+ slug: wallets/resources/features
+ path: wallets/pages/features.mdx
+ - page: Understanding Smart Wallets
+ path: wallets/pages/concepts/intro-to-account-kit.mdx
+ - link: GitHub
+ href: https://github.com/alchemyplatform/aa-sdk
+ - link: Changelog
+ href: https://github.com/alchemyplatform/aa-sdk/blob/main/CHANGELOG.md
+ - link: Contributing
+ href: https://github.com/alchemyplatform/aa-sdk/blob/main/CONTRIBUTING.md
+ - link: Gas benchmarks
+ href: https://github.com/alchemyplatform/aa-benchmarks
+ - page: Migrating to 4.x.x
+ slug: migration-guide
+ path: wallets/pages/migration-guide.mdx
+ - page: Terms
+ slug: resources/terms
+ path: wallets/pages/resources/terms.mdx
+ - page: Types
+ slug: resources/types
+ path: wallets/pages/resources/types.mdx
+ - page: Middleware
+ path: wallets/pages/concepts/middleware.mdx
+
+ - section: Troubleshooting
+ contents:
+ - page: General FAQs
+ path: wallets/pages/resources/faqs.mdx
+ - page: Wallet APIs Errors
+ path: wallets/pages/troubleshooting/wallet-apis-errors.mdx
+ - page: Gas Manager FAQs
+ path: wallets/pages/gas-manager-admin-api/gas-manager-faqs.mdx
+ - page: Gas Manager Errors
+ path: wallets/pages/gas-manager-admin-api/gas-manager-errors.mdx
+ - page: Bundler FAQs
+ path: wallets/pages/bundler-api/bundler-faqs.mdx
+ - page: Bundler RPC Errors
+ path: wallets/pages/bundler-api/bundler-rpc-errors.mdx
+ - page: Server-side rendering
+ slug: troubleshooting/ssr
+ path: wallets/pages/troubleshooting/server-side-rendering.mdx
+ - page: Contact us
+ slug: resources/contact-us
+ path: wallets/pages/resources/contact-us.mdx
- tab: rollups
layout:
- section: Introduction
diff --git a/fern/wallets/CONTRIBUTING.md b/fern/wallets/CONTRIBUTING.md
new file mode 100644
index 000000000..f8fa6a073
--- /dev/null
+++ b/fern/wallets/CONTRIBUTING.md
@@ -0,0 +1,367 @@
+# Wallet Documentation Contributing Guidelines
+
+
+
+## 🤖 AI Assistant Guidelines
+
+**ROLE**: You are helping write documentation for Alchemy's Smart Wallets product. Always follow these rules when generating or editing content.
+
+**CORE PRINCIPLES**:
+
+* SIMPLIFY: Hide blockchain complexity, focus on developer outcomes
+* STANDARDIZE: Use consistent terminology and voice
+* ACTIONABLE: Provide clear, direct instructions
+
+**TERMINOLOGY ENFORCEMENT**:
+
+* ALWAYS use approved terms from the "Approved Terms" section
+* NEVER use terms from the "Prohibited Terms" section
+* REPLACE prohibited terms with their approved alternatives
+
+**VOICE REQUIREMENTS**:
+
+* Use second person ("you") not first person ("we", "I")
+* Use active voice, not passive voice
+* Be direct and confident, avoid qualifiers like "perhaps", "might"
+* Follow Google Developer Documentation Style Guide standards
+
+
+
+## Overview
+
+When writing or editing documentation for Alchemy Smart Wallets, follow these comprehensive style guidelines. These rules ensure consistency, clarity, and developer-focused content that abstracts away blockchain complexity.
+
+**Foundation**: Follow the [Google Developer Documentation Style Guide](https://developers.google.com/style) as the base standard.
+
+***
+
+## 1. Core Principles
+
+### Simplify and Abstract
+
+* **Goal**: Hide Account Abstraction and blockchain complexity
+* **Focus**: Developer outcomes, not implementation details
+* **Example**:
+ * ❌ **Don't**: "Send a UserOperation to the bundler and use a paymaster"
+ * ✅ **Do**: "Send gasless transactions"
+
+### Standardize
+
+* **Goal**: Consistent terminology, voice, and document structure across all docs
+* **Application**: Use identical terms for identical concepts
+
+### Be Actionable
+
+* **Goal**: Clear instructions that help developers achieve goals quickly
+* **Implementation**: Direct commands, specific steps, working examples
+
+***
+
+## 2. Terminology Standards
+
+### ✅ Approved Terms (ALWAYS USE)
+
+| Term | Usage | Context |
+| -------------------------- | --------------------- | ---------------------------------------- |
+| `"Smart Wallets"` | Primary product term | Capitalize when referring to the product |
+| `"smart account"` | Technical term | Lowercase in general text |
+| `"aa-sdk"` | Code references only | Never in prose, only in code blocks |
+| `"gasless"` | Payment model | Not "gas-less" |
+| `"onchain"` | Blockchain reference | Not "on-chain" |
+| `"transactions"` | User actions | Not "user operations" |
+| `"sponsor gas"` | Gas payment feature | Not "gas manager" |
+| `"pay gas with any token"` | ERC20 payment feature | Not "ERC20 paymaster" |
+
+### ❌ Prohibited Terms (NEVER USE)
+
+**Replacement Rules**:
+
+| Prohibited Term | ➡️ Use Instead | Exception |
+| -------------------------------------------- | --------------------------------------------- | --------------------------------------- |
+| `"Account Abstraction"` or `"AA"` | Avoid entirely | Only in advanced technical docs |
+| `"ERC-4337"` | Avoid entirely | Only when discussing protocol specifics |
+| `"user operation"` or `"user ops"` | `"transactions"` | Never |
+| `"bundler"` | `"sending transactions"` | Never |
+| `"entrypoint"` | Avoid entirely | Implementation detail |
+| `"smart contract account"` | `"wallet"` | Never |
+| `"Account Kit"` | `"Smart Wallets"` | Never |
+| `"gas manager"` | `"sponsor gas"` or `"pay gas with any token"` | Except "Gas Manager API" |
+| `"paymaster"` | Context-specific replacement | Except "paymaster contract" |
+| `"Signer"` | `"authentication"` or `"owner"` | Never |
+| `"modular account v2"`, `"light account v1"` | `"smart account"` | Never |
+
+### 🏷️ Brand Reference Rules
+
+**Company References**:
+
+* ❌ **Don't use**: "Alchemy" or "our" in documentation
+* ✅ **Examples**:
+ * ❌ "Alchemy Smart Wallets" → ✅ "Smart Wallets"
+ * ❌ "our smart account" → ✅ "smart accounts"
+
+***
+
+## 3. Voice and Tone Standards
+
+### 📝 Voice Requirements
+
+**Second Person Voice** (REQUIRED):
+
+* ✅ **Use**: "you" throughout documentation
+* ❌ **Avoid**: "we", "I", "one"
+* ❌ **Never**: "We recommend..." → ✅ **Use**: "Recommended approach:"
+
+**Active Voice** (REQUIRED):
+
+* ✅ **Use**: "Create a wallet"
+* ❌ **Avoid**: "A wallet should be created"
+
+**Direct Commands** (REQUIRED):
+
+* ✅ **Use**: "Install the SDK"
+* ❌ **Avoid**: "You need to install..." or "You should install..."
+
+### 🎯 Tone Requirements
+
+**Be Confident and Opinionated**:
+
+* ❌ **Avoid qualifiers**: "perhaps", "might want to", "you may wish to"
+* ✅ **Be direct**: State the recommended approach clearly
+
+**Consistency**:
+
+* Use identical terminology for identical concepts across all documents
+* Maintain consistent voice throughout each document
+
+### 📐 Capitalization Rules
+
+| Type | Rule | Examples |
+| ---------------------------- | -------------------------- | ------------------------------------- |
+| **Product terms** | Capitalize | "Smart Wallets" |
+| **Titles and sidebar names** | Capitalize first word only | "Getting started with authentication" |
+| **API names** | Capitalize proper nouns | "Gas Manager API", "Bundler API" |
+| **Type definitions** | Capitalize | `Provider`, `Signer`, `Account` |
+
+***
+
+## 4. Content Structure Rules
+
+### 📋 Headers and Titles
+
+**Requirements**:
+
+* ❌ **No AA-specific terms** in titles or headers
+* ✅ **Use developer-friendly, outcome-focused titles**
+* ✅ **Keep concise** for sidebar navigation (avoid wrapping)
+
+**Examples**:
+
+* ❌ "Gas Manager Quickstart" → ✅ "Sponsor gas"
+* ❌ "UserOp Configuration" → ✅ "Configure transactions"
+
+### 🔗 Content Organization
+
+**Link Strategy**:
+
+* **Link to existing docs** instead of repeating content
+* **Use relative links**: All relative links should begin with `/wallets/...` instead of full URLs (`https://www.alchemy.com/docs/wallets/...`)
+* **Ensure no broken or circular references**
+
+***
+
+## 5. Code and Technical Standards
+
+### 📋 Prerequisites and Setup
+
+**Always Include**:
+
+* Prerequisites and assumptions
+* Version requirements
+* Configuration steps
+
+**Examples**:
+
+```markdown
+Before implementing social login, configure your Smart Wallets dashboard...
+Ensure you are using aa-sdk version 3.x or later...
+```
+
+### 💻 Code Block Requirements
+
+**Formatting Rules**:
+
+* ✅ **Use backticks** for all code references, function names, technical terms
+* ✅ **Include language specification** in code blocks
+* ✅ **Apply `twoslash`** to all examples for type checking
+
+**Example Structure**:
+
+````markdown
+```ts twoslash
+// Your example code here
+```
+````
+
+### 🏗️ Example Standards
+
+**Every Example Must Be**:
+
+1. **Standalone** - can be copied and run independently
+2. **Compilable** - passes type checking
+3. **Working** - produces expected results
+
+**Example Organization**:
+
+* Split long examples into multiple files
+* Use `example.ts` tab for main code
+* Use `config.ts` tab for setup
+* Follow this pattern:
+ 1. Install aa-sdk
+ 2. Get required configs (API keys, Policy IDs, private key)
+ 3. Copy the files
+ 4. Run `example.ts`
+
+**Highlighting**:
+
+* Use [Fern highlighting](https://buildwithfern.com/learn/docs/writing-content/components/code-blocks) to focus on critical parts
+* Use snippets for common configuration to avoid duplication
+
+***
+
+## 6. Markdown Formatting Standards
+
+### 📝 Structure Requirements
+
+**Follow**:
+
+* [Google Markdown Style Guide](https://google.github.io/styleguide/docguide/style.html)
+* Remark-lint rules for consistency
+* Proper heading hierarchy (H1 → H2 → H3)
+
+**Framework Support**:
+
+* If a guide supports multiple frameworks, use tabs within one document
+* Example tabs: React, React Native, Other JavaScript
+
+**Links and References**:
+
+* ✅ **Use relative links**: `[/wallets/authentication]`
+* ❌ **Avoid full URLs**: `[https://www.alchemy.com/docs/...]`
+* ✅ **Include alt text** for all images
+* ✅ **Verify no broken links**
+
+### 🖼️ Images and Assets
+
+**All documentation assets are hosted on Cloudinary**
+
+**Adding New Images**:
+
+1. **Upload to Cloudinary**:
+
+ * Sign-in to Cloudinary through Okta.
+ * Folder structure: `docs/aa-sdk/images/[subdirectory]/`
+ * Use the Cloudinary dashboard or API (can get credentials for API through dashboard)
+ * Set `overwrite: true` to replace existing assets if updating existing ones.
+
+2. **Reference in Documentation**:
+
+ ```markdown
+ 
+ ```
+
+ Or for HTML:
+
+ ```html
+
+ ```
+
+3. **Best Practices**:
+ * ✅ Use descriptive filenames (e.g., `auth0-config.png` not `image1.png`)
+ * ✅ Optimize images before upload (compress PNGs, use appropriate quality for JPEGs)
+ * ✅ Use kebab-case for filenames
+ * ✅ Always include alt text for accessibility
+ * ❌ Don't commit local image files to the repository
+
+**Folder Structure on Cloudinary**:
+
+```
+docs/aa-sdk/
+├── images/ # Main documentation images
+│ ├── getting-started/ # Quickstart and setup images
+│ └── ...
+└── shared/ # Shared assets referenced across multiple docs
+```
+
+***
+
+## 7. Quality Assurance Checklist
+
+### ✅ Pre-Publish Validation
+
+**Terminology Validation**:
+
+* \[ ] No prohibited terms used anywhere in the document
+* \[ ] All approved terms used correctly and consistently
+* \[ ] Proper capitalization applied throughout
+* \[ ] No AA-specific terms in headers or titles
+
+**Voice and Style Validation**:
+
+* \[ ] Follows Google Developer Documentation Style Guide
+* \[ ] Second-person voice used throughout ("you" not "we")
+* \[ ] Active voice used consistently
+* \[ ] Direct, confident tone without unnecessary qualifiers
+* \[ ] Outcome-focused titles and headers
+
+**Code and Technical Validation**:
+
+* \[ ] Twoslash applied to all code snippets
+* \[ ] Language specified for all code blocks
+* \[ ] All code references properly formatted with backticks
+* \[ ] Examples are standalone, compilable, and working
+* \[ ] Prerequisites clearly stated
+
+**Format and Structure Validation**:
+
+* \[ ] Proper markdown hierarchy maintained
+* \[ ] All links are relative and functional
+* \[ ] No broken or circular references
+* \[ ] Content links to existing docs instead of repeating information
+* \[ ] Consistent spacing and formatting throughout
+
+### 🔧 AI Assistant Validation
+
+**For AI Tools** (GitHub Copilot, Cursor, etc.):
+
+* \[ ] Document follows structured format for easy parsing
+* \[ ] Clear do/don't examples provided
+* \[ ] Terminology rules explicitly stated with replacements
+* \[ ] Voice requirements clearly defined
+* \[ ] Code standards include specific formatting requirements
+
+***
+
+## 8. Implementation Guide
+
+### For Human Contributors
+
+1. **Before Writing**: Review this entire guide
+2. **While Writing**: Reference the approved/prohibited terms tables
+3. **Before Submitting**: Complete the quality checklist
+4. **When Editing**: Ensure consistency with existing docs
+
+### For AI Assistants
+
+**⚠️ CRITICAL**: Do not modify existing code snippets beyond formatting (indentation, language tags). Code changes can break functionality.
+
+1. **Priority Order**: Terminology > Voice > Structure > Style
+2. **Conflict Resolution**: When in doubt, choose the simpler, more developer-friendly option
+3. **Validation**: Cross-reference every term against the approved/prohibited lists
+4. **Consistency**: Maintain identical terminology across all generated content
+
+***
+
+*This document is the definitive style guide for Smart Wallets documentation. All content must conform to these standards.*
diff --git a/fern/wallets/README.md b/fern/wallets/README.md
index ba2f6e65e..674e49c4f 100644
--- a/fern/wallets/README.md
+++ b/fern/wallets/README.md
@@ -1,7 +1,116 @@
----
-title: Wallets Placeholder
-description: Learn about wallets and their integration with Alchemy.
-slug: docs/wallets
----
+# Smart Wallets Documentation
-Account Kit documentation is maintained in the [aa-sdk repository](https://github.com/alchemyplatform/aa-sdk). See its [README](https://github.com/alchemyplatform/aa-sdk/blob/main/docs/README.md) for contribution guidelines.
+This repository contains the documentation for Smart Wallets that gets published to [alchemy.com/docs](https://alchemy.com/docs) which is built using [Fern](https://buildwithfern.com/learn/docs/getting-started/overview).
+
+The contents are automatically merged with [Alchemy's Official Docs repo](https://github.com/alchemyplatform/docs) to create seamless updates without needing to make changes separately in that repo.
+
+## Structure
+
+```text
+aa-sdk/
+├── docs/ # This project
+│ ├── docs.yml # The main configuration file that defines the documentation structure and sidebar navigation
+│ ├── pages/ # Contains the documentation markdown files
+│ ├── components/ # Contains all the custom React components used in markdown
+| ├── specs/ # Contains OpenRPC and OpenAPI spec definitions which are dereferenced during build
+| └── api-generators/ # Contains generators.yaml files used by Fern to reference API specs
+└── docs-site/ # Git Submodule containing Fern and all non-wallet docs content
+```
+
+> \[!WARNING]
+> If you want to make changes to content outside the Wallets tab, please do so in the main [Alchemy Docs repo](https://github.com/alchemyplatform/docs). See its [README](https://github.com/alchemyplatform/docs?tab=readme-ov-file#alchemy-documentation) and [contributing guidelines](https://github.com/alchemyplatform/docs/blob/main/CONTRIBUTING.md) for more information.
+
+## Making Updates
+
+### Docs Content
+
+To add or modify documentation content:
+
+* Add/edit markdown files in the `pages/` directory
+* Follow the existing markdown formatting conventions. Fern uses [Github-flavored Markdown](https://github.github.com/gfm/)
+* You may use any [existing Fern components](https://buildwithfern.com/learn/docs/content/components/overview)
+ * Do **not** use `import`/`require` statements in MDX. Fern injects these for you.
+
+To add new pages to navigation:
+
+* Update the `navigation` section in `docs.yml`
+* Reference markdown files from `pages/` by path using `wallets/pages`
+
+### Images
+
+**All documentation images are hosted on Cloudinary** for optimal performance and CDN delivery.
+
+To add new images:
+
+1. **Upload to Cloudinary**:
+
+ * Sign-in to Cloudinary through Okta.
+ * Folder structure: `docs/aa-sdk/images/[subdirectory]/`
+ * Use descriptive, kebab-case filenames (e.g., `auth0-config.png`)
+ * Set `overwrite: true` if replacing an existing asset
+
+2. **Reference in Markdown**:
+
+ ```markdown
+ 
+ ```
+
+ Or using HTML:
+
+ ```html
+
+ ```
+
+3. **Best Practices**:
+ * ✅ Always include descriptive alt text for accessibility
+ * ✅ Optimize images before upload (compress PNGs, use appropriate JPEG quality)
+ * ✅ Use consistent naming conventions
+ * ❌ Don't commit local image files to the repository
+
+For detailed guidelines, see the [Images and Assets section in CONTRIBUTING.md](./CONTRIBUTING.md#-images-and-assets)
+
+### SDK References
+
+SDK References are automatically generated from relevant projects within the monorepo. Both the markdown files within `docs/pages/reference` and the docs navigation structure in `docs/docs.yml` are generated and should **not** be edited manually. In the root, to generate references from code you can run:
+
+```shell
+yarn docs:sdk
+```
+
+## Local Development
+
+To run docs locally, you must have `pnpm` installed as a global dependency. `corepack` can install it for you. Run:
+
+```bash
+asdf install # or `mise install`
+corepack enable
+```
+
+Then to start the dev server, run:
+
+```bash
+yarn docs:dev
+```
+
+This will install dependencies, temporarily move files into `docs-site`, and keep the submodule updated for you.
+
+## Preview Changes
+
+1. Create a pull request with your changes
+2. The CI will automatically generate a preview URL in the PR comments
+3. Review the preview to ensure your changes appear as expected
+
+## Publishing
+
+Documentation changes are automatically published to [alchemy.com/docs](https://alchemy.com/docs) when merged to the `main` branch.
+
+## Technical Details
+
+* The `scripts/insert-docs.sh` script is run during local and during CI/CD from both [aa-sdk](https://github.com/alchemyplatform/aa-sdk/) and [docs](https://github.com/alchemyplatform/docs) repos. It handles:
+ * Inserting Smart Wallets documentation configuration into the main docs site config
+ * Moving API specs to the correct locations in the main docs repo
+* Documentation is built and published using [Fern CLI](https://buildwithfern.com/learn/cli-reference/overview#setting-up-docs)
+* All documentation images are hosted on Cloudinary CDN at `alchemyapi-res.cloudinary.com`
diff --git a/fern/wallets/components/icons/ClockForwardIcon.tsx b/fern/wallets/components/icons/ClockForwardIcon.tsx
new file mode 100644
index 000000000..0af0f2dad
--- /dev/null
+++ b/fern/wallets/components/icons/ClockForwardIcon.tsx
@@ -0,0 +1,19 @@
+import type { SVGProps } from "react";
+
+export const ClockForwardIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/DotsIcon.tsx b/fern/wallets/components/icons/DotsIcon.tsx
new file mode 100644
index 000000000..9a1ed345d
--- /dev/null
+++ b/fern/wallets/components/icons/DotsIcon.tsx
@@ -0,0 +1,16 @@
+import type { SVGProps } from "react";
+
+export const DotsIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/ExpoIcon.tsx b/fern/wallets/components/icons/ExpoIcon.tsx
new file mode 100644
index 000000000..35f8b1ae0
--- /dev/null
+++ b/fern/wallets/components/icons/ExpoIcon.tsx
@@ -0,0 +1,10 @@
+import type { SVGProps } from "react";
+
+export const ExpoIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/InfraIcon.tsx b/fern/wallets/components/icons/InfraIcon.tsx
new file mode 100644
index 000000000..52b847915
--- /dev/null
+++ b/fern/wallets/components/icons/InfraIcon.tsx
@@ -0,0 +1,16 @@
+import type { SVGProps } from "react";
+
+export const InfraIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/PointerIcon.tsx b/fern/wallets/components/icons/PointerIcon.tsx
new file mode 100644
index 000000000..cc8b31c7a
--- /dev/null
+++ b/fern/wallets/components/icons/PointerIcon.tsx
@@ -0,0 +1,16 @@
+import type { SVGProps } from "react";
+
+export const PointerIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/ReactNativeIcon.tsx b/fern/wallets/components/icons/ReactNativeIcon.tsx
new file mode 100644
index 000000000..5dfb37807
--- /dev/null
+++ b/fern/wallets/components/icons/ReactNativeIcon.tsx
@@ -0,0 +1,22 @@
+import type { SVGProps } from "react";
+
+export const ReactNativeIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/SignerIcon.tsx b/fern/wallets/components/icons/SignerIcon.tsx
new file mode 100644
index 000000000..6dabfc3e7
--- /dev/null
+++ b/fern/wallets/components/icons/SignerIcon.tsx
@@ -0,0 +1,19 @@
+import type { SVGProps } from "react";
+
+export const SignerIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/components/icons/SmartContractIcon.tsx b/fern/wallets/components/icons/SmartContractIcon.tsx
new file mode 100644
index 000000000..abc84e9b7
--- /dev/null
+++ b/fern/wallets/components/icons/SmartContractIcon.tsx
@@ -0,0 +1,16 @@
+import type { SVGProps } from "react";
+
+export const SmartContractIcon = (props: SVGProps) => (
+
+);
diff --git a/fern/wallets/pages/authentication/login-methods/bring-your-own-auth.mdx b/fern/wallets/pages/authentication/login-methods/bring-your-own-auth.mdx
new file mode 100644
index 000000000..6af5ade7f
--- /dev/null
+++ b/fern/wallets/pages/authentication/login-methods/bring-your-own-auth.mdx
@@ -0,0 +1,287 @@
+---
+title: Bring Your Own Authentication
+description: Integrate your existing authentication system with Alchemy Smart Wallets using JWT tokens
+slug: wallets/authentication/login-methods/bring-your-own-auth
+---
+
+Integrate your existing authentication provider and add smart wallet functionality to your app without changing your users' login experience. We support JWT-based and OIDC-compliant authentication providers.
+
+## Prerequisites
+
+Before implementing JWT authentication, verify that your authentication system meets these requirements:
+
+### 1. OIDC Compliance
+
+Your authentication system must be [**OpenID Connect (OIDC) compliant**](https://auth0.com/docs/authenticate/protocols/openid-connect-protocol#openid-and-jwts) and capable of issuing **JWT tokens**.
+
+### 2. Required Endpoints
+
+#### OpenID Connect Discovery Endpoint
+
+Host a discovery endpoint at: `/.well-known/openid-configuration`
+
+The response must include these required fields from the [**OpenID spec**](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata):
+
+```json
+{
+ "issuer": "",
+ "jwks_uri": "",
+ "id_token_signing_alg_values_supported": ["RS256"],
+ "authorization_endpoint": "",
+ "response_types_supported": [""],
+ "subject_types_supported": [""]
+}
+```
+
+**Field explanations:**
+
+* `issuer`: Your API domain (must match the `iss` claim in your JWTs)
+* `jwks_uri`: Endpoint URL where Alchemy can retrieve the JWT public key for verification
+* `id_token_signing_alg_values_supported`: Must include "RS256" algorithm
+
+### 3. JWT Structure Requirements
+
+#### JWT Header Requirements
+
+Your JWT header must include:
+
+| Field | Description | Example |
+| ----- | ------------------------------------------------------ | --------- |
+| `alg` | Algorithm used to sign the JWT | `"RS256"` |
+| `typ` | Token type | `"JWT"` |
+| `kid` | Key ID that matches the `kid` field from your JWKs URI | `"key-1"` |
+
+**Important**: The `kid` field in the JWT header must correspond to a key identifier from your JWKS endpoint to enable proper key lookup during token verification.
+
+#### JWT Payload Requirements
+
+Your JWTs must contain these claims:
+
+| Claim | Description | Example |
+| ------- | ---------------------------------------- | ------------------------------------------------------- |
+| `iss` | Issuer - your auth provider's API domain | `"https://auth.example.com"` |
+| `sub` | Subject - unique user identifier | `"user123"` or `"550e8400-e29b-41d4-a716-446655440000"` |
+| `aud` | Audience - client ID issued by Alchemy | `"your-alchemy-audience-id"` |
+| `nonce` | Target public key hash (see below) | `"a1b2c3d4..."` |
+
+**Important**: The `nonce` claim must be `toHex(sha256(targetPublicKey))` **without** the leading `0x`.
+
+
+ You can generate the `targetPublicKey` from the [Alchemy Signer
+ SDK](/wallets/reference/account-kit/signer/classes/AlchemySignerWebClient/targetPublicKey)
+
+
+
+ If your OAuth provider reserves the `nonce` claim, you can use `tknonce` as an
+ alternative. Only one of `nonce` or `tknonce` needs to be set.
+
+
+
+ The combination of (`iss`, `sub`, `aud`) acts as a unique user identifier.
+ Ensure these values are consistent for each user.
+
+
+### 4. Audience Claim
+
+The `aud` claim must be set to your Alchemy-provided audience ID. You can obtain the audience claim from your Smart Wallet settings in the [Alchemy Dashboard](https://dashboard.alchemy.com/apps/latest/services/smart-wallets).
+
+
+
+## Implementation Guide
+
+
+
+ ## Using React Hooks
+
+ ### Step 1: Install Dependencies
+
+ ```bash
+ npm install @account-kit/signer @account-kit/react
+ ```
+
+ ### Step 2: Generate Target Public Key
+
+ Before creating your JWT, generate the required `targetPublicKey`:
+
+ ```tsx twoslash
+ import { AlchemySignerWebClient } from "@account-kit/signer";
+ import { sha256, stringToBytes } from 'viem'
+
+ const client = new AlchemySignerWebClient({
+ connection: {
+ apiKey: "your-api-key",
+ },
+ iframeConfig: {
+ iframeContainerId: "signer-iframe-container",
+ },
+ });
+
+ // Generate the target public key
+ const targetPublicKey = await client.targetPublicKey();
+
+ // Use this in your JWT's nonce claim
+ const nonce = sha256(stringToBytes(targetPublicKey)).slice(2)
+ ```
+
+ ### Step 3: Create and Submit JWT
+
+ After generating your JWT with the proper claims, authenticate the user:
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate, isPending } = useAuthenticate();
+
+ const handleJwtAuth = async (jwt: string) => {
+ try {
+ await authenticate({
+ type: "custom-jwt",
+ jwt // Your generated JWT with required claims
+ });
+ } catch (error) {
+ console.error("Authentication failed:", error);
+ }
+ };
+ ```
+
+ ### Step 4: Check Authentication Status
+
+ Monitor authentication status and handle the connected user:
+
+ ```tsx twoslash
+ import { useSignerStatus, useUser } from "@account-kit/react";
+
+ const MyComponent = () => {
+ const { isConnected } = useSignerStatus();
+ const user = useUser();
+
+ if (isConnected && user) {
+ return (
+
+
Welcome, {user.email}!
+
Wallet Address: {user.address}
+
+ );
+ }
+
+ return
Not authenticated
;
+ };
+ ```
+
+
+
+ ## Authenticating Users with a JWT token
+
+ **Endpoint:** `POST https://api.g.alchemy.com/signer/v1/auth-jwt`
+
+ **Request Body:**
+
+ ```json
+ {
+ "jwt": "eyJhbGciOiJSUzI1NiJ9...", // Your base64 encoded JWT
+ "targetPublicKey": "0x1234...",
+ }
+ ```
+
+ **Success Response:**
+
+ ```json
+ {
+ "isSignup": false,
+ "credentialBundle": "encrypted-credential-bundle",
+ "orgId": "organization-id"
+ }
+ ```
+
+ ## Pre-generating Embedded Wallet Addresses using JWT token
+
+ **Endpoint:** `POST https://api.g.alchemy.com/signer/v1/auth-jwt`
+
+ To pre-generate wallet addresses before user authentication, omit the `targetPublicKey`:
+
+ **Request Body:**
+
+ ```json
+ {
+ "jwt": "eyJhbGciOiJSUzI1NiJ9...",
+ }
+ ```
+
+ **Pre-generation Response:**
+
+ ```json
+ {
+ "isSignup": true,
+ "userId": "alchemy-user-id",
+ "address": "0xabcdef...", // EOA signer address
+ "solanaAddress": "5Gv7...", // Solana address (if applicable)
+ "orgId": "organization-id"
+ }
+ ```
+
+
+ The `address` returned is the EOA signer address. To get the actual smart contract wallet address, call the `getAddress()` method on your smart account implementation using this signer address.
+
+
+
+ For setting up an account config, see the [Signer Quickstart](/wallets/signer/quickstart).
+
+
+
+
+## Architecture Overview
+
+The JWT authentication flow works as follows:
+
+1. **User Authentication**: User logs into your existing auth system
+2. **JWT Generation**: Your backend generates a JWT with required claims
+3. **Submit to Alchemy**: Frontend submits JWT to Alchemy's auth endpoint
+4. **Verification**: Alchemy verifies JWT using your JWKS endpoint
+5. **Wallet Access**: User gains access to their smart wallet
+
+```mermaid
+sequenceDiagram
+ participant User
+ participant YourApp as Your Application
+ participant YourAuth as Your Auth System
+ participant Alchemy as Alchemy API
+
+ User->>YourApp: Login request
+ YourApp->>YourAuth: Authenticate user
+ YourAuth->>YourApp: Return JWT
+ YourApp->>Alchemy: Submit JWT (/v1/auth-jwt)
+ Alchemy->>YourApp: Verify JWT and return credentials/wallet info
+ YourApp->>User: Wallet ready
+```
+
+## Testing and Validation
+
+### JWT Validation Tool
+
+Use [jwt.io](https://jwt.io/) to decode and validate your JWT structure before integration.
+
+### Required Claims Checklist
+
+* `iss` matches your OpenID config issuer
+* `sub` contains unique user identifier
+* `aud` contains your Alchemy-provided audience ID
+* `nonce` or `tknonce` contains `toHex(sha256(targetPublicKey))` without `0x`
+* JWT is signed with RS256 algorithm
+
+### Integration Testing
+
+1. Verify your `.well-known/openid-configuration` endpoint is accessible
+2. Test JWKS endpoint returns valid public keys
+3. Validate JWT signature verification works
+4. Test complete authentication flow with sample users
+
+## Next Steps
+
+### Sending a User Operation
+
+Once your users have been authenticated, you can start [sending transactions](/wallets/transactions/send-transactions).
+
+### Sponsor Gas for a User Operation
+
+Start [sponsoring gas fees](/wallets/transactions/sponsor-gas) for transactions via the Gas Manager.
diff --git a/fern/wallets/pages/authentication/login-methods/email-magic-link.mdx b/fern/wallets/pages/authentication/login-methods/email-magic-link.mdx
new file mode 100644
index 000000000..2185ac56c
--- /dev/null
+++ b/fern/wallets/pages/authentication/login-methods/email-magic-link.mdx
@@ -0,0 +1,382 @@
+---
+title: Email Magic Link Authentication
+description: How to implement Email Magic Link authentication across different frameworks
+slug: wallets/authentication/login-methods/email-magic-link
+---
+
+Email magic link authentication is a two-step process:
+
+1. The user enters their email address and requests a magic link
+2. The user clicks the link in their email, which redirects them back to your application to complete authentication
+
+
+ Email OTP has been shown to have up to a 3x higher conversion rate and a
+ 10-second faster flow compared to magic links. Consider using [Email
+ OTP](/wallets/authentication/login-methods/email-otp) for better user
+ experience.
+
+
+
+
+ ## Implementation Options
+
+ You can implement Email Magic Link authentication in two ways:
+
+ * [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
+ * [Custom UI](#custom-ui) - Complete control over the user experience
+
+ ## Pre-built UI Components
+
+ Smart Wallets provides pre-built UI components that handle the entire Email Magic Link authentication flow with minimal code.
+
+ ### Step 1: Add Authentication Components to Your Page
+
+ Before configuring your authentication, first add one of the pre-built components to your application:
+
+
+
+ Or:
+
+
+
+ ### Step 2: Configure Email Magic Link in UI Components
+
+ After adding the components, configure the Email Magic Link authentication in your application config:
+
+ ```tsx twoslash
+ import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+ import { sepolia, alchemy } from "@account-kit/infra";
+
+ const uiConfig: AlchemyAccountsUIConfig = {
+ auth: {
+ sections: [
+ [
+ {
+ type: "email",
+ emailMode: "magicLink",
+
+ // Optional customizations:
+ buttonLabel: "Continue with Email",
+ placeholder: "Enter your email address",
+ },
+ ],
+ ],
+ },
+ };
+
+ export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ },
+ uiConfig,
+ );
+ ```
+
+ ## Custom UI
+
+ If you need complete control over the user experience, you can implement your own custom UI for Email Magic Link authentication using Smart Wallets hooks.
+
+ ### Step 1: Send the Magic Link
+
+ First, prompt your user for their email address and send a magic link:
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate } = useAuthenticate();
+
+ // When the user submits their email
+ const handleSendMagicLink = (email: string) => {
+ authenticate(
+ {
+ type: "email",
+ emailMode: "magicLink",
+ email,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (email magic link + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 2: Handle the Redirect
+
+ When the user clicks the magic link in their email, they'll be redirected back to your application. You need to extract the authentication bundle from the URL and complete the authentication:
+
+ ```tsx twoslash
+ import { useEffect } from "react";
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate } = useAuthenticate();
+
+ // Handle the redirect when the component mounts
+ useEffect(() => {
+ const handleRedirect = () => {
+ const url = new URL(window.location.href);
+ const bundle = url.searchParams.get("bundle");
+
+ if (bundle) {
+ authenticate(
+ {
+ type: "email",
+ bundle,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (email magic link + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ }
+ };
+
+ handleRedirect();
+ }, [authenticate]);
+ ```
+
+ ### Step 3: Track Authentication Status
+
+ Use the `useSignerStatus` hook to determine if the user is authenticated:
+
+ ```tsx twoslash
+ import { useSignerStatus } from "@account-kit/react";
+
+ // Inside your component
+ const { isConnected } = useSignerStatus();
+
+ // You can use isConnected to conditionally render UI
+ ```
+
+
+
+
+ This guide assumes you have already followed the [Setup
+ Guide](/wallets/react-native/signer/setup-guide) and have set up the Alchemy
+ Account Provider using this
+ [guide](/wallets/react-native/signer/authenticating-users/setting-up-the-accounts-provider).
+ Please refer to the guides above for more information on how to properly setup
+ your project.
+
+
+
+ For a complete example of how we can setup a project and use the various
+ available authentication methods, please refer to our [quickstart
+ example](https://github.com/alchemyplatform/account-kit-expo-quickstart).
+
+
+ ### Set the Email Mode to `Magic Link` in your Smart Wallets Dashboard
+
+ In your Alchemy Accounts Dashboard:
+
+ * Navigate to the **Smart Wallets** tab
+
+ * Select the config you would be using for your project and click the **Edit** button
+
+ * Scroll down to the **Email Mode** options in the **Email** section and select **Magic Link**
+
+
+
+ * Click the **Save Changes** button
+
+ ### Send an Email Magic Link to a User
+
+ To send an email magic link to a user, you can use the `authenticate()` function from the `useAuthenticate()` hook with the `type` set to `email` and the `emailMode` set to `magicLink`.
+
+ ```tsx twoslash sign-in-with-magic-link.tsx
+ import { useAuthenticate } from "@account-kit/react-native";
+ import React, { useState } from "react";
+ import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";
+
+ function SignInWithOtp() {
+ const { authenticate } = useAuthenticate();
+ const [email, setEmail] = useState("");
+
+ const handleUserSignInWithMagicLink = () => {
+ try {
+ authenticate({
+ email,
+ type: "email",
+ });
+
+ // Magic link sent to the user's email. Prompt the user to click the link in their email.
+ } catch (e) {
+ Alert.alert("Error sending Magic Link. Check logs for more details.");
+
+ console.log("Error sending Magic Link: ", e);
+ }
+ };
+
+ return (
+
+ Enter Your Email to Sign In
+
+ setEmail(val.toLowerCase())}
+ placeholder="john@doe.com"
+ />
+
+ {({ pressed }) => (
+
+ Sign In
+
+ )}
+
+
+
+ );
+ }
+ ```
+
+ ### Authenticate User via Deep Link
+
+ When a user clicks on the magic link in their email, it should deep link to your app if this has been setup correctly.
+
+ A `bundle` parameter present in the deep link url will be used to authenticate the user and save the user's session.
+
+ Here's an example of what this might look like:
+
+ ```tsx example.tsx twoslash
+ import { useEffect } from "react";
+ import { Linking } from "react-native";
+ import { useAuthenticate } from "@account-kit/react-native";
+
+ const App = () => {
+ const { authenticate } = useAuthenticate();
+
+ // Authenticate a user using a bundle returned from a deep link
+ const handleUserAuth = async ({ bundle }: { bundle: string }) => {
+ authenticate({ bundle, type: "email" });
+ };
+
+ // Handle incoming deep links and authenticate the user
+ const handleIncomingURL = (event: { url: string }) => {
+ const regex = /[?&]([^=#]+)=([^]*)/g;
+
+ let params: Record = {};
+ let match: RegExpExecArray | null;
+
+ while ((match = regex.exec(event.url))) {
+ if (match[1] && match[2]) {
+ params[match[1]] = match[2];
+ }
+ }
+
+ if (!params.bundle) {
+ return;
+ }
+
+ handleUserAuth({
+ bundle: params.bundle ?? "",
+ });
+ };
+
+ // Create a subscription to handle incoming deep links
+ useEffect(() => {
+ const subscription = Linking.addEventListener("url", handleIncomingURL);
+
+ return () => subscription.remove();
+ }, []);
+
+ return null;
+ };
+ ```
+
+
+
+ ## Other Javascript Frameworks
+
+ Email magic link authentication allows you to log in and sign up users using an email address. Your users will receive a link in their inbox which will redirect them to your site (configured in the dashboard) to complete login.
+
+
+ We recommend using the OTP email flow instead, as it is more reliable across different browser environments. OTP flows have also been shown to have up to a 3x higher conversion rate and a 10-second faster flow compared to magic link.
+
+ For setting up the OTP flow, see [Email OTP Authentication](/wallets/authentication/login-methods/email-otp).
+
+
+
+ For setting up an account config, see the [Signer Quickstart](/wallets/signer/quickstart).
+
+
+ ## Authenticate a user
+
+ ```ts twoslash
+ import { signer } from "./signer";
+
+ // send the email
+ // resolves when the user is fully authenticated (magic link + optional MFA), even if completion happens in another tab/window
+ await signer.authenticate({
+ type: "email",
+ emailMode: "magicLink",
+ email: "user@mail.com",
+ });
+
+ // later once the user has clicked the link
+ const url = new URL(window.location.href);
+ const bundle = url.searchParams.get("bundle");
+ if (!bundle) {
+ throw new Error("No bundle found in URL");
+ }
+
+ // resolves when the user is fully authenticated (magic link + optional MFA), even if completion happens in another tab/window
+ await signer.authenticate({
+ type: "email",
+ bundle,
+ });
+ ```
+
+ ### Track Authentication Status
+
+ Use `signer.on("statusChanged", callback)` and the `AlchemySignerStatus` enum to respond to OTP/MFA prompts and completion:
+
+ ```ts twoslash
+ import { signer } from "./signer";
+ import { AlchemySignerStatus } from "@account-kit/signer";
+
+ signer.on("statusChanged", (status) => {
+ switch (status) {
+ case AlchemySignerStatus.AWAITING_EMAIL_AUTH:
+ // show OTP input UI
+ break;
+ case AlchemySignerStatus.AWAITING_MFA_AUTH:
+ // show TOTP input UI
+ break;
+ case AlchemySignerStatus.CONNECTED:
+ // authentication complete
+ break;
+ }
+ });
+ ```
+
+
+
+## Next Steps
+
+### Add Authenticator App (TOTP) Verification (Optional)
+
+Consider enabling [Multi-Factor Authentication](/wallets/react/mfa/setup-mfa) to require users to enter a 6-digit TOTP code from their authenticator app after clicking the magic link. This extra layer of security protects user accounts if their email is compromised.
diff --git a/fern/wallets/pages/authentication/login-methods/email-otp.mdx b/fern/wallets/pages/authentication/login-methods/email-otp.mdx
new file mode 100644
index 000000000..0252a07a9
--- /dev/null
+++ b/fern/wallets/pages/authentication/login-methods/email-otp.mdx
@@ -0,0 +1,548 @@
+---
+title: Email OTP Authentication
+description: How to implement Email OTP authentication across different frameworks
+slug: wallets/authentication/login-methods/email-otp
+---
+
+Email OTP (One-Time Password) authentication is a two-step process:
+
+1. The user enters their email address and requests a verification code
+2. The user enters the 6-digit code they receive in their inbox to complete authentication
+
+
+
+ ## Overview
+
+ You can implement Email OTP authentication in two ways:
+
+ * [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
+ * [Custom UI](#custom-ui) - Complete control over the user experience
+
+ ## Pre-built UI Components
+
+ Smart Wallets provides pre-built UI components that handle the entire Email OTP authentication flow with minimal code.
+
+ ### Step 1: Add Authentication Components to Your Page
+
+ Before configuring your authentication, first add one of the pre-built components to your application:
+
+
+
+ Or:
+
+
+
+ ### Step 2: Configure Email OTP in UI Components
+
+ After adding the components, configure the Email OTP authentication in your application config:
+
+ ```tsx twoslash
+ import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+ import { sepolia, alchemy } from "@account-kit/infra";
+
+ const uiConfig: AlchemyAccountsUIConfig = {
+ auth: {
+ sections: [
+ [
+ {
+ type: "email",
+ emailMode: "otp",
+
+ // Optional customizations:
+ buttonLabel: "Continue with Email",
+ placeholder: "Enter your email address",
+ },
+ ],
+ ],
+ },
+ };
+
+ export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ },
+ uiConfig,
+ );
+ ```
+
+ ## Custom UI
+
+ If you need complete control over the user experience, you can implement your own custom UI for Email OTP authentication using Smart Wallets hooks.
+
+ ### Step 1: Send the OTP
+
+ First, prompt your user for their email address and send an OTP:
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate } = useAuthenticate();
+
+ // When the user submits their email
+ const handleSendCode = (email: string) => {
+ authenticate(
+ {
+ type: "email",
+ emailMode: "otp",
+ email,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (email OTP + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 2: Show OTP Input on Status Change
+
+ Use the `useSignerStatus` hook and `AlchemySignerStatus` enum to react to status changes:
+
+ ```tsx twoslash
+ import React from "react";
+ import { useSignerStatus } from "@account-kit/react";
+ import { AlchemySignerStatus } from "@account-kit/signer";
+
+ const TrackStatus = () => {
+ const { status } = useSignerStatus();
+
+ return (
+ <>
+ {status === AlchemySignerStatus.AWAITING_EMAIL_AUTH && (
+
Prompt the user to enter the OTP code
+ )}
+ >
+ );
+ };
+ ```
+
+ ### Step 3: Verify the OTP
+
+ Once the user receives the code, they'll enter it in your application:
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate } = useAuthenticate();
+
+ // When the user submits the OTP code
+ const handleVerifyCode = (otpCode: string) => {
+ authenticate(
+ {
+ type: "otp",
+ otpCode,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (email OTP + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle invalid code error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 4: Check authentication status
+
+ Use the `useSignerStatus` hook to determine if the user is authenticated:
+
+ ```tsx twoslash
+ import { useSignerStatus } from "@account-kit/react";
+
+ // Inside your component
+ const { isConnected } = useSignerStatus();
+
+ // You can use isConnected to conditionally render UI
+ ```
+
+
+
+
+ This guide assumes you have already followed the [Setup
+ Guide](/wallets/react-native/signer/setup-guide) and have set up the Alchemy
+ Account Provider using this
+ [guide](/wallets/react-native/signer/authenticating-users/setting-up-the-accounts-provider).
+ Please refer to the guides above for more information on how to properly setup
+ your project.
+
+
+
+ For a complete example of how we can setup a project and use the various
+ available authentication methods, please refer to our [quickstart
+ example](https://github.com/alchemyplatform/account-kit-expo-quickstart).
+
+
+ Authenticating a user is easy using the `useAuthenticate()` hook from the `@account-kit/react-native` package.
+
+ ### Set the Email Mode to `One Time Password (OTP)` in your Smart Wallets Dashboard
+
+
+ This is the default mode for email authentication. Only follow these steps if
+ you had previously set the email mode to `Magic Link`.
+
+
+ In your Alchemy Accounts Dashboard:
+
+ * Navigate to the **Smart Wallets** tab
+
+ * Select the config you would be using for your project and click the **Edit** button
+
+ * Scroll down to the **Email Mode** options in the **Email** section and select **One Time Password (OTP)**
+
+
+
+ * Click the **Save Changes** button
+
+ ### Send a One-Time Password (OTP) to a User
+
+ To send an OTP to a user's email, use the `authenticate()` function from the `useAuthenticate()` hook with the `type` set to `email` and the `emailMode` set to `otp`.
+
+ ```tsx twoslash sign-in-with-otp.tsx
+ import { useAuthenticate } from "@account-kit/react-native";
+ import React, { useState } from "react";
+ import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";
+
+ function SignInWithOtp() {
+ const { authenticate } = useAuthenticate();
+ const [email, setEmail] = useState("");
+
+ const handleUserSignInWithOtp = () => {
+ try {
+ authenticate({
+ email,
+ type: "email",
+ });
+
+ // OTP sent to the user's email. Prompt the user to enter the OTP into your app.
+ } catch (e) {
+ Alert.alert("Error sending OTP Code. Check logs for more details.");
+
+ console.log("Error seding OTP CODE: ", e);
+ }
+ };
+
+ return (
+
+ Enter Your Email to Sign In
+
+ setEmail(val.toLowerCase())}
+ placeholder="john@doe.com"
+ />
+
+ {({ pressed }) => (
+
+ Sign In
+
+ )}
+
+
+
+ );
+ }
+ ```
+
+ ### Prompt the User to enter the One-Time Password to complete authentication
+
+ The user will receive an email with a one-time password (OTP) to enter into your app.
+
+ Provide a means for the user to enter the OTP into your app and then call the `authenticate()` function from the `useAuthenticate()` hook passing the OTP code to the `otpCode` parameter, and the `type` set to `otp`.
+
+ ```tsx twoslash verify-otp.tsx
+ import { useAuthenticate } from "@account-kit/react-native";
+ import React, { useState } from "react";
+ import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";
+
+ function VerifyOtp() {
+ const { authenticate } = useAuthenticate();
+ const [otpCode, setOtpCode] = useState("");
+
+ const handleUserVerifyOtp = () => {
+ try {
+ authenticate({
+ otpCode,
+ type: "otp",
+ });
+
+ // OTP verified. User is authenticated.
+ } catch (e) {
+ Alert.alert("Error verifying OTP Code. Check logs for more details.");
+
+ console.log("Error verifying OTP CODE: ", e);
+ }
+ };
+
+ return (
+
+
+ Enter Your OTP Code
+
+
+
+ {({ pressed }) => (
+
+ Submit OTP
+
+ )}
+
+
+
+
+ );
+ }
+ ```
+
+ Here's an example of a Sign In component using OTP. Feel free to embed this into your application to give it a try!
+
+
+ ```tsx sign-in-with-otp.tsx filename="sign-in-with-otp.tsx"
+ // @noErrors
+ import React, { useCallback, useState } from "react";
+ import { View, Text, TextInput, Button } from "react-native";
+ import { useAuthenticate, useUser } from "@account-kit/react-native";
+
+ import { OtpPopUp } from "./otp-popup";
+
+ export const SignInWithOtp = () => {
+ const [email, setEmail] = useState("");
+ const [showOtp, setShowOtp] = useState(false);
+
+ const [loading, setLoading] = useState(false);
+ const { authenticate } = useAuthenticate();
+ const { user } = useUser();
+
+ // Make an authentication request to a user's email
+ const performAuthRequest = useCallback(
+ (email: string) => {
+ try {
+ authenticate({
+ email,
+ type: "email",
+ emailMode: "otp",
+ });
+
+ setLoading(true);
+ setShowOtp(true);
+ } catch (e) {
+ Alert.alert("Error sending OTP Code. Check logs for more details.");
+
+ console.log("Error seding OTP CODE: ", e);
+ }
+ },
+ [authenticate],
+ );
+
+ const completeAuth = useCallback(() => {
+ setLoading(false);
+ setShowOtp(false);
+ }, []);
+
+ return (
+
+ {user && (
+ <>
+ User Authenticated As: {user.email}
+ {user.address}
+ >
+ )}
+
+ Enter Email
+
+
+ );
+ };
+ ```
+
+ ```tsx otp-popup.tsx filename="otp-popup.tsx"
+ // @noErrors
+ import React, { useCallback, useState } from "react";
+ import {
+ Modal,
+ Text,
+ TextInput,
+ Button,
+ SafeAreaView,
+ Alert,
+ } from "react-native";
+ import { useAuthenticate } from "@account-kit/react-native";
+
+ export const OtpPopUp = ({
+ show,
+ completeAuth,
+ close,
+ }: {
+ show: boolean;
+ completeAuth: () => void;
+ close: () => void;
+ }) => {
+ const { authenticate } = useAuthenticate();
+ const [otpCode, setOtpCode] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ // Authenticate a user using a bundle returned from a deep link
+ const handleUserOtp = useCallback(
+ (otpCode: string) => {
+ try {
+ setLoading(true);
+ authenticate({ otpCode, type: "otp" }); //<-- Pass the user's OTP code to the authenticate method using `otp` as the type value
+
+ completeAuth();
+ } catch (e) {
+ Alert.alert("Error verifying OTP Code. Check logs for more details.");
+
+ console.log("Error verifying OTP CODE: ", e);
+ }
+ },
+ [authenticate],
+ );
+
+ return (
+
+
+ Enter OTP
+
+
+
+ );
+ };
+ ```
+
+
+
+
+ ## Other Javascript Frameworks
+
+ Email OTP authentication allows you to log in and sign up users using an email address. Your users will receive six-digit code in their inbox which they can enter in your site to complete login.
+
+
+ For setting up an account config, see the [Signer Quickstart](/wallets/signer/quickstart).
+
+
+ ## Authenticate a user
+
+ ```ts twoslash
+ import { signer } from "./signer";
+
+ // send the email
+ // Promise resolves when the user is fully authenticated (OTP + optional MFA),
+ // even if final step completes in another tab/window
+ await signer.authenticate({
+ type: "email",
+ emailMode: "otp",
+ email: "user@mail.com",
+ });
+
+ // later once the user has entered the code from their email
+ // Promise resolves when the user is fully authenticated (OTP + optional MFA),
+ // even if final step completes in another tab/window
+ await signer.authenticate({
+ type: "otp",
+ otpCode: "123456",
+ });
+ ```
+
+ ### Track Authentication Status
+
+ Use `signer.on("statusChanged", callback)` and the `AlchemySignerStatus` enum to respond to OTP/MFA prompts and completion:
+
+ ```ts twoslash
+ import { signer } from "./signer";
+ import { AlchemySignerStatus } from "@account-kit/signer";
+
+ signer.on("statusChanged", (status) => {
+ switch (status) {
+ case AlchemySignerStatus.AWAITING_EMAIL_AUTH:
+ // show OTP input UI
+ break;
+ case AlchemySignerStatus.AWAITING_MFA_AUTH:
+ // show TOTP input UI
+ break;
+ case AlchemySignerStatus.CONNECTED:
+ // authentication complete
+ break;
+ }
+ });
+ ```
+
+
+
+## Next Steps
+
+### Add Authenticator App (TOTP) Verification (Optional)
+
+If you'd like to add a **second security step** to Email OTP, you can enable [Multi-Factor Authentication](/wallets/react/mfa/setup-mfa). This prompts users for a **6-digit TOTP code** from their authenticator app (e.g. Google Authenticator, Authy) after they verify their email.
diff --git a/fern/wallets/pages/authentication/login-methods/passkey-signup.mdx b/fern/wallets/pages/authentication/login-methods/passkey-signup.mdx
new file mode 100644
index 000000000..25dee2d70
--- /dev/null
+++ b/fern/wallets/pages/authentication/login-methods/passkey-signup.mdx
@@ -0,0 +1,466 @@
+---
+title: Passkey Signup Authentication
+description: How to implement Passkey Signup authentication across different frameworks
+slug: wallets/authentication/login-methods/passkey-signup
+---
+
+Passkeys provide a secure, passwordless authentication method that can be used to create wallets for your users without going through email verification flows. You can implement passkey signup with or without an associated email address.
+
+
+
+
+
+ ## Implementation Options
+
+ You can implement Passkey Signup authentication in two ways:
+
+ * [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
+ * [Custom UI](#custom-ui) - Complete control over the user experience
+
+ ## Pre-built UI Components
+
+ Smart Wallets provides pre-built UI components that handle the entire Passkey Signup authentication flow with minimal code.
+
+ ### Step 1: Add Authentication Components to Your Page
+
+ Before configuring your authentication, first add one of the pre-built components to your application:
+
+
+
+ Or:
+
+
+
+ ### Step 2: Configure Passkey Signup in UI Components
+
+ After adding the components, configure the Passkey Signup authentication in your application config:
+
+ ```tsx twoslash
+ import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+ import { sepolia, alchemy } from "@account-kit/infra";
+
+ const uiConfig: AlchemyAccountsUIConfig = {
+ auth: {
+ sections: [
+ [
+ // Include passkey in a section
+ { type: "passkey" },
+
+ // You can combine with other authentication methods
+ { type: "email" },
+ ],
+ ],
+ // Enable automatic passkey creation after signup
+ addPasskeyOnSignup: true,
+ },
+ };
+
+ export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ },
+ uiConfig,
+ );
+ ```
+
+ ## Custom UI
+
+ If you need complete control over the user experience, you can implement your own custom UI for Passkey Signup authentication using Smart Wallets hooks.
+
+ ### Option 1: Passkey Signup with Email (Recommended)
+
+ This approach associates an email with the passkey, allowing users to recover their account if they lose access to their device.
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate } = useAuthenticate();
+
+ // When the user submits their email and wants to create a passkey
+ const handlePasskeySignup = (email: string) => {
+ // Important: Validate the email before proceeding
+ if (!isValidEmail(email)) {
+ // Handle validation error
+ return;
+ }
+
+ authenticate(
+ {
+ type: "passkey",
+ email,
+ },
+ {
+ onSuccess: () => {
+ // Success - passkey created and user authenticated
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+
+ // Simple email validation function
+ const isValidEmail = (email: string) => {
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+ };
+ ```
+
+
+ It's important that you validate the email before creating an account for the user. This is to prevent users from losing access to their wallets if they lose their device.
+
+
+ ### Option 2: Passkey Signup without Email
+
+ This approach creates a passkey without an associated email. Use this only if you have another recovery mechanism in place.
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate } = useAuthenticate();
+
+ // When the user wants to create a passkey without email
+ const handlePasskeyOnlySignup = (username: string) => {
+ authenticate(
+ {
+ type: "passkey",
+ createNew: true,
+ username, // A unique identifier for the passkey
+ },
+ {
+ onSuccess: () => {
+ // Success - passkey created and user authenticated
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 3: Track Authentication Status
+
+ Use the `useSignerStatus` hook to determine if the user is authenticated:
+
+ ```tsx twoslash
+ import { useSignerStatus } from "@account-kit/react";
+
+ // Inside your component
+ const { isConnected } = useSignerStatus();
+
+ // You can use isConnected to conditionally render UI
+ ```
+
+
+
+
+ This guide assumes you have already followed the [Setup
+ Guide](/wallets/react-native/signer/setup-guide) and have set up the Alchemy
+ Account Provider using this
+ [guide](/wallets/react-native/signer/authenticating-users/setting-up-the-accounts-provider).
+ Please refer to the guides above for more information on how to properly setup
+ your project.
+
+
+
+ For a complete example of how we can setup a project and use the various
+ available authentication methods, please refer to our [quickstart
+ example](https://github.com/alchemyplatform/account-kit-expo-quickstart).
+
+
+ Authenticating a user is easy using the `useAuthenticate()` hook from the `@account-kit/react-native` package. Before we can use that, you'll need to configure your application to associate it with a domain you control.
+
+ ## Step 1: Set an rpId in `createConfig`
+
+ The rpId ("relaying party ID") specifies the domain on which passkeys are allowed to function. While passkeys on web applications are automatically associated with the website's domain, mobile applications must be registered with a domain to prove that they are associated.
+
+ In your call to `createConfig`, pass an `rpId` parameter set to a domain you control. Note that the scheme is always assumed to be "https://" and should be omitted.
+
+ ```typescript
+ const config = createConfig({
+ // ... other config
+ rpId: "your-domain.com",
+ });
+ ```
+
+ ## Step 2: Host a Site Association JSON
+
+ While passkeys on web applications are automatically associated with the website's domain, mobile applications must be registered on that domain to prove that they are associated. To do so, you will need to host a JSON file referencing your app on your domain. The details of doing so differ on iOS and Android.
+
+ ### iOS configuration
+
+ [More information in Apple docs](https://developer.apple.com/documentation/xcode/supporting-associated-domains)
+
+ On your webserver, set up the route
+
+ ```
+ GET https:///.well-known/apple-app-site-association
+ ```
+
+ This route should serve a static JSON object containing your team id and identifier. You should replace `` and `` in the below snippet, so it might appear as e.g. `H123456789.com.yourapp.passkeyExample`.
+
+ ```JSON
+ {
+ "applinks": {},
+ "webcredentials": {
+ "apps": ["."]
+ },
+ "appclips": {}
+ }
+ ```
+
+ Next, in XCode under "Signing & Capabilities", add a new capability of type "Associated Domains". Now add the following, replacing `` with the domain on which you hosted the JSON (e.g. `your-domain.com`):
+
+ ```
+ webcredentials:
+ ```
+
+ ### Android configuration
+
+ [More information in Android docs](https://developer.android.com/identity/sign-in/credential-manager#add-support-dal)
+
+ On your webserver, set up the route
+
+ ```
+ GET https:///.well-known/assetlinks.json
+ ```
+
+ This route should serve a static JSON object containing the following information:
+
+ ```json
+ [
+ {
+ "relation": ["delegate_permission/common.get_login_creds"],
+ "target": {
+ "namespace": "android_app",
+ "package_name": "",
+ "sha256_cert_fingerprints": [""]
+ }
+ }
+ ]
+ ```
+
+ You should replace `` with the package name, e.g. `com.yourapp.passkeyExample`, and `""` with the SHA256 fingerprints of your app's \[signing certificate], e.g. `"FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:12:75:91:03:3B:9C"`.
+
+ ## Step 3: Add a Passkey
+
+ You have the option of creating an account with passkey and email or with passkey alone.
+
+
+
+ ### Option 1: Creating an account with passkey and email (recommended)
+
+ ```tsx twoslash create-passkey-and-email.tsx
+ import { useAuthenticate } from "@account-kit/react-native";
+ import React, { useState } from "react";
+ import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";
+
+ function CreatePasskeyAndEmail() {
+ const { authenticate } = useAuthenticate();
+ const [email, setEmail] = useState("");
+
+ const handleCreatePasskeyAndEmail = () => {
+ // Important: Validate the email before proceeding
+ if (!isValidEmail(email)) {
+ // Handle validation error
+ return;
+ }
+ try {
+ authenticate({
+ type: "passkey",
+ email,
+ });
+
+ // Prompt the user to create a passkey, and create an account once they do.
+ } catch (e) {
+ Alert.alert("Error creating passkey. Check logs for more details.");
+
+ console.log("Error creating passkey: ", e);
+ }
+ };
+
+ return (
+
+ Enter Your Email to Create Account
+
+ setEmail(val.toLowerCase())}
+ placeholder="john@doe.com"
+ />
+
+ {({ pressed }) => (
+
+ Sign In
+
+ )}
+
+
+
+ );
+ }
+
+ // Simple email validation function
+ const isValidEmail = (email: string) => {
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+ };
+ ```
+
+
+ It's important that you validate the email before creating an account for the
+ user. This is to prevent users from losing access to their wallets if they
+ lose their device.
+
+
+ ### Option 2: Creating a New Account
+
+ To create an account with a passkey, use the `authenticate()` function, with the `type` set to `"passkey"` and `createNew` set to `true`.
+
+ ```tsx twoslash create-passkey.tsx
+ import { useAuthenticate } from "@account-kit/react-native";
+ import React, { useState } from "react";
+ import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";
+
+ function CreatePasskey() {
+ const { authenticate } = useAuthenticate();
+
+ const handleCreatePasskey = () => {
+ try {
+ authenticate({
+ type: "passkey",
+ createNew: true,
+ // This will be the name of the saved passkey on the user's device.
+ username: "Your App user",
+ });
+ } catch (e) {
+ Alert.alert("Error creating passkey. Check logs for more details.");
+
+ console.log("Error creating passkey: ", e);
+ }
+ };
+
+ return (
+
+ Create an account with a passkey
+
+
+ {({ pressed }) => (
+
+ Create account
+
+ )}
+
+
+
+ );
+ }
+ ```
+
+ ## Step 4: Sign In with a Passkey
+
+ To sign in with an existing passkey, use the `authenticate()` function, with the `type` set to `"passkey"`.
+
+ ```tsx twoslash create-passkey.tsx
+ import { useAuthenticate } from "@account-kit/react-native";
+ import React, { useState } from "react";
+ import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";
+
+ function SignInWithPasskey() {
+ const { authenticate } = useAuthenticate();
+
+ const handleSignIn = () => {
+ try {
+ authenticate({
+ type: "passkey",
+ createNew: false,
+ });
+ } catch (e) {
+ Alert.alert(
+ "Error signing in with passkey. Check logs for more details.",
+ );
+ console.log("Error signing in with passkey: ", e);
+ }
+ };
+
+ return (
+
+ Sign in with passkey
+
+
+ {({ pressed }) => (
+
+ Sign In
+
+ )}
+
+
+
+ );
+ }
+ ```
+
+
+
+ ## Other Javascript Frameworks
+
+ It is possible to create wallets for users using just a passkey. This is useful for creating wallets for users if you don't want to go through the email OTP or magic link flow.
+
+ ## Authenticate a user with email and passkey
+
+ If you want to allow sign-up and login with a passkey, you can ask the user for an email to associate with their passkey. This way, they can log in with their email or passkey in the future. Under the hood, the email is also used to check if an account exists already so you can have a unified sign-up and login flow.
+
+
+ It's important that you validate this email before creating an account for the user. This is to prevent users from losing access to their wallets if they lose their device.
+
+
+ ```ts twoslash
+ import { signer } from "./signer";
+
+ const result = await signer.authenticate({
+ type: "passkey",
+ email: "user@mail.com",
+ });
+ ```
+
+
+ For setting up an account config, see the [Signer Quickstart](/wallets/signer/quickstart).
+
+
+
diff --git a/fern/wallets/pages/authentication/login-methods/sms-login.mdx b/fern/wallets/pages/authentication/login-methods/sms-login.mdx
new file mode 100644
index 000000000..ef8859b4a
--- /dev/null
+++ b/fern/wallets/pages/authentication/login-methods/sms-login.mdx
@@ -0,0 +1,241 @@
+---
+title: SMS Authentication
+description: How to authenticate users with phone number and SMS OTP code
+slug: wallets/authentication/login-methods/sms-login
+---
+
+
+ SMS auth is currently in closed alpha. If you'd like early access please reach
+ out to [wallets@alchemy.com](mailto:wallets@alchemy.com).
+
+
+Authenticate users with their phone number by sending verification codes via SMS.
+
+## How It Works
+
+SMS authentication is a two-step process:
+
+1. *Send the verification code:* the user enters their phone number and requests a verification code.
+2. *Submit the verification code:* the user enters the 6-digit code they receive via SMS to complete authentication.
+
+The SDK will handle phone number validation, code generation, and delivery automatically.
+
+We support all major countries across Europe, Asia, North and South America.
+
+Pricing varies by country and requires specific area codes to be enabled for your account.
+
+## Prerequisites
+
+* Early access to SMS auth configured on your account: [Request early access!](mailto:wallets@alchemy.com)
+* A configured app with [smart wallets](/wallets/pages/react/quickstart.mdx) and a [gas sponsorship policy](https://dashboard.alchemy.com/gas-manager/policy/create).
+* SDK version ≥v4.53.0
+
+## Implementation
+
+
+
+
+ SMS auth is not yet supported with pre-built UI components. Please follow [this guide](/wallets/pages/react/react-hooks) to set up custom UI and configure authentication with hooks.
+
+
+ ### Step 1: Send the SMS verification code
+
+ Ensure you pass the phone number including the country code in the format `+` (i.e. \`+15553219876).
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react"
+
+ const { authenticate } = useAuthenticate()
+
+ const handleSendCode = (phone: string) => {
+ authenticate(
+ { type: "sms", phone: "+123456789" },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (SMS OTP + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 2: Show OTP input on status change
+
+ Use the `useSignerStatus` hook to show the otp input once the code is sent:
+
+ ```tsx twoslash
+ import React from "react";
+ import { useSignerStatus } from "@account-kit/react";
+ import { AlchemySignerStatus } from "@account-kit/signer";
+
+ const TrackStatus = () => {
+ const { status } = useSignerStatus();
+
+ return (
+ <>
+ {status === AlchemySignerStatus.AWAITING_SMS_AUTH && (
+
Prompt the user to enter the OTP code received via SMS
+ )}
+ >
+ );
+ };
+ ```
+
+ ### Step 3: Verify the OTP code
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ const { authenticate } = useAuthenticate();
+
+ // When the user submits the OTP code
+ const handleVerifyCode = (otpCode: string) => {
+ authenticate(
+ {
+ type: "otp",
+ otpCode,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (SMS OTP).
+ },
+ onError: (error) => {
+ // Handle invalid code error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 4: Check authentication status
+
+ Use the `useSignerStatus` hook we set up before to wait until the user is authenticated:
+
+ ```tsx twoslash
+ import { useSignerStatus } from "@account-kit/react";
+
+ // Inside your component
+ const { isConnected } = useSignerStatus();
+
+ // You can use isConnected to conditionally render UI
+ ```
+
+
+
+
+ SMS auth is not yet supported with pre-built UI components. Please follow
+ [this guide](/wallets/pages/react-native/overview) to
+ set up custom UI and configure authentication with hooks.
+
+
+ ### Step 1: Send the SMS verification code
+
+ Ensure you pass the phone number including the country code in the format `+` (i.e. \`+15553219876).
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react-native"
+
+ const { authenticate } = useAuthenticate()
+
+ const handleSendCode = (phone: string) => {
+ authenticate(
+ { type: "sms", phone: "+123456789" },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (SMS OTP + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 2: Show OTP input on status change
+
+ Use the `useSignerStatus` hook to show the otp input once the code is sent:
+
+ ```tsx twoslash
+ import React from "react";
+ import { useAuthenticate, useSignerStatus } from "@account-kit/react-native"
+ import { AlchemySignerStatus } from "@account-kit/signer";
+ import { View } from "react-native";
+
+ const TrackStatus = () => {
+ const { status } = useSignerStatus();
+
+ return (
+ <>
+ {status === AlchemySignerStatus.AWAITING_SMS_AUTH && (
+ Prompt the user to enter the OTP code received via SMS
+ )}
+ >
+ );
+ };
+ ```
+
+ ### Step 3: Verify the OTP code
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react-native"
+
+ const { authenticate } = useAuthenticate();
+
+ // When the user submits the OTP code
+ const handleVerifyCode = (otpCode: string) => {
+ authenticate(
+ {
+ type: "otp",
+ otpCode,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (SMS OTP).
+ },
+ onError: (error) => {
+ // Handle invalid code error
+ },
+ },
+ );
+ };
+ ```
+
+ ### Step 4: Check authentication status
+
+ Use the `useSignerStatus` hook we set up before to wait until the user is authenticated:
+
+ ```tsx twoslash
+ import { useAuthenticate, useSignerStatus } from "@account-kit/react-native"
+
+ // Inside your component
+ const { isConnected } = useSignerStatus();
+
+ // You can use isConnected to conditionally render UI
+ ```
+
+
+
+ Ensure you've set up your project by following [this](/wallets/pages/core/quickstart) guide first.
+
+ ```ts
+ import { AlchemyWebSigner } from "@account-kit/signer"
+ const signer = new AlchemyWebSigner(...)
+
+ // send sms verification code
+ await signer.authenticate({ type: "sms", phone: "+123456789" })
+ ...
+ // verify sms verification code
+ await signer.authenticate({ type: "otp", otpCode: "123456" })
+ ```
+
+
+
+## Next Steps
+
+* Send [transactions](/wallets/pages/transactions/send-transactions/index.mdx)
diff --git a/fern/wallets/pages/authentication/login-methods/social-login.mdx b/fern/wallets/pages/authentication/login-methods/social-login.mdx
new file mode 100644
index 000000000..d7b81f9d3
--- /dev/null
+++ b/fern/wallets/pages/authentication/login-methods/social-login.mdx
@@ -0,0 +1,234 @@
+---
+title: Social Login Authentication
+description: How to implement Social Login authentication across different frameworks
+slug: wallets/authentication/login-methods/social-login
+---
+
+Social login authentication allows users to authenticate using OAuth providers like Google, Facebook, or custom providers through Auth0. There are two authentication flows available:
+
+1. **Redirect flow**: Redirects the user to the provider's login page and back to your application
+2. **Popup flow**: Opens the provider's login page in a popup window without leaving your application
+
+
+
+ ## Prerequisites
+
+ Before implementing social login in your application, you need to configure your Smart Wallets dashboard and application:
+
+ 1. **Follow the Setup Instructions in the Getting Started Guide**:
+ * See the [Getting Started with Authentication](/wallets/react/quickstart) page for complete setup instructions
+ * Pay special attention to the social authentication provider configuration and whitelisted origins setup
+
+ 2. **Key Configuration Requirements**:
+ * Enable desired social providers in your Smart Wallets dashboard
+ * Add your application's domain to the whitelisted origins
+ * Set `enablePopupOauth: true` in your config if using popup flow
+
+ ## Implementation Options
+
+ You can implement Social Login authentication in two ways:
+
+ * [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
+ * [Custom UI](#custom-ui) - Complete control over the user experience
+
+ ## Pre-built UI Components
+
+ Smart Wallets provides pre-built UI components that handle the entire Social Login authentication flow with minimal code.
+
+ ### Step 1: Add Authentication Components to Your Page
+
+ Before configuring your authentication, first add one of the pre-built components to your application:
+
+
+
+ Or:
+
+
+
+ ### Step 2: Configure Social Login in UI Components
+
+ After adding the components, configure the Social Login authentication in your application config:
+
+ ```tsx twoslash
+ import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+ import { sepolia, alchemy } from "@account-kit/infra";
+
+ const uiConfig: AlchemyAccountsUIConfig = {
+ auth: {
+ sections: [
+ [
+ // Include social login providers
+ { type: "social", authProviderId: "google", mode: "popup" },
+ { type: "social", authProviderId: "facebook", mode: "popup" },
+ { type: "social", authProviderId: "apple", mode: "popup" },
+ ],
+ ],
+ },
+ };
+
+ export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ // Required for popup flow
+ enablePopupOauth: true,
+ },
+ uiConfig,
+ );
+ ```
+
+ ## Custom UI
+
+ If you need complete control over the user experience, you can implement your own custom UI for Social Login authentication using Smart Wallets hooks.
+
+ ### Step 1: Configure Your Application
+
+ Before implementing social login, make sure you've:
+
+ 1. Set up your authentication providers in the Smart Wallets dashboard
+ 2. If using popup flow, set `enablePopupOauth: true` in your Account Kit configuration
+
+ ### Step 2: Implement the Authentication
+
+ Create buttons or UI elements for each social provider you want to support:
+
+ ```tsx twoslash
+ import { useAuthenticate } from "@account-kit/react";
+
+ // Inside your component
+ const { authenticate, isPending } = useAuthenticate();
+
+ // For redirect flow
+ const handleGoogleRedirectLogin = () => {
+ authenticate(
+ {
+ type: "oauth",
+ authProviderId: "google",
+ mode: "redirect",
+ redirectUrl: "/", // Redirect to this page after authentication
+ },
+ {
+ onError: (error) => {
+ // Handle error
+ // The page will redirect on success, so no need for onSuccess handler
+ },
+ },
+ );
+ };
+
+ // For popup flow
+ const handleGooglePopupLogin = () => {
+ authenticate(
+ {
+ type: "oauth",
+ authProviderId: "google",
+ mode: "popup",
+ },
+ {
+ onSuccess: () => {
+ // Authentication successful!
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+ ```
+
+
+
+
+ This guide assumes you have already followed the [Setup
+ Guide](/wallets/react-native/signer/setup-guide) and have set up the Alchemy
+ Account Provider using this
+ [guide](/wallets/react-native/signer/authenticating-users/setting-up-the-accounts-provider).
+ Please refer to the guides above for more information on how to properly setup
+ your project.
+
+
+
+ For a complete example of how we can setup a project and use the various
+ available authentication methods, please refer to our [quickstart
+ example](https://github.com/alchemyplatform/account-kit-expo-quickstart).
+
+
+ ## Authenticate a User via Social Auth
+
+ To authenticate a user via **social auth**, use the `authenticate()` function from the `useAuthenticate()` hook with the `type` set to `OAuth`, the `redirectUrl` set to your app's deep link, the `mode` set to `redirect` and the `authProviderId` set to the social auth provider you want to use.
+
+ Here is an example, authenticating a user via Google:
+
+ ```tsx twoslash example.tsx
+ import React from "react";
+ import { View, Text, Pressable, Alert } from "react-native";
+ import { useAuthenticate } from "@account-kit/react-native";
+
+ function SignInWithSocialAuth() {
+ const { authenticate } = useAuthenticate();
+
+ const handleUserSignInWithGoogle = () => {
+ try {
+ authenticate({
+ type: "oauth",
+ redirectUrl: "://",
+ mode: "redirect",
+ authProviderId: "google",
+ });
+ } catch (e) {
+ Alert.alert("Error authenticating user. Check logs for more details.");
+
+ console.log("Error authenticating user: ", e);
+ }
+ };
+
+ return (
+
+ Sign In with Google
+
+ Sign In
+
+
+ );
+ }
+ ```
+
+
+ Ensure that you have added your app's scheme to your `Whitelisted Origins` in
+ the [Alchemy Dashboard](https://dashboard.alchemy.com/).
+
+
+
+
+ ## Other Javascript Frameworks
+
+ Social login authentication allows you to log in and sign up users using an OAuth provider, such as Google Sign-In or Facebook Login. You can also configure custom providers through Auth0.
+
+
+ To set up configurations to enable social login, see the [Signer Quickstart](/wallets/signer/quickstart) to enable a social policy in the dashboard.
+
+
+ ## Authenticate a user
+
+ ```ts twoslash
+ import { signer } from "./signer";
+
+ await signer.authenticate({
+ type: "oauth",
+ authProviderId: "google", // Choose between the auth providers you selected to support from your auth policy
+ mode: "redirect", // Alternatively, you can choose "popup" mode
+ redirectUrl: "/", // After logging in, redirect to the index page
+ });
+ ```
+
+
+
+## Next Steps
+
+### Enabling Authenticator App (TOTP) MFA
+
+If you want to require a second factor for Social Login, see [Social Login with Multi-Factor Authentication](/wallets/react/mfa/social-login). Once users have set up a TOTP-based authenticator app, they'll be prompted for their 6-digit code automatically.
+
+### Custom Social Providers
+
+For custom OAuth providers like GitHub, Twitter, etc., see the [Custom Social Providers](/wallets/react/login-methods/social-providers) documentation.
diff --git a/fern/wallets/pages/authentication/overview.mdx b/fern/wallets/pages/authentication/overview.mdx
new file mode 100644
index 000000000..7b630c394
--- /dev/null
+++ b/fern/wallets/pages/authentication/overview.mdx
@@ -0,0 +1,129 @@
+---
+title: Overview
+description: Comprehensive guide to authentication methods and user onboarding with Alchemy Smart Wallets
+slug: wallets/authentication/overview
+---
+
+Authentication is the process by which users verify their identity to access your application. In web3, providing users with seamless login experiences for their decentralized applications and blockchain wallets is essential for creating exceptional user experiences that drive adoption.
+
+## Why Seamless Authentication Matters in Web3
+
+Traditional blockchain authentication creates significant barriers to user adoption that generally don't exist in web2 experiences:
+
+* **Complex private key management** - Users must securely store and manage cryptographic keys
+* **Wallet installation requirements** - Additional software downloads and setup
+* **Technical knowledge barriers** - Understanding of blockchain concepts and wallet security
+* **Poor user experience** - Lengthy onboarding flows and confusing interfaces
+
+Alchemy Smart Wallets solves these challenges by providing seamless and familiar login methods while maintaining the security and decentralization benefits of blockchain technology.
+
+## Features
+
+### Multiple Login Methods
+
+Choose from a variety of authentication options to match your users' preferences:
+
+* **[Email OTP](/wallets/authentication/login-methods/email-otp)** - Passwordless login with 6-digit verification codes
+* **[Email Magic Links](/wallets/authentication/login-methods/email-magic-link)** - One-click authentication via secure email links
+* **[Social Login](/wallets/authentication/login-methods/social-login)** - OAuth with Google, Apple, Discord, Twitter, and more
+* **[Passkey Authentication](/wallets/authentication/login-methods/passkey-signup)** - Biometric and hardware-based passwordless login
+* **[Bring Your Own Auth](/wallets/authentication/login-methods/bring-your-own-auth)** - Integrate existing JWT/OIDC authentication systems
+* And more!
+
+### Flexible Implementation Options
+
+Implement authentication in the way that best fits your application:
+
+* **Pre-built UI Components** - Deploy quickly with minimal code using `` or ``
+* **Custom UI Integration** - Build completely custom experiences with headless React hooks
+* **Modal or Embedded Flows** - Choose between popup modals or embedded authentication forms
+* **Whitelabel Solutions** - Customize theming and branding to match your application
+
+### Framework Support
+
+Alchemy Smart Wallets authentication works across multiple platforms:
+
+* **React** - Full-featured hooks, components, and TypeScript support
+* **React Native** - Mobile-optimized authentication flows for iOS and Android
+* **Other JavaScript Frameworks** - Svelte, Vue, and vanilla JavaScript support
+* **Backend Integration** - Server-side authentication validation and user management
+
+### Enterprise-Grade Security
+
+* **Non-custodial Architecture** - Users maintain full control over their private keys
+* **Secure Enclave Storage** - Keys generated and stored in hardware security modules
+* **Multi-Factor Authentication (MFA)** - Additional security layers for high-value operations
+* **Account Recovery** - Flexible recovery options through trusted contacts or backup methods
+
+### Multi-Factor Authentication (MFA)
+
+Add additional security layers for sensitive operations:
+
+* **Email OTP MFA** - Secondary verification via email codes
+* **Email Magic Link MFA** - Additional verification via email links
+* **Social Login MFA** - Secondary authentication through social providers
+
+## Implementation Approaches
+
+### Quick Start with Pre-built Components
+
+Deploy authentication in minutes with minimal code:
+
+```tsx twoslash
+import { AuthCard } from "@account-kit/react";
+
+function App() {
+ return (
+
+ );
+}
+```
+
+## Next Steps
+
+Ready to implement authentication? Start with these guides:
+
+1. **[React Quickstart](/wallets/react/quickstart)** - Complete React setup with authentication
+2. **[Email OTP Guide](/wallets/authentication/login-methods/email-otp)** - Implement email-based authentication
+3. **[Social Login Setup](/wallets/authentication/login-methods/social-login)** - Configure OAuth providers
+4. **[Custom UI Implementation](/wallets/react/react-hooks)** - Build custom authentication experiences
+
+After authenticating users, you can generate wallets for them and they can start sending transactions with just a few clicks.
diff --git a/fern/wallets/pages/authentication/shared/embedded-auth-example.mdx b/fern/wallets/pages/authentication/shared/embedded-auth-example.mdx
new file mode 100644
index 000000000..528bc50b5
--- /dev/null
+++ b/fern/wallets/pages/authentication/shared/embedded-auth-example.mdx
@@ -0,0 +1,18 @@
+## Using Embedded Authentication
+
+To embed authentication directly in your page:
+
+```tsx twoslash
+import React from "react";
+import { AuthCard } from "@account-kit/react";
+
+export default function MyLoginPage() {
+ return (
+
+
+
+ );
+}
+```
+
+For more details on embedded authentication, see the [Embedded Authentication](/wallets/react/ui-components#embedded-auth) documentation.
diff --git a/fern/wallets/pages/authentication/shared/modal-auth-example.mdx b/fern/wallets/pages/authentication/shared/modal-auth-example.mdx
new file mode 100644
index 000000000..b9be898d7
--- /dev/null
+++ b/fern/wallets/pages/authentication/shared/modal-auth-example.mdx
@@ -0,0 +1,16 @@
+## Using Modal Authentication
+
+To add authentication in a modal popup:
+
+```tsx twoslash
+import React from "react";
+import { useAuthModal } from "@account-kit/react";
+
+export default function MyPage() {
+ const { openAuthModal } = useAuthModal();
+
+ return Sign in;
+}
+```
+
+For more details on modal configuration, see the [Modal Authentication](/wallets/react/ui-components#modal-auth) documentation.
diff --git a/fern/wallets/pages/authentication/what-is-a-signer.mdx b/fern/wallets/pages/authentication/what-is-a-signer.mdx
new file mode 100644
index 000000000..633852bf7
--- /dev/null
+++ b/fern/wallets/pages/authentication/what-is-a-signer.mdx
@@ -0,0 +1,138 @@
+---
+outline: deep
+title: Choosing a Signer
+description: Explore Smart Wallets integration guides for signers including Magic.Link, Privy, Web3Auth, EOAs, and many more!
+slug: wallets/signer/what-is-a-signer
+---
+
+A **Signer** is a service (e.g. Alchemy Signer) or application (e.g. MetaMask) that manages the private key and signs operations. Most web3 users today use an [Externally Owned Account (EOA)](https://ethereum.org/en/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) with a self-custodial Signer such as MetaMask to manage the private key.
+
+With Smart Wallets, you will deploy a **smart account** for each user instead of an EOA wallet. This smart account stores the user's assets (e.g. tokens or NFTs).
+
+The signer connected to the `SmartAccountClient` is used to sign messages, data including user operations and transactions. The signatures for the user operation will be only valid and execute if the signer is the owner or one of the owners of the account.
+
+Using Smart Wallets, you can secure your user's account with an email, social login, or passkeys. You can also use a self-custodial wallet like MetaMask as the Signer. You can choose any Signer service or application to manage the Owner private key for the user.
+
+This doc provides a basic introduction to signers and the criteria you should consider when choosing which Signer to use with Smart Wallets in your application.
+
+## Role of a Signer
+
+The Signer plays a crucial role in your app because it controls the user's smart account. The Signer is responsible for:
+
+* Securely storing the user's private key which controls the user's assets
+* Authenticating the user
+* Protecting the user's account from phishing attacks
+* Signing user operations requested by the user, if and only if the user has authenticated
+* Optionally offering account recovery methods
+
+## Alchemy Signer
+
+User security is our top priority. Alchemy Signer is a non-custodial signer service where private keys are securely generated inside a [secure enclave](https://docs.turnkey.com/security/secure-enclaves). Only the end user can access their keys, not Alchemy nor your app. Read more about Turnkey's [security model](https://docs.turnkey.com/security/our-approach) that powers Alchemy Signer.
+
+Alchemy Signer is supported out of the box with Smart Wallets via easy to use [React Hooks](/wallets/react/getting-started) and lower-lever [SDKs](/wallets/signer/quickstart). Using Smart Wallets with the Alchemy Signer, you can secure your user's account with an email, social login, or passkeys.
+
+## Criteria to consider
+
+Here are some important criteria to consider when choosing a Signer.
+
+* **Custody model:** Who has access to the private key?
+ * Self-Custodial: the end user controls the private key and manually approves signature requests
+ * Non-Custodial: a third-party service manages the private key or a subset of the key shares, but cannot sign transactions without the user’s involvement
+ * Custodial: a third-party service manages the private key and can sign transactions without the user’s involvement
+ * **Alchemy Signer** is a fully non-custodial provider - only the end user can access their keys, not Alchemy, not your app, and not Turnkey.
+
+* **Security model**: Assess the security model of the provider. Where is the private key stored? (on a device? in the cloud? on what cloud provider?) Is the private key encrypted? What encryption algorithm is used? Who has access to the decryption keys? This is a non-exhaustive list and we recommend doing further research.
+ * **Alchemy Signer**: Private keys are secured by Turnkey. Turnkey runs all secure workloads in “Secure Enclaves,” a type of Trusted Execution Environment. Read about the security approach [here](https://docs.turnkey.com/security/our-approach).
+
+* **Authentication methods:** What authentication method delivers the right balance of security, self-sovereignty, and ease-of-use for your target users?
+ * **Alchemy Signer:** See full support [here](/wallets/react/ui-components#customize-authentication-ui).
+ * Email magic-link and OTP
+ * Social logins: OAuth providers (google, github, facebook and custom providers via Auth0)
+ * Passkey sign up and login
+
+* **Availability:** If the Signer service provider goes down, will users be able to sign transactions?
+
+ * **Alchemy signer:** The private key export method does not rely on Alchemy's infrastructure, so even if Alchemy is down, a user can still export their private key. Private key export doesn't rely on Alchemy being up, but on Turnkey being up. For full availability if Turnkey goes down the user should export their private key at some point.
+
+* **Key export:** Does the Signer allow the end user to export their private key? Can the user initiate an export even if the service provider has gone down? This is an important factor to ensure the user retains control of their assets no matter what happens to the service provider.
+ * **Alchemy Signer** allows user's to easily [export their private key](/wallets/reference/account-kit/react/hooks/useExportAccount), allowing them a right to exit at any time.
+
+* **Key recovery**: If the user forgets their password or loses their passkey, what recovery methods does the Signer provide? If the provider stores a backup copy of the private key or MPC key shares, where are those backups stored and who has access to them?
+ * **Alchemy signer:** You can add an additional backup login method with a passkey so that users can recover their key with either their email/social or passkey login. Additionally, if using an account that supports more than one owner, you can add multiple owners to an accounts making it recoverable by any owner.
+
+## Types of Signers
+
+### Non-custodial wallets
+
+Non-custodial wallet providers store private keys such that they cannot access the private key without the user’s involvement. For example, the user must provide a password or passkey that only they know in order to decrypt the private key stored by the provider. Users benefit from heightened security, while remaining in control of their private keys at all times. This is similar to a safety deposit box vault: the provider secures the bank vault but only the user has access to the individual safety deposit boxes (e.g. wallets).
+
+**Example**: Alchemy, Turnkey, Magic
+
+### MPC wallets (non-custodial)
+
+Multi-Party Computation (MPC) Signers split the Owner Account private key into key shares that are then distributed to a number of share holders. Share holders only know the value of their key share and transaction holders can sign transactions without revealing their key shares to other holders.
+
+Valid signatures do not always require all shares to sign a transaction. MPC Signers can set a threshold, requiring a certain number of shares for a signature to be valid. Common configurations are 2 of 2 shares or 2 of 3 shares. By requiring multiple shares, MPC models mitigate the risks associated with a single key being compromised.
+
+Some MPC signers provide recovery services in which key share(s) are backed up in the service provider’s cloud, on the end user’s device, or in the end user’s cloud (e.g. iCloud or Google Drive). When evaluating an MPC provider, it’s important to understand where each key share is stored.
+
+**Example**: Privy, Fireblocks MPC, Portal, Capsule, WalletKit
+
+
+ There are two common approaches to MPC.
+
+ Traditionally, MPC services leveraged SSSS (Shamir’s Secret Shard Sharing). This approach generates a private key in one location and then the shares are distributed to the parties involved. When a user wants to sign, they need to retrieve N of M shares and reconstruct the key locally.
+
+ An improvement on SSSS is Threshold Signature Scheme (TSS). In this model, the key is never recreated during signing. Instead, each party is given the message to sign and then signs the payload locally before broadcasting the signature to the rest of the group. This allows for the key material to remain private and deconstructed.
+
+ TSS is safer than SSSS because is possible to create the initial shares without ever constructing the original key on any one device. However, the tradeoff is that signing requires a Peer-to-Peer exchange which introduces latency.
+
+ You can read more about the difference between TSS and SSSS [here](https://www.dynamic.xyz/blog/the-evolution-of-multi-signature-and-multi-party-computation).
+
+
+### Decentralized MPC network (non-custodial)
+
+A decentralized MPC network is an extension on the MPC approach outlined above. Instead of relying on a single, centralized service to store a key share and initiate signature requests, an MPC network distributes this responsibility across many nodes in a network. The user’s private key is split into many key shares with each share store by a different node. The user may request signatures from the network and a valid signature will be produced if and only if a threshold number of nodes agree to sign the request.
+
+Examples: Lit Protocol, Web3Auth (Torus Network)
+
+### Self-custodial wallet
+
+Self-custodial wallets store the private key locally where only the end user can access it. For example, the user may store their seed phrase in a browser extension, in a mobile app using their phone’s secure enclave, or in a hardware wallet. When using a self-custodial wallet, the user is the only one with the power to sign transactions.
+
+Self-custodial wallets require the user to maintain good security hygiene at all times. They also rely on the user to backup a copy of their private key in the event the wallet is lost or destroyed. If the user loses access to the device on which their private key is stored, they will have no way to recover the account unless they backed up the private key in another device or location.
+
+**Example**: MetaMask, Ledger
+
+### Custodial wallet
+
+Custodial wallet providers have full control over the user’s private key and sign transactions on behalf of the user. These services typically implement security measures to ensure that only the authorized user(s) can request a signature. These providers are also typically regulated entities (e.g., qualified custodians). The user must trust this service provider to securely store the private key and sign transactions if and only if the user wishes.
+
+**Example**: Coinbase Custody, Bitgo
+
+## Supported signers
+
+
+ When using the [React](/wallets/react/overview) or
+ [Core](/wallets/core/overview) libraries, all of the account instances are
+ created with the `AlchemyWebSigner` as an owner on the accounts.
+
+
+The Smart Account Signer interface is used to define an owner for [Smart Contract Accounts](/wallets/resources/types#smartcontractaccount).
+The interface takes two forms:
+
+1. `SmartAccountSigner` -- This is the base interface for all signer. It defines methods for signing messages and typed data, as well as getting the address of the signer.
+2. `SmartAccountAuthenticator` -- This is an extension of the `SmartAccountSigner` that exposes additional methods for Signers that require authentication before signing.
+
+Within Smart Wallets and `@aa-sdk/core`, we provide a number of implementations for `SmartAccountSigner` and `SmartAccountAuthenticator`:
+
+1. [`AlchemyWebSigner`](/wallets/reference/account-kit/signer/classes/AlchemyWebSigner) - This is an implementation of the `SmartAccountAuthenticator` that uses the our signer service to provision private keys securely
+ for end users. It allows you to authenticate your users with methods more familiar to them, such as email or social login.
+2. [`LocalAccountSigner`](/wallets/reference/aa-sdk/core/classes/LocalAccountSigner) - This signer is useful if you have a mnemonic or private key locally that you want to use as an
+ owner of a smart contract account. This is useful for testing and development purposes.
+3. [`WalletClientSigner`](/wallets/reference/aa-sdk/core/classes/WalletClientSigner) - This signer is useful if you want to use a [wallet client](https://viem.sh/docs/clients/wallet) as an owner.
+ Since a wallet client can wrap any [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) compliant provider, this is useful if you want to use EOA extensions or 3rd party signer service SDKs.
+
+***
+
+*Disclaimer: This page refers to third-party services, products software, technology, and content (collectively, “Third-Party Services”) that may be integrated or interact with Alchemy’s software and services. Alchemy is not responsible for any Third-Party Service, or for any compatibility issues, errors, or bugs caused in whole or in part by the Third-Party Service or any update or upgrade thereto. Your use of any Third-Party Service is at your own risk. You are responsible for obtaining any associated licenses and consents to the extent necessary for you to use the Third-Party Services. Your use of the Third-Party Services may be subject to separate terms and conditions set forth by the provider (including disclaimers or warnings), separate fees or charges, or a separate privacy notice. You are responsible for understanding and complying with any such terms or privacy notice.*
diff --git a/fern/wallets/pages/authorization/policies/overview.mdx b/fern/wallets/pages/authorization/policies/overview.mdx
new file mode 100644
index 000000000..1f6cdd1be
--- /dev/null
+++ b/fern/wallets/pages/authorization/policies/overview.mdx
@@ -0,0 +1,93 @@
+---
+title: Overview
+description: Overview of Smart Wallet Policies and Session Keys
+slug: wallets/signer/policies/overview
+---
+
+**Smart Wallets** enable seamless crypto experiences with gas sponsorship, batched transactions, and chain abstraction. Secured by non-custodial Trusted Execution Environments (TEEs) and enterprise-grade audited smart contract accounts, smart wallets protect user assets with both offchain and onchain safeguards.
+
+Policies allow you to set rules and constraints governing how smart wallets operate, ensuring security and control over onchain actions. Using the policy dashboard, you can easily configure rules such as spending limits, contract allowlists, or denylists. This overview explains how you can leverage policies to further secure your smart wallet.
+
+**Key Features:**
+
+* **Granular control**: Define allowable actions, such as approved contracts or maximum transfer amounts.
+* **Scalability**: Policies are built on Alchemy's high-performance RPC and gas sponsorship systems, ensuring seamless operation at scale.
+* **Composable Security:** Policies can be defined onchain or offchain and seamlessly composed to authorize smart wallet operations to multi-layer beyond standard authentication mechanisms.
+
+
+
+## Offchain Policies (EVM & Solana)
+
+Offchain policies shift rule enforcement to Alchemy's offchain infrastructure. In EVM, these policies can also be composed with the onchain policies to provide multi-layered security.
+
+Offchain policies support many rules including:
+
+* **Transaction Limits:** Restrict the value of transfers (e.g., cap at 1 ETH per transaction or 10 ETH daily) to prevent overspending or unauthorized large moves.
+* **Contract Allowlists:** Limit interactions to approved smart contracts (e.g., Uniswap, Aave), enhancing security by blocking untrusted protocols.
+* **Multi-factor Authentication:** Add an extra layer of security by requiring multiple authentication factors to sign a transaction.
+* **Chain Restrictions:** Limit transactions to only certain chains
+* **Gas Sponsorship Rules:** Define custom conditions for gas sponsorship (e.g. sponsor up to $10 in gas, or first 10 transactions, allowlist/blocklist senders for sponsorship, custom rules, etc.).
+
+## Onchain Policies (EVM only)
+
+Onchain policies lock in trust, transparency, and developer right to exit — rules baked into smart contracts enforce security and consistency without relying on fragile offchain servers or middlemen. Because the rules are defined onchain, they remain consistent regardless of which key provider is used.
+
+* **Transaction Limits:** Use onchain modules to restrict the value of transfers (e.g., cap at 1 ETH per transaction or 10 ETH daily) to prevent overspending or unauthorized large moves.
+* **Contract Allowlists:** Use onchain modules to limit interactions to approved smart contracts (e.g., Uniswap, Aave), enhancing security by blocking untrusted protocols.
+* **Multi-Signature Requirements:** Enforce quorum rules (e.g., 2-of-3 signers for high-value transactions), ideal for shared custody, treasury management, or onchain multi-factor authentication.
+* **Time Restrictions:** Allow transactions only within specific time windows (e.g., within the next 24 hours), enabling scheduled operations.
+* **Gas Sponsorship Rules:** Define requirements for paymaster definitions (e.g., require your token is used for gas or that transactions are sponsored under certain conditions).
+* **Asset-Specific Caps:** Use onchain session keys to set limits on ERC-20 token transfers (e.g., max 1000 USDC per action) or native assets.
+* **Deny Lists:** Block interactions with flagged addresses or contracts, mitigating risks from known vulnerabilities.
+
+## What Are Session Keys?
+
+Session Keys allow you to add multiple signers to smart wallet with scoped permissions that are validated onchain. This unlocks:
+
+* Multiple owners for wallets
+* Automated tasks like claiming rewards or recurring payments
+* Stronger security by limiting exposure of the main account key
+
+They're ideal for reducing UX friction while maintaining onchain guarantees.
+
+### Skip duplicate confirmations
+
+With session keys, users don't need to approve every dapp interaction using their main wallet. Instead, a dapp can use a session key to perform follow-up actions — fast and frictionless — all within a defined scope.
+
+**Example**: Enabling auto-approval for repeated steps like claiming testnet tokens or submitting votes to reduce user friction of having to sign many transactions.
+
+Session keys unlock a simplified authentication process by allowing users to interact with apps without needing to confirm each action using their primary key. Instead, users create a session key with permissions specific to the app, then the app can use that key for future actions from the apps server or client. This speeds up the user interaction and provides a smoother experience and allows apps to have secure server side wallet control.
+
+### Automate actions
+
+Use session keys to run workflows without needing user input each time. Automate:
+
+* Claim flows
+* Token approvals
+* Recurring transactions
+
+By granting only the necessary permissions, session keys ensure automation stays secure.
+
+Users and apps can automate actions within predefined limits using specific on-chain permissions. Session keys can be used to streamline processes like recurring payments, contract interactions, or any activity that benefits from automation.
+
+### Securely delegate access
+
+Session keys reduce exposure of the main private key by creating temporary or restricted alternatives. Even if compromised, session keys limit damage because they're restricted to only the permissions you defined, not your entire account.
+
+Session keys reduce risk by limiting access:
+
+* They're scoped to specific actions
+* Can expire after a set time
+* Can be revoked independently of the main account key
+
+By delegating authorization to a separate key, the exposure of the main private key is minimized.
+
+### Build with granular permissions
+
+Session keys are safe because they're built on a flexible permission system. You control exactly what a session key can do, when it can act, and how much it can spend.
+
+Modular Account V2 includes a growing library of permission types — from spending limits and time windows to contract allowlists and function restrictions. You can also build custom permission modules for specialized use cases.
+
+This granular control lets you create session keys that are perfectly scoped for each use case, whether that's a one-time NFT mint, recurring DeFi interactions, or complex multi-step workflows.
+
+> Want to build your own permission system? Let us know — Modular Account V2 is designed to plug in custom modules easily.
diff --git a/fern/wallets/pages/authorization/policies/session-key-permissions.mdx b/fern/wallets/pages/authorization/policies/session-key-permissions.mdx
new file mode 100644
index 000000000..77dceca66
--- /dev/null
+++ b/fern/wallets/pages/authorization/policies/session-key-permissions.mdx
@@ -0,0 +1,47 @@
+---
+title: Supported Permissions for Session Keys
+description: Explore the supported permission types you can apply to session keys to safely delegate access in smart accounts.
+slug: wallets/smart-contracts/modular-account-v2/session-key-permissions
+---
+
+## What Permissions Can You Apply to Session Keys?
+
+Session keys work best when they are tightly scoped. Our smart wallets, specifically Modular Account V2, gives you a flexible set of built-in permission types. These permissions are validated onchain removing any dependency on an offchain engine and increasing security of users' wallets.
+
+Use these to limit what a key can do, for how long, where it can call, and how much it can spend.
+
+### Time Range
+
+Supports limiting keys with a start and/or expiry time ranges
+
+### ERC-20 Spending Limits
+
+Supports limiting how much of a specific ERC-20 token a key may spend
+
+### **Gas and native token spending limits**
+
+Supports limiting how much native tokens, e.g. ETH on mainnet, a key may spend
+
+### Access Control Lists
+
+Supports limiting function selectors and/or external contracts that a key may interact with.
+
+### Internal Selector Allowlist
+
+Supports limiting what functions a key may call on the Modular Account (e.g. installing or uninstalling modules, or upgrading the account)
+
+> You can combine multiple permissions on a single session key. These can be layered to match the minimum required access for your use case.
+
+## Composing Permissions: Fine-Grained Control
+
+Permissions are composable. You can apply multiple permissions to a single session key to tightly define its behavior.
+
+Example: You could create a session key that:
+
+* Only works between June 1–June 15
+* Can call just one staking contract
+* Spend no more than 100 USDC
+
+Use Case: In Alchemy's modular wallet [demo](https://demo.alchemy.com/), one key is scoped to auto-stake a user's funds once a day, but only within a capped budget and only on a verified contract.
+
+By combining permissions, you can build tailored, safe delegation schemes — perfect for dapps, relayers, and complex workflows.
diff --git a/fern/wallets/pages/authorization/smart-account-types/choosing-a-smart-account.mdx b/fern/wallets/pages/authorization/smart-account-types/choosing-a-smart-account.mdx
new file mode 100644
index 000000000..2c326d65d
--- /dev/null
+++ b/fern/wallets/pages/authorization/smart-account-types/choosing-a-smart-account.mdx
@@ -0,0 +1,41 @@
+---
+title: Choosing a Smart Account
+description: Learn about different smart account implementations to use with Smart Wallets
+slug: wallets/smart-contracts/choosing-a-smart-account
+---
+
+## Why Smart Accounts?
+
+Smart accounts are the key to unlocking the best online user experience ever had-- enabling features that have never been possible with traditional EOAs (externally-owned-accounts) like session keys, granular permissions, different authentication methods like email and passkeys, and much more.
+
+Beyond that, alchemy's Smart Wallets offers a simple & clean SDK to provide your users with top-tier ERC-4337 smart account features. With Smart Wallets's most powerful, most natively-supported account being Modular Account V2.
+
+MAv2 is the most fully-featured and efficient smart account out there, allowing for limitless modular expansion. All the while the account is secured by extensive [audits](https://github.com/alchemyplatform/modular-account/tree/develop/audits) as well as a [bug bounty on Cantina.](https://cantina.xyz/bounties/246de4d3-e138-4340-bdfc-fc4c95951491)
+
+MAv2 also strives to be maximally gas-efficient, being the **only** account where runtime deployment breaks the **sub-100k gas barrier**, which is cheaper than *one Uniswap swap*. For more details around MAv2, and for more sweet benchmarks, check out the [MAv2 overview!](/wallets/smart-contracts/modular-account-v2/overview)
+
+## Modular Account V2
+
+MAv2 is a zero-compromise smart account designed from the ground up for maximal security, modularity, and gas-efficiency. MAv2 abides by [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900), meaning if you have a use case, there's probably a module for it-- and if there isn't, the simple, straightforward module interface ensures building new innovative features is a walk in the park.
+
+By default, MAv2 includes a fallback validation. This allows a zero-customization setup where the account's owner (which, technically, could be another account!) has full control over the account. The fallback validation signer, or the account's owner, can be swapped or the entire validation can be disabled entirely in favor of one or more other validations.
+
+Validations are themselves modules that define an authorization scheme with granularly managed control over the account. You could use a [WebAuthn validation](https://github.com/alchemyplatform/modular-account/blob/develop/src/modules/validation/WebAuthnValidationModule.sol) to support passkeys or biometrics for example.
+
+MAv2 also supports [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md) out of the box, allowing EOAs to seamlessly turn into maximally expandable smart accounts.
+
+## Light Account
+
+While **MAv2** is the recommended choice, Smart Wallets also supports other audited and gas-optimized ERC-4337 smart account implementations.
+
+[Light Account](/wallets/smart-contracts/other-accounts/light-account) is a minimalist ERC-4337 smart account optimized for **low gas costs**. It is based on **Ethereum Foundation's SimpleAccount** but adds key improvements like:
+
+* Ownership transfers
+* ERC-1271 signature validation
+
+While **Light Account is fully audited**, it does **not** support **advanced modular features like modules or session keys**.
+
+## Bring Your Own Smart Account
+
+If you have your own **ERC-4337-compatible** smart account, you can integrate it with Smart Wallets.\
+See our guide on [using a custom smart account](/wallets/third-party/smart-contracts).
diff --git a/fern/wallets/pages/authorization/smart-account-types/modular-account-v2/overview.mdx b/fern/wallets/pages/authorization/smart-account-types/modular-account-v2/overview.mdx
new file mode 100644
index 000000000..6d416aa09
--- /dev/null
+++ b/fern/wallets/pages/authorization/smart-account-types/modular-account-v2/overview.mdx
@@ -0,0 +1,58 @@
+---
+title: Modular Account V2
+description: An overview of the Modular Account V2 smart account.
+slug: wallets/smart-contracts/modular-account-v2/overview
+---
+
+Alchemy's Modular Account V2 is the most robust and feature-rich smart account on the market with top-tier enterprise grade security. Being an ERC-6900 account, MAv2 can tap into a rich ecosystem of modules comprising different authentication methods like multisig or webauthn, as well as permissions and more!
+
+With multiple developer-years of work and two comprehensive audits from [ChainLight](https://github.com/alchemyplatform/modular-account/blob/develop/audits/2024-12-03_chainlight_14afcd8.pdf) and [Quantstamp](https://github.com/alchemyplatform/modular-account/blob/develop/audits/2024-12-11_quantstamp_14afcd8.pdf), MAv2 is the most advanced smart account available:
+
+* 40%+ reduced gas costs
+* Rich ecosystem of modules from teams like Circle
+* Super simple & clean developer experience
+
+## Cost
+
+Cost matters, so gas optimization was essential throughout the whole development process. As a result, MAv2 is one of the most optimized smart contracts ever written.
+
+As an example, see the gas comparison for deployment execution costs below:
+
+| | **Runtime gas for account creation** | **% More expensive than MAv2** |
+| ------------------------------ | ------------------------------------ | ------------------------------ |
+| **Alchemy Modular Account v2** | **97,764** | - |
+| **ZeroDev Kernel v3** | 180,465 | 84.6% |
+| **Safe (with 4337 module)** | 289,207 | 195.8% |
+
+We have a whole host of open-source benchmarks [here!](https://github.com/alchemyplatform/aa-benchmarks)
+
+At scale, these optimizations could save tens of thousands of dollars in gas fees if not more-- all without compromising on features or security.
+
+## Modularity
+
+Along with Circle, Trust Wallet, Quantstamp and the Ethereum Foundation, we've already developed multiple plug & play modules for use with ERC-6900 accounts like MAv2. Out of the box, on the validation side, MAv2 supports [session keys](/wallets/smart-contracts/modular-account-v2/session-keys), Webauthn validation (passkeys/biometrics).
+
+On the permissions front, MAv2 already has first-class support for the following:
+
+* Allowlists, ensuring a validation can only access what you want it to and nothing else.
+* ERC-20 spend limits, think [session keys with spend limits!](/wallets/smart-contracts/modular-account-v2/session-keys)
+* Native spend limits, which take gas into account.
+* Enforcing expiries, so your session keys can last as short or as long as you need them to.
+
+Note that these features are only possible thanks to the modular nature of ERC-6900, as they're all separate modules. This also means it's possible to combine any number of modules to perfectly fine-tune your permission set.
+
+Want a session key that's only valid for 24 hours, can only spend up to 0.001 ETH (incl. gas) and 100 USDC, all the while it's only able to authenticate calls to one specific address with one specific function? [Go for it!](/wallets/smart-contracts/modular-account-v2/session-keys)
+
+## Simplicity
+
+Regardless of how feature-rich MAv2 is, it's all for nothing if it's not easy to use. The account standard MAv2 adheres to has over 5,000 words-- it's understandably intimidating. However, the depth of ERC-6900 allows each feature in MAv2 to be secure and ultimately extremely simple for you, the developer.
+
+Leveraging the standard's lack of ambiguity, aa-sdk can safely create high-level zero-cost abstractions, which translate down to RPC calls exactly how you expect them to. No more fighting with your tools.
+
+The SDK is designed from the ground up to get out of your way so you can focus on what matters-- building the best smart wallet experience for your app!
+
+## EIP-7702 support
+
+EIP-7702 is an upcoming Ethereum upgrade set to launch in the coming months as part of the Pectra hardfork. Specifically, EIP-7702 enables Externally Owned Accounts (EOAs) to use smart contract account features.
+
+You can create an EIP-7702 compliant version of an MAv2 account, learn how to [here](/wallets/transactions/using-eip-7702)
diff --git a/fern/wallets/pages/bundler-api/bundler-api-endpoints/bundler-api-endpoints.mdx b/fern/wallets/pages/bundler-api/bundler-api-endpoints/bundler-api-endpoints.mdx
new file mode 100644
index 000000000..61db052f8
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/bundler-api-endpoints/bundler-api-endpoints.mdx
@@ -0,0 +1,22 @@
+---
+title: Bundler API Endpoints
+description: Use the Bundler APIs to send userOps in EVM networks.
+subtitle: Use the Bundler APIs to send userOps in EVM networks.
+url: https://alchemy.com/docs/reference/bundler-api-endpoints
+slug: reference/bundler-api-endpoints
+---
+
+## What exactly is the Bundler API?
+
+The Bundler APIs are a collection of [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace) compliant JSON-RPC endpoints.
+
+## Methods
+
+| Method | Description |
+| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
+| [eth_sendUserOperation](/reference/eth-senduseroperation) | Sends a userOp to the given EVM network. |
+| [eth_estimateUserOperationGas](/reference/eth-estimateuseroperationgas) | Estimates the gas values for a `userOp`. |
+| [eth_supportedEntryPoints](/reference/eth-supportedentrypoints) | Returns an array of the `entryPoint` addresses supported by the client. |
+| [eth_getUserOperationByHash](/reference/eth-getuseroperationbyhash) | Returns a `userOp` based on a hash (`userOpHash`). |
+| [eth_getUserOperationReceipt](/reference/eth-getuseroperationreceipt) | Get the `UserOperationReceipt` based on the `userOpHash` value. |
+| [rundler_maxPriorityFeePerGas](/reference/rundler-maxpriorityfeepergas) | Returns a fee per gas that is an estimate of how much users should set as a priority fee in `userOps` for Rundler endpoints. |
diff --git a/fern/wallets/pages/bundler-api/bundler-api-fee-logic.mdx b/fern/wallets/pages/bundler-api/bundler-api-fee-logic.mdx
new file mode 100644
index 000000000..44684a8ac
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/bundler-api-fee-logic.mdx
@@ -0,0 +1,74 @@
+---
+title: Bundler API Fee Logic
+description: Fee logic for Bundler API ( eth_sendUserOperation )
+subtitle: Fee logic for Bundler API ( eth_sendUserOperation )
+url: https://alchemy.com/docs/reference/bundler-api-fee-logic
+slug: reference/bundler-api-fee-logic
+---
+
+Fee logic for Bundler API ( `eth_sendUserOperation` )
+
+## Fee Logic
+
+To provide its services, Alchemy's Rundler requires fees when using [`eth_sendUserOperation`](/reference/eth-senduseroperation), and these fees differ based on the mainnet or testnet in use. Rundler's requirements for priority fees are expressed via the [`rundler_maxPriorityFeePerGas`](/reference/rundler-maxpriorityfeepergas) endpoint.
+
+Each Bundler API endpoint has an [associated compute unit cost](/reference/compute-unit-costs#gas-manager--bundler-apis).
+
+**The following table provides a detailed breakdown of the fee logic and recommendations for each network type:**
+
+| Network Type | Network Name | Extra Fee Requirement |
+| ------------ | --------------------- | ---------------------------------------------------------------------- |
+| Mainnet | All except Arb chains | Priority fee buffer: 25% Base fee buffer: 27% minimum, 50% recommended |
+| Mainnet | Arbitrum Nitro chains | Priority fee buffer: None Base fee buffer: 27%, 50% recommended |
+| Testnet | All testnets | Priority fee buffer: None Base fee buffer: 27%, 50% recommended |
+
+**Recommended Actions for Calculating `maxFeePerGas`**:
+
+1. **Fetch Current Base Fee**: Use the method [`eth_getBlockByNumber`](/reference/eth-getblockbynumber) with the `'latest'` parameter to get the current `baseFeePerGas`.
+
+2. **Apply Buffer on Base Fee**: To account for potential fee changes, apply a buffer on the current base fee based on the requirements and recommendations in the table shown above. (27% is the minimum for bundler acceptance, but we recommend at least 50%)
+
+3. **Fetch Current Priority Fee with Rundler**: Use the [`rundler_maxPriorityFeePerGas`](/reference/rundler-maxpriorityfeepergas) method to query the current priority fee for the network.
+
+4. **Apply Buffer on Priority Fee**: Once you have the current priority fee using `rundler_maxPriorityFeePerGas`, increase it according to the fee requirement table shown above for any unexpected changes (No buffer for Arbitrum Mainnet and 25% buffer for all other mainnets).
+
+5. **Determine `maxFeePerGas`**: Add the buffered values from steps 2 and 4 together to obtain the `maxFeePerGas` for your user operation.
+
+
+ The Alchemy bundler requires the simulated gas limit efficiency of both a UO's pre-operation gas and call gas to be greater than or equal to 15%. (Note: the 15% efficiency value is subject to change and we will update docs if it does.)
+
+ **Gas limit efficiency** = gas used / gas limit
+
+ **Pre-operation gas** = `preVerificationGas` + `verificationGasLimit` + `paymasterVerificationGasLimit`
+
+ **Note**: for EP v0.6 `paymasterVerificationGasLimit` == `verificationGasLimit`
+
+ This check is intended to prevent user operations from taking up gas limit space in a bundle, but then not using the gas on-chain. This could prevent other UO's from being bundled that otherwise could have. It is recommended to use the results from the `eth_estimateUserOperationGas` endpoint, with slight buffers if desired while keeping above 15% efficiency.
+
+
+
+ It's recommended to use our [AA SDK](https://accountkit.alchemy.com/) to
+ minimize the complexities of estimating user op gas fees.
+
+
+***
+
+## FAQs
+
+### How many compute units will it cost to create a smart contract account?
+
+You can deploy a smart contract account in two ways:
+
+* **Contract deployment by a sending transaction**: You can deploy the smart contract account which is essentially a contract by sending a transaction through [`eth_sendRawTransaction`](/reference/eth-sendrawtransaction) from an EOA. This is same as other contract deployments. In this case, it will cost you 250 CUs which is the cost of calling `eth_sendRawTransaction`.
+* **Contract deployment by sending a user operation**: You can deploy the smart contract account by sending a user operation through [`eth_sendUserOperation`](/reference/eth-senduseroperation) with a non-empty `initCode`. In this case it will cost you 1000 CUs which is the cost of calling `eth_sendUserOperation`.
+
+### How do we determine fee values to give your UO the best chance of landing on chain?
+
+* [alchemy\_requestGasAndPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) is an opinionated endpoint that tries to set fee values that give your user operations a high chance of landing on-chain. Its likely that we're over-estimating here a bit, but this is intentional in order to land your UOs faster!
+
+* We encourage you to try out different fee percentages and determine what works best for you as a balance between cost and chance/time to mine.
+
+* For [alchemy\_requestGasAndPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) we offer the ability to override our fee estimates with the `feeOverride` parameters.
+
+ * We default to increasing baseFee by 50% and priorityFee by 5%.
+ * **Note**: The feeOverride parameters don't include preVerificationGas (PVG) . The method will always increase the estimated PVG by 5% to give the UO a better chance to mine if the L1 /L2 fee ratio changes. If you would like to modify this value, its recommended you use [alchemy\_requestPaymasterAndData](/reference/alchemy-requestpaymasteranddata) instead.
diff --git a/fern/wallets/pages/bundler-api/bundler-api-quickstart.mdx b/fern/wallets/pages/bundler-api/bundler-api-quickstart.mdx
new file mode 100644
index 000000000..ba5952864
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/bundler-api-quickstart.mdx
@@ -0,0 +1,60 @@
+---
+title: Bundler API Quickstart
+description: Learn about the Bundler API and how you can use it work with user operations.
+subtitle: Learn about the Bundler API and how you can use it work with user operations.
+url: https://alchemy.com/docs/reference/bundler-api-quickstart
+slug: reference/bundler-api-quickstart
+---
+
+## What is a Bundler?
+
+The concept of a Bundler is introduced by [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337), which aims to bring Account Abstraction to EVM chains. With the expectation of an increased adoption of Smart Contract Wallets (SCWs) for their user-friendly experience and flexibility, the Bundler plays a vital role. Essentially, it forwards the user operations to the Entrypoint which are then further forwarded to the smart contract accounts for execution. Our custom built Bundler called Rundler provides high performance and reliability, it's written in Rust and is [completely open source](https://github.com/alchemyplatform/rundler).
+
+To gain deeper insights into this topic, explore our [blog post on Account Abstraction](https://www.alchemy.com//blog/account-abstraction).
+
+## What is the Bundler API?
+
+The Bundler APIs are a collection of [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337#rpc-methods-eth-namespace) compliant JSON-RPC endpoints which makes it possible for users to work with user operations.
+
+
+ Please note that using
+ [`aa-sdk`](https://accountkit.alchemy.com/getting-started/setup.html) to send
+ `UserOperations` is the easiest way to access our Bundler without directly
+ dealing with the raw APIs.
+
+
+## Bundler API Endpoints
+
+All of the Bundler API endpoints are listed below along with links to their individual pages and brief descriptions.
+
+* ### [`eth_sendUserOperation`](/docs/node/bundler-api/bundler-api-endpoints/eth-send-user-operation)
+
+ Submits a user operation to a Bundler. If the request is successful, the endpoint will return a user operation hash that the caller can use to look up the status of the user operation. If it fails, or another error occurs, an error code and description will be returned.
+
+* ### [`eth_estimateUserOperationGas`](/docs/node/bundler-api/bundler-api-endpoints/eth-estimate-user-operation-gas)
+
+ Estimates the gas values for a user operation. It returns the `preVerificationGas`, `verificationGasLimit`, and `callGasLimit` values associated with the provided user operation.
+
+* ### [`eth_getUserOperationByHash`](/docs/node/bundler-api/bundler-api-endpoints/eth-get-user-operation-by-hash)
+
+ Returns a user operation based on the given user operation hash. It returns the user operation along with extra information including what block/transaction it was included in. If the operation has not yet been included, it will return `null`.
+
+* ### [`eth_getUserOperationReceipt`](/docs/node/bundler-api/bundler-api-endpoints/eth-get-user-operation-receipt)
+
+ Returns a user operation receipt ( metadata associated with the given user operation ) based on the given user operation hash. It returns `null` if the user operation has not yet been included.
+
+* ### [`rundler_maxPriorityFeePerGas`](/docs/node/bundler-api/bundler-api-endpoints/rundler-max-priority-fee-per-gas)
+
+ Returns a fee per gas that is an estimate of how much users should set as a priority fee in UOs for Rundler endpoints.
+
+* ### [`eth_supportedEntryPoints`](/docs/node/bundler-api/bundler-api-endpoints/eth-supported-entry-points)
+
+ Returns a list of Entrypoint contract addresses supported by the bundler endpoints.
+
+## Request Limits
+
+| API Endpoint | Free Tier | Growth Tier | Enterprise Tier |
+| --------------------------------------------------------------------------------------------- | ----------- | ----------- | --------------- |
+| [eth\_sendUserOperation](/docs/node/bundler-api/bundler-api-endpoints/eth-send-user-operation) | 18,000 / hr | 54,000 / hr | Unlimited |
+
+These are hourly request limits.
diff --git a/fern/wallets/pages/bundler-api/bundler-faqs.mdx b/fern/wallets/pages/bundler-api/bundler-faqs.mdx
new file mode 100644
index 000000000..da9f711f3
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/bundler-faqs.mdx
@@ -0,0 +1,258 @@
+---
+title: FAQs
+description: Frequently asked questions about the Bundler
+subtitle: Frequently asked questions about the Bundler
+url: https://alchemy.com/docs/wallets/reference/bundler-faqs
+slug: wallets/reference/bundler-faqs
+---
+
+## userOperation
+
+### How can I track the status of a userOp?
+
+To understand the status of a userOp you can use the `eth_getUserOperationByHash` method as follows: loop over `eth_getUserOperationByHash` for as long as you are willing to wait for the userOp to land. If it still returns `null` after the timeout, there are two possibilities:
+
+1. **The userOp is still pending**: This is the most common scenario and typically means *the fees are too low*. In this case, you should drop and replace the userOp with higher fees.
+2. **The userOp has been dropped**: The most common (but rare) reason is that they paymaster signature has expired. However, this should rarely happen if you set a reasonable sponsorship expiry time, unless there is a significant delay in sending the userOp after the paymaster signs it.
+
+### How do I get my userOp unstuck from the mempool?
+
+For EIP-1559 fee markets, the base fee is fixed per block. To prioritize the inclusion of your userOp and get it unstuck from the mempool, you need to increase the `maxPriorityFeePerGas`. This can be achieved by dropping and replacing the userOp with a new one that has a higher `maxPriorityFeePerGas`.
+
+### Can a userOp be accepted by the bundler and then dropped while it’s in the mempool?
+
+This is a possible but rare scenario and can occur due to several reasons:
+
+* The userOp is replaced by another userOp from the same sender with the same nonce.
+* The signature of the Gas Manager has expired, rendering the userOp ineligible for sponsorship.
+* The validation function of the userOp fails when it is being bundled into a transaction for on-chain submission.
+* The mempool runs out of memory, causing the bundler to drop userOps with the lowest fees.
+
+### Can I retrieve the hash of the bundle transaction right after calling `eth_sendUserOperation` without waiting for the transaction to get mined?
+
+The transaction hash is not included in the response of `eth_sendUserOperation` for the following reasons:
+
+* The hash of the bundle transaction that a userOp is included in can change before that userOp is mined. This can happen for multiple reasons, such as the pending bundle transaction being dropped and replaced by the bundler if it’s underpriced, or the bundle transaction being frontrun by another bundle that includes the userOp.
+* Adding the transaction hash to the response of `eth_sendUserOperation` is incompatible with the future P2P mempool, since any bundler can bundle the userOp and land it on chain.
+
+## Common Errors
+
+### What are `precheck failed` errors and how do I handle them?
+
+`precheck failed` errors are typically related to gas and/or fees. Our bundler follows the standard [ERC 4337 implementation](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4337.md#client-behavior-upon-receiving-a-useroperation) for gas and fee checks to 1) ensure your userOp lands on chain and to 2) protect the bundler from potential attacks in order to support scalability.
+
+These errors are often related to market movement between the time gas and fees are estimated and the time when userOps are submitted to the bundler. This issue is especially prevalent on testnets. Our bundler currently rejects upon sending userOps if they are underpriced compared to the network rate to ensure inclusion in a block.
+
+To handle these errors, you should:
+
+* add buffers on top of our gas estimates to account for market fluctuations
+* implement retry mechanisms.
+
+### What is a `Replacement Underpriced` error and how can I resolve it?
+
+You might get a `"Replacement Underpriced Error"` when using `eth_sendUserOperation`. This error occurs when a user already has an existing userOp in the mempool. userOps can become "stuck" in the mempool if their gas fee limits are too low to be included in a bundle.
+
+To resolve this, you need to increase both `maxFeePerGas` and `maxPriorityFeePerGas` by at least 10%.
+
+To do so, follow the next steps:
+
+1. **Re-estimate gas fees**: This can be done in various ways which are mentioned below:
+
+ 1. Use the [`eth_maxPriorityFeePerGas`](/reference/eth-maxpriorityfeepergas) method to obtain `maxPriorityFeePerGas`.
+ 2. If using the Alchemy SDK, use the [`getFeeData`](/reference/sdk-getfeedata) method to obtain both the current `maxPriorityFeePerGas` and `maxFeePerGas`. This method is also available on web3 libraries like `ethers.js` and can be accessed through the provider as `provider.getFeeData()`.
+
+2. **Choose the suitable increase values**: Once you have the re-estimated gas fees, choose the maximum of a 10% increase or the re-estimated values. This ensures that your new gas fee is competitively priced to be included in a block.
+
+3. **Account for Rundler's service tip**: Rundler requires a small tip for its services via `maxPriorityFeePerGas`. Detailed information about this can be found [below](/reference/bundler-faqs#fees).
+
+After calculating the new values, send your `userOp` again with the updated fees and it should go through successfully.
+
+### What are `-32521: Execution reverted` errors and how can I debug them?
+
+These occur when the `userOp` reverts during execution. These errors typically contain `revertData`, which are optional bytes with additional information about the revert.
+
+What revertData contains:
+
+1. **First 4 bytes**: the error selector (e.g., 0x08c379a0 = Error(string)). This is the Keccak-256 hash of the error signature.
+2. **Remaining bytes**: ABI-encoded arguments for that error (if any).
+
+To debug these errors, you should:
+
+* Isolate the first 4 bytes of `revertData`.
+* Paste into a signatures DB (e.g. [4byte.sourcify.dev](https://4byte.sourcify.dev)).
+ * If it matches a known error, you’ll see the standard signature.
+ * If not, it's a **custom error** - check the contract's ABI or source for error declarations.
+* Decode any arguments (using a tool like [calldata.swiss-knife.xyz](https://calldata.swiss-knife.xyz/decoder) or your ABI).
+* Map the error back to the contract source code to understand under what conditions it reverts.
+
+## Parallel nonces
+
+### What is the maximum number of supported parallel nonces?
+
+Our bundler supports up to 4 parallel nonces (default value from ERC-7562) for unstaked senders and unlimited parallel nonces for staked senders. See [below](/reference/bundler-faqs#what-is-the-minimum-amount-that-must-be-staked-with-the-entrypoint) for stake requirements.
+
+Unstaked accounts that attempt to exceed this limit will receive the error `Max operations (4) reached for account`. Staking the account removes the restriction. Accounts can be staked by calling `addStake(uint32 unstakeDelaySec)` on the EntryPoint contract, and later `unlockStake()` followed by `withdrawStake(address payable)` to recover the stake.
+
+Staked senders are subject to ERC-7562 reputation rules. If a sender submits a large number of userOps and subsequently invalidates them all, they may be throttled or banned.
+
+### Can I include multiple parallel nonces in a single bundle?
+
+To include multiple parallel nonces in the same bundle, the account must stake the [minimum stake amount](/reference/bundler-faqs#what-is-the-minimum-amount-that-must-be-staked-with-the-entrypoint) with the EntryPoint.
+
+## Signatures
+
+### What is a dummy signature?
+
+Our APIs are compatible with any type of smart account. This means regardless of the smart account you're using, our endpoints will work with it. However, different smart accounts have unique ways of signing transactions, known as signature patterns. A dummy signature is essentially a template or example signature that aligns with the signature pattern of your specific account type.
+
+For certain API endpoints (ex: [eth\_estimateUserOperationGas](/reference/eth-estimateuseroperationgas)), particularly those involved in gas estimation, a dummy signature is required in the request. This is because these endpoints need to simulate or estimate the transaction without actually executing it, and the dummy signature helps in this process.
+
+## Fees
+
+### What are the bundler fees?
+
+To provide its services, Alchemy's Rundler requires fees when using [`eth_sendUserOperation`](/reference/eth-senduseroperation), and these fees differ based on the mainnet or testnet in use. Rundler's requirements for priority fees are expressed via the [`rundler_maxPriorityFeePerGas`](/reference/rundler-maxpriorityfeepergas) endpoint.
+
+Each Bundler API endpoint has an [associated compute unit cost](/reference/compute-unit-costs#gas-manager--bundler-apis).
+
+The following table provides a detailed breakdown of the fee logic and recommendations for each network type:
+
+| Network Type | Network Name | Extra Fee Requirement |
+| ------------ | --------------------- | ---------------------------------------------------------------------- |
+| Mainnet | All except Arb chains | Priority fee buffer: 25% Base fee buffer: 27% minimum, 50% recommended |
+| Mainnet | Arbitrum Nitro chains | Priority fee buffer: None Base fee buffer: 27%, 50% recommended |
+| Testnet | All testnets | Priority fee buffer: None Base fee buffer: 27%, 50% recommended |
+
+Recommended Actions for Calculating `maxFeePerGas`:
+
+1. **Fetch Current Base Fee**: Use the method [`eth_getBlockByNumber`](/reference/eth-getblockbynumber) with the `'latest'` parameter to get the current `baseFeePerGas`.
+
+2. **Apply Buffer on Base Fee**: To account for potential fee changes, apply a buffer on the current base fee based on the requirements and recommendations in the table shown above. (27% is the minimum for bundler acceptance, but we recommend at least 50%)
+
+3. **Fetch Current Priority Fee with Rundler**: Use the [`rundler_maxPriorityFeePerGas`](/reference/rundler-maxpriorityfeepergas) method to query the current priority fee for the network.
+
+4. **Apply Buffer on Priority Fee**: Once you have the current priority fee using `rundler_maxPriorityFeePerGas`, increase it according to the fee requirement table shown above for any unexpected changes (No buffer for Arbitrum Mainnet and 25% buffer for all other mainnets).
+
+5. **Determine `maxFeePerGas`**: Add the buffered values from steps 2 and 4 together to obtain the `maxFeePerGas` for your user operation.
+
+
+ The Alchemy bundler requires the simulated gas limit efficiency of both a UO's pre-operation gas and call gas to be greater than or equal to 15%. (Note: the 15% efficiency value is subject to change and we will update docs if it does.)
+
+ **Gas limit efficiency** = gas used / gas limit
+
+ **Pre-operation gas** = `preVerificationGas` + `verificationGasLimit` + `paymasterVerificationGasLimit`
+
+ **Note**: for EP v0.6 `paymasterVerificationGasLimit` == `verificationGasLimit`
+
+ This check is intended to prevent user operations from taking up gas limit space in a bundle, but then not using the gas on-chain. This could prevent other UO's from being bundled that otherwise could have. It is recommended to use the results from the `eth_estimateUserOperationGas` endpoint, with slight buffers if desired while keeping above 15% efficiency.
+
+
+
+ It's recommended to use our [Smart Wallets
+ SDK](https://www.alchemy.com/docs/wallets) to minimize the complexities of
+ estimating userOp gas fees.
+
+
+### How do we determine fee values to give your userOp the best chance of landing on chain?
+
+* [alchemy\_requestGasAndPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) is on opinionated endpoint that tries to set fee values that give your userOps a high chance of landing on-chain. It's likely that we're over-estimating here a bit, but this is intentional in order to land your UOs faster!
+
+* We encourage you to try out different fee percentages and determine what works best for you as a balance between cost and chance/time to mine.
+
+* For [alchemy\_requestGasAndPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) we offer the ability to override our fee estimates with the `feeOverride` parameters.
+
+ * We default to increasing baseFee by 50% and priorityFee by 5%.
+ * **Note**: The feeOverride parameters don't include preVerificationGas (PVG). The method will always increase the estimated PVG by 5% to give the UO a better chance to mine if the L1 /L2 fee ratio changes. If you would like to modify this value, its recommended you use [alchemy\_requestPaymasterAndData](/reference/alchemy-requestpaymasteranddata) instead.
+
+## EntryPoint
+
+### Which EntryPoint versions are supported?
+
+We currently support versions v0.6 and v0.7 of ERC-4337. If you need support for v0.8, please contact us at [wallets@alchemy.com](mailto:wallets@alchemy.com).
+
+### Which EntryPoint version should I use, v0.6 or v0.7?
+
+The latest version of ERC-4337 is v0.7, which introduces optimizations aimed at improving the experience for both developers and end users. These include gas savings for users, optimized data structures, better gas estimation, simplified postOp logic, and structured errors during validation.
+
+The appropriate version to use is determined by the smart contract account for which you are trying to send a userOp. Typically, a smart contract account will be written to be compatible with either v0.6 or v0.7. To determine which version is compatible, you should look at the smart contract account’s source code and check the first parameter of the `validateUserOp` function. If it has type `UserOperation`, the account uses v0.6. If the parameter type is `PackedUserOperation`, the account uses v0.7.
+
+For more information about the differences between the versions, refer to the specifications for [ERC-4337 v0.6.0](https://github.com/eth-infinitism/account-abstraction/blob/v0.6.0/eip/EIPS/eip-4337.md) and [ERC-4337 v0.7.0](https://github.com/eth-infinitism/account-abstraction/blob/v0.7.0/erc/ERCS/erc-4337.md), particularly the description of the user operation fields.
+
+### At which addresses are the EntryPoint contracts for v0.6 and v0.7 deployed?
+
+The EntryPoint contracts for v0.6 and v0.7 are deployed at the following addresses across all chains supported by Alchemy:
+
+EntryPoint v0.7: `0x0000000071727De22E5E9d8BAf0edAc6f37da032`\
+EntryPoint v0.6: `0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789`
+
+### When will EntryPoint v0.6 be deprecated?
+
+We currently do not have a date set to deprecate support for EntryPoint v0.6 but plan to deprecate sometime in 2026. Please ensure that you have migrated to EntryPoint v0.7 by that time. If you have any questions or need assistance with the migration process, please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+## Entity Staking Requirements
+
+### What is the minimum amount that must be staked with the EntryPoint?
+
+Mainnets:
+
+| Chain Name | Min Stake (Native Token) |
+| ------------------- | ------------------------ |
+| Ethereum Mainnet | 0.1 ETH |
+| BNB Mainnet | 0.1 BNB |
+| Polygon Mainnet | 100 MATIC |
+| Arbitrum One | 0.1 ETH |
+| Optimism Mainnet | 0.1 ETH |
+| Zora Mainnet | 0.1 ETH |
+| Frax Mainnet | 0.1 ETH |
+| Base Mainnet | 0.1 ETH |
+| Polynomial Mainnet | 0.1 ETH |
+| World Chain Mainnet | 0.1 ETH |
+| Shape Mainnet | 0.1 ETH |
+| Arbitrum Nova | 0.1 ETH |
+| Berachain Mainnet | 0.1 BERA |
+| Anime Mainnet | 0.1 ETH |
+| Race Mainnet | 0.1 ETH |
+| Unichain Mainnet | 0.1 ETH |
+| Soneium Mainnet | 0.1 ETH |
+| Ink Mainnet | 0.1 ETH |
+| Story Mainnet | 0.1 ETH |
+| Celo Mainnet | 0.1 CELO |
+| OpBNB Mainnet | 0.4 BNB |
+
+Testnets:
+
+| Chain Name | Min Stake (Native Token) |
+| ------------------- | ------------------------ |
+| Ethereum Sepolia | 0.1 ETH |
+| BNB Testnet | 0.1 BNB |
+| Polygon Amoy | 10 MATIC |
+| Arbitrum Sepolia | 0.1 ETH |
+| Optimism Sepolia | 0.1 ETH |
+| Zora Sepolia | 0.1 ETH |
+| Alchemy Sepolia | 0.1 ETH |
+| Base Sepolia | 0.1 ETH |
+| Polynomial Sepolia | 0.1 ETH |
+| World Chain Sepolia | 0.1 ETH |
+| Shape Sepolia | 0.1 ETH |
+| Anime Sepolia | 0.1 ETH |
+| Race Sepolia | 0.1 ETH |
+| Unichain Sepolia | 0.1 ETH |
+| Soneium Minato | 0.1 ETH |
+| Ink Sepolia | 0.1 ETH |
+| Monad Testnet | 0.1 ETH |
+| Openloot Sepolia | 0.1 ETH |
+| Wylerchain Sepolia | 0.1 ETH |
+| Gensyn Testnet | 0.1 ETH |
+| Rise Testnet | 0.1 ETH |
+| Story Aeneid | 0.1 ETH |
+| Converge Testnet | 0.1 ETH |
+| Celo Alfajores | 0.1 CELO |
+| Tea Sepolia | 0.1 ETH |
+| Educhain Testnet | 0.1 ETH |
+| OpBNB Testnet | 0.4 BNB |
+
+Paymasters and factories must have at least the above stake or their userOps will be rejected. Accounts only need to stake if they wish to exceed 4 parallel nonces in the Bundler's mempool; otherwise, userOps beyond this limit will be rejected. The same stake amounts apply to accounts.
+
+### What is the minimum delay value?
+
+The minimum unstake delay required by Rundler is 1 Day. Paymasters and factories must configure at least this delay or their userOps will be rejected. Staked accounts are subject to the same delay requirement.
diff --git a/fern/wallets/pages/bundler-api/bundler-rpc-errors.mdx b/fern/wallets/pages/bundler-api/bundler-rpc-errors.mdx
new file mode 100644
index 000000000..e7b893e61
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/bundler-rpc-errors.mdx
@@ -0,0 +1,122 @@
+---
+title: Bundler RPC Errors
+description: Learn about the different Bundler error codes.
+subtitle: Learn about the different Bundler error codes.
+url: https://alchemy.com/docs/wallets/reference/bundler-rpc-errors
+slug: wallets/reference/bundler-rpc-errors
+---
+
+This document provides a list of the JSON-RPC errors that you might encounter when using the Bundler API. These are in addition to the [standard JSON-RPC error codes](/reference/error-reference#json-rpc-error-codes) returned by a bad method call.
+
+### `-32500`: Rejected by EntryPoint’s `simulateValidation`
+
+* **Description**: The `userOp` is rejected by entryPoint's `simulateValidation`, during account creation or validation. The `-32500` error code may be accompanied by an additional [`AAxx` revert code](/reference/entrypoint-revert-codes) provided by the EntryPoint to give additional guidance.
+
+**Data Fields**:
+
+* `reason`: Optional string providing the main reason for the rejection.
+* `inner_reason`: Optional string providing additional details for the rejection.
+* `revert_data`: Optional bytes containing additional data related to the rejection.
+
+### `-32501`: Rejected by paymaster's `validatePaymasterUserOp`
+
+* **Description**: The `userOp` is rejected by paymaster's `validatePaymasterUserOp`.
+
+**Data Fields**:
+
+* `paymaster`: Address of the paymaster.
+* `reason`: String providing the reason for the rejection.
+
+### `-32502`: Opcode violation
+
+* **Description**: The `userOp` does an Opcode violation or tries to access inaccessible storage. Before submitting `userOps`, bundlers must make sure `userOps` don't grief the bundler by accessing banned opcodes when checking a signature.
+
+**Data Fields**:
+
+* `entity`: Type of entity (e.g., paymaster, sender) accessing the storage.
+
+* `opcode`: Opcode that caused the violation.
+
+* Additional fields (when associated with `StakeTooLow`):
+
+ * `needs_stake`: Entity that needs staking.
+ * `accessing_entity`: Type of entity accessing the storage.
+ * `accessed_address`: Address of the accessed storage.
+ * `accessed_entity`: Optional type of accessed entity.
+ * `slot`: Storage slot accessed.
+ * `minimum_stake`: Minimum required stake.
+ * `minimum_unstake_delay`: Minimum required unstake delay.
+
+### `-32503`: Out of time range
+
+* **Description**: Either the account or the paymaster returned a time-range, and it is already expired or will expire soon.
+
+**Data Fields**:
+
+* `valid_until`: Timestamp indicating the valid until time.
+* `valid_after`: Timestamp indicating the valid after time.
+* `paymaster`: Optional address of the paymaster.
+
+### `-32504`: Throttled or banned
+
+* **Description**: The `userOp` was rejected because the paymaster or aggregator is throttled or banned.
+
+**Data Fields**:
+
+* `entity`: Type of entity (e.g., paymaster, aggregator) that is throttled or banned.
+* `address`: Address of the entity.
+
+### `-32505`: Stake or unstake-delay too low
+
+* **Description**: The `userOp` was rejected because the paymaster's or signature aggregator's stake or unstake delay was too low.
+
+**Data Fields**:
+
+* `needs_stake`: Entity that needs staking.
+* `accessing_entity`: Type of entity accessing the storage.
+* `accessed_address`: Address of the accessed storage.
+* `accessed_entity`: Optional type of accessed entity.
+* `slot`: Storage slot accessed.
+* `minimum_stake`: Minimum required stake.
+* `minimum_unstake_delay`: Minimum required unstake delay.
+
+### `-32506`: Unsupported aggregator
+
+* **Description**: The `userOp` was rejected because the wallet specified unsupported signature aggregator.
+
+**Data Fields**:
+
+* `aggregator`: Address of the unsupported aggregator.
+
+### `-32507`: Invalid signature
+
+* **Description**: The `userOp` was rejected because it contains an invalid signature from the sender or the paymaster.
+
+**Data Fields**:
+
+* No additional data fields.
+
+### `-32521`: Execution reverted
+
+* **Description**: The `userOp` was reverted during the execution phase.
+
+**Data Fields**:
+
+* `revert_data`: Optional bytes containing additional data related to the revert.
+
+### `-32602`: Invalid `userOp`
+
+* **Description**: The `userOp` struct/fields sent to the bundler were invalid.
+
+**Data Fields**:
+
+* `current_max_priority_fee`: Optional U256 containing the current maximum priority fee.
+* `current_max_fee`: Optional U256 containing the current maximum fee.
+
+### `-32603`: Internal error
+
+Description: Internal JSON-RPC error.
+
+**Data Fields**:
+
+* No additional data fields.
diff --git a/fern/wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v06-revert-codes.mdx b/fern/wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v06-revert-codes.mdx
new file mode 100644
index 000000000..254583d71
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v06-revert-codes.mdx
@@ -0,0 +1,315 @@
+---
+title: EntryPoint v0.6 Revert Codes
+description: Learn about the revert codes returned by the ERC-4337 EntryPoint v0.6
+subtitle: Learn about the revert codes returned by the ERC-4337 EntryPoint v0.6
+url: https://alchemy.com/docs/wallets/reference/entrypoint-v06-revert-codes
+slug: wallets/reference/entrypoint-v06-revert-codes
+---
+
+Bundler JSON-RPC error codes are often accompanied by an additional AAxx code provided by the EntryPoint.
+
+* **AA1x** error codes relate to **creating an account**
+* **AA2x** error codes relate to **the sender or the user operation itself** (nonce, signature, prefund, time validity, verification gas)
+* **AA3x** error codes relate to **paymasters** (deposit, expiration, verification)
+* **AA4x** error codes relate to **verification**
+* **AA5x** errors relate to **post execution actions**
+* **AA9x** error codes are **general and not related to a certain theme** (invalid addresses, failed sends, invalid aggregator, etc.)
+
+***
+
+## AA10 sender already constructed
+
+The `sender` has already been created. This error may occur if you attempt to create an account multiple times.
+
+**Possible Solutions**
+
+1. Remove the `initCode` from the userOp struct.
+
+***
+
+## AA13 initCode failed or OOG
+
+The `initCode` failed to create the smart account. There are two possible reasons:
+
+1. The `initCode` ran out of gas (OOG)
+2. The `initCode` reverted during the account deployment process
+
+**Possible Solutions**
+
+1. Check the account has native token to pay for its deployment if you aren't using a paymaster.
+2. Check that the factory address in the `initCode` is correct (the factory address is the first 20 bytes of the `initCode`).
+3. Check that the `verificationGasLimit` is high enough for the `initCode` to complete without running out of gas.
+4. If the `initCode` reverted, investigate why using tools like [Tenderly](https://tenderly.co/).
+
+***
+
+## AA14 initCode must return sender
+
+The address of the smart account deployed with the `initCode` does not match the sender address of the user operation.
+
+**Possible Solutions**
+
+1. Check that the `initCode` is correct.
+
+ 1. The first 20 bytes should be the factory address.
+ 2. The remaining bytes should be the encoded function call.
+
+2. Verify that the sender address was generated deterministically from `initCode`.
+
+***
+
+## AA15 initCode must create sender
+
+The `initCode` does not return any sender address.
+
+Possible reasons:
+
+1. The `initCode` factory is not creating an account.
+2. The `initCode` factory is creating an account, but is not returning the deployed sender address.
+
+**Possible solutions**
+
+1. Check that the `initCode` is correct.
+
+ 1. The first 20 bytes should be the factory address.
+ 2. The remaining bytes should be the encoded function call.
+
+2. Verify that the `initCode` factory is implemented correctly, i.e., it deploys the smart account and returns the sender address.
+
+***
+
+## AA20 account not deployed
+
+The sender of the userOp is not deployed and the `initCode` is not specified.
+
+**Possible Solutions**
+
+1. Check that you are using the correct sender address.
+2. If this is the first transaction by this account make sure the `initCode` is included in the userOp.
+3. Check that you are sending the userOp to the correct network.
+
+***
+
+## AA21 Didn’t pay prefund
+
+The sender did not have enough native tokens to prefund the EntryPoint for the user operation.
+
+**Possible Solutions**
+
+1. If you are not using a paymaster, check that the account has enough native token to cover the required prefund.
+2. If you are using a paymaster, check that the paymaster and data fields are set.
+
+***
+
+## AA22 expired or not due
+
+The `signature` of the user operation is not valid because it is outside of the specified time range.
+
+This error occurs when the `block.timestamp` is after the `validUntil` timestamp or before the `validAfter` timestamp.
+
+**Possible Solutions**
+
+1. If you are using time-based signatures, check that the `validAfter` and `validUntil` fields are set correctly and that the userOp is sent within the specified range.
+2. If you not using time-based signatures, check that the `validAfter` and `validUntil` fields are set to `0`.
+
+***
+
+## AA23 reverted (or OOG)
+
+The sender signature validation reverted or ran out of gas (OOG).
+
+**Possible Solutions**
+
+1. Check that the `verificationGasLimit` is high enough to cover the gas costs of`validateUserOp`.
+2. If you are not using a paymaster, check that the sender has enough native tokens to cover the required prefund.
+3. If you are using a paymaster to cover the gas fees, verify that the paymaster and data fields are set.
+
+***
+
+## AA24 signature error
+
+The signature of the `userOp` is invalid.
+
+**Possible Solutions**
+
+1. Check that the userOp was correctly signed.
+
+ 1. The `userOpHash` is correctly computed
+ 2. The `entryPointAddress` is correct
+ 3. The `chainId` is correct
+ 4. The smart account expects the same type of signature
+
+***
+
+## AA25 Invalid account nonce
+
+The `nonce` of the userOp is invalid. The userOp may be reusing an old nonce or formatting the nonce incorrectly.
+
+**Possible Solutions**
+
+1. Check that you are not using a `nonce` that has already been used.
+2. Check that you are not using a `nonce` that is too far in the future (more than 10 higher than the current `nonce`.
+3. Check that the `nonce` is formatted correctly.
+
+***
+
+## AA30 paymaster not deployed
+
+The paymaster contract is not deployed.
+
+**Possible Solutions**
+
+1. Check that the first 20 bytes of the `paymasterAndData` field are the address of the paymaster contract you intend to use.
+2. Check that the paymaster contract is deployed on the network you are using.
+
+***
+
+## AA31 paymaster deposit too low
+
+The paymaster contract does not have enough funds deposited into the EntryPoint contract to cover the gas of the userOp.
+
+**Possible Solutions**
+
+1. Please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+***
+
+## AA32 Paymaster expired or not due
+
+The paymaster's signature is outside of the specified time range and has expired.
+
+**Possible Solutions**
+
+1. Make sure you are sending the userOp within the `sponsorship expiry` period specified in your Gas Manager policy.
+
+***
+
+## AA33 reverted (or OOG)
+
+The paymaster signature was rejected or verifying the paymaster signature ran out of gas (OOG).
+
+**Possible Solutions**
+
+1. Check that the `verificationGasLimit` is high enough to cover the `validatePaymasterUserOp` function's gas costs.
+2. If the userOp is well formed with a high enough `verificationGasLimit`, please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+***
+
+## AA34 Signature Error
+
+The paymaster's signature is invalid.
+
+**Possible solutions**
+
+1. Check the format of the signature in the `paymasterAndData` or the `paymaster` field depending on the EntryPoint version you are using.
+
+***
+
+## AA40 over verificationGasLimit
+
+The amount of gas used to verify the smart account or paymaster signature was higher than userOp's `verificationGasLimit`.
+
+**Possible Solutions**
+
+1. Check that the `verificationGasLimit` set for the userOp is high enough to cover the gas used for smart account and paymaster verification.
+2. Investigate why the smart account and/or paymaster used more gas than expected using tools like [Tenderly](https://tenderly.co/).
+3. Please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+***
+
+## AA41 too little verificationGas
+
+Verifying the userOp took too much gas and did not complete.
+
+**Potential Solutions**
+
+1. Increase the `verificationGasLimit`.
+
+***
+
+## AA50 postOp reverted
+
+The paymaster contract's postOp function reverted.
+
+**Possible Solutions**
+
+1. Please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+***
+
+## AA51 prefund below actualGasCost
+
+The actual gas cost of the userOp ended was higher than the prefund covered by the smart account or the paymaster.
+
+***
+
+## AA90 invalid beneficiary
+
+The bundler specified an invalid address or the zero address as the beneficiary of the userOp.
+
+**Possible Solutions**
+
+1. Please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+***
+
+## AA91 failed send to beneficiary
+
+The beneficiary of the bundler fee was unable to receive compensation from the EntryPoint.
+
+**Possible Solutions**
+
+1. Please file a ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+***
+
+## AA92 internal call only
+
+A function intended only for internal calls within the EntryPoint was called externally. This occurs if `innerHandleOp` is invoked by an address other than the EntryPoint itself.
+
+**Possible Solutions:**
+
+1. Do not call `innerHandleOp` directly from outside the EntryPoint.
+2. Ensure you are calling the EntryPoint’s main methods (`handleOps`, etc.) rather than internal helper methods.
+
+***
+
+## AA93 invalid paymasterAndData
+
+The paymasterAndData field is of an incorrect length.
+
+**Possible Solutions**
+
+1. Check that `paymasterAndData` is either empty or at least 20 bytes long.
+
+***
+
+## AA94 gas values overflow
+
+A gas value of the userOp did not fit into a `uint160`.
+
+**Possible Solutions**
+
+1. Check that all the gas limit and gas price fields of the userOp fit into `uint160`.
+
+***
+
+## AA95 out of gas
+
+The entire operation (or a sub-call) ran out of gas. This is usually due to too low gas limits passed to `handleOps`.
+
+**Possible Solutions:**
+
+1. Increase the gas limit provided to the bundler or `handleOps` call.
+2. Optimize your code to require less gas.
+
+***
+
+## AA96 invalid aggregator
+
+The aggregator address is invalid. For example, it might be the special `address(1)` marker used internally or not meet aggregator requirements.
+
+**Possible Solutions:**
+
+1. Use a proper aggregator address that implements the `IAggregator` interface.
+2. Check that you’re not using reserved addresses that are disallowed by the EntryPoint.
diff --git a/fern/wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v07-revert-codes.mdx b/fern/wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v07-revert-codes.mdx
new file mode 100644
index 000000000..19426693c
--- /dev/null
+++ b/fern/wallets/pages/bundler-api/entrypoint-revert-codes/entrypoint-v07-revert-codes.mdx
@@ -0,0 +1,258 @@
+---
+title: EntryPoint v0.7 Revert Codes
+description: Learn about the revert codes returned by the ERC-4337 EntryPoint v0.7
+subtitle: Learn about the revert codes returned by the ERC-4337 EntryPoint v0.7
+url: https://alchemy.com/docs/wallets/reference/entrypoint-v07-revert-codes
+slug: wallets/reference/entrypoint-v07-revert-codes
+---
+
+Bundler JSON-RPC error codes are often accompanied by an additional AAxx code provided by the EntryPoint.
+
+* **AA1x** error codes relate to **creating an account**
+* **AA2x** error codes relate to **the sender or the user operation itself** (nonce, signature, prefund, time validity, verification gas)
+* **AA3x** error codes relate to **paymasters** (deposit, expiration, verification)
+* **AA9x** error codes are **general and not related to a certain theme** (invalid addresses, failed sends, invalid aggregator, etc.)
+
+***
+
+## AA10 sender already constructed
+
+The `sender` (smart account) has already been created. This error may occur if you attempt to create the same account multiple times using `initCode`.
+
+**Possible Solutions:**
+
+1. Remove the `initCode` if you're re-submitting a UserOp for an existing account.
+2. Ensure the `initCode` is only used for initial account creation.
+
+***
+
+## AA13 initCode failed or OOG
+
+The `initCode` ran out of gas (OOG) or reverted during the account creation process. The smart account was not successfully deployed.
+
+**Possible Solutions:**
+
+1. Ensure the account has sufficient native token to pay for deployment if you aren't using a paymaster.
+2. Verify the factory address (the first 20 bytes of the `initCode`) is correct.
+3. Increase the `verificationGasLimit` to ensure the `initCode` can execute fully.
+4. If the `initCode` reverted, investigate the cause (e.g., incorrect factory bytecode, logic errors).
+
+***
+
+## AA14 initCode must return sender
+
+The `initCode` returned a different address than the expected `sender`. The code that handles the creation must return the `sender` address as the newly deployed contract.
+
+**Possible Solutions:**
+
+1. Check your factory or account creation logic to ensure it returns the correct address.
+2. Verify that the deployed account address matches the address specified in the `UserOperation`.
+
+***
+
+## AA15 initCode must create sender
+
+After executing `initCode`, the `sender` address must have code (the account contract must be deployed). If the `sender` remains a non-contract address (EOA-like), this error is raised.
+
+**Possible Solutions:**
+
+1. Ensure that `initCode` actually deploys a contract at the `sender` address.
+2. Verify your factory contract logic to ensure a contract is always deployed.
+
+***
+
+## AA21 didn't pay prefund
+
+The `sender` did not have enough deposit to cover the required prefund for the operation (if no paymaster is used).
+
+**Possible Solutions:**
+
+1. Increase the deposit of the `sender` to cover the gas costs (via `depositTo` on the EntryPoint).
+2. Use a paymaster to cover the operation costs if funds are insufficient.
+
+***
+
+## AA22 expired or not due
+
+The operation is either expired or not yet valid based on its `validUntil` or `validAfter` time fields.
+
+**Possible Solutions:**
+
+1. Check that the current block timestamp is within the valid time range for your UserOp.
+2. Ensure you haven’t missed the operation’s validity window and resubmit within its allowed timeframe.
+
+***
+
+## AA23 reverted
+
+The account’s `validateUserOp` call reverted. This could indicate a logical error within the account validation step.
+
+**Possible Solutions:**
+
+1. Investigate the revert reason returned by the account contract using tools like Tenderly.
+2. Ensure the `validateUserOp` logic in the account contract is correct and doesn’t revert under normal conditions.
+
+***
+
+## AA24 signature error
+
+The `validateUserOp` call or signature verification failed. The signature provided did not pass validation.
+
+**Possible Solutions:**
+
+1. Verify that you’re using the correct private key and signature scheme.
+2. Check that the aggregator (if used) is correct and that the signature is properly formatted.
+
+***
+
+## AA25 invalid account nonce
+
+The nonce provided by the `sender` in the UserOp is invalid, possibly out of sync with the EntryPoint’s stored nonce.
+
+**Possible Solutions:**
+
+1. Fetch the current nonce from the EntryPoint before submitting the UserOp.
+2. Ensure the UserOp uses the correct nonce sequence.
+
+***
+
+## AA26 over verificationGasLimit
+
+The account or paymaster validation exceeded the specified `verificationGasLimit`.
+
+**Possible Solutions:**
+
+1. Increase `verificationGasLimit` in the UserOp to cover the complexity of `validateUserOp`.
+2. Optimize the validation logic to consume less gas.
+
+***
+
+## AA31 paymaster deposit too low
+
+The paymaster did not have enough deposit to cover the prefund of the UserOp.
+
+**Possible Solutions:**
+
+1. Increase the paymaster’s stake and deposit into the EntryPoint.
+2. Ensure the paymaster deposit is sufficient before submitting a UserOp that relies on it.
+
+***
+
+## AA32 paymaster expired or not due
+
+The paymaster validity window has expired or is not yet active.
+
+**Possible Solutions:**
+
+1. Check the paymaster’s time validity parameters.
+2. Submit within the valid timeframe specified by the paymaster’s validation logic.
+
+***
+
+## AA33 reverted
+
+`validatePaymasterUserOp` call reverted. This indicates a logic error in the paymaster’s validation code.
+
+**Possible Solutions:**
+
+1. Investigate the revert reason using debugging tools.
+2. Ensure that the paymaster’s validation logic is correct and doesn’t revert under the given conditions.
+
+***
+
+## AA34 signature error
+
+The paymaster’s signature verification (or other form of authorization) failed.
+
+**Possible Solutions:**
+
+1. Check the paymaster’s signature or authorization mechanism.
+2. Confirm the correct aggregator or verification method is used if applicable.
+
+***
+
+## AA36 over paymasterVerificationGasLimit
+
+The paymaster’s validation logic exceeded its assigned `paymasterVerificationGasLimit`.
+
+**Possible Solutions:**
+
+1. Increase the `paymasterVerificationGasLimit`.
+2. Reduce the complexity of `validatePaymasterUserOp` so it fits within the assigned gas limit.
+
+***
+
+## AA90 invalid beneficiary
+
+The `beneficiary` address (the entity that receives the collected fees) is `address(0)` or invalid.
+
+**Possible Solutions:**
+
+1. Set a valid `beneficiary` address in the `handleOps` call.
+2. Ensure the `beneficiary` parameter is not empty.
+
+***
+
+## AA91 failed send to beneficiary
+
+The EntryPoint failed to transfer the collected fees to the `beneficiary`.
+
+**Possible Solutions:**
+
+1. Ensure the `beneficiary` is able to receive ETH.
+2. Check if the `beneficiary` address is a contract that reverts on ETH reception.
+
+***
+
+## AA92 internal call only
+
+A function intended only for internal calls within the EntryPoint was called externally. This occurs if `innerHandleOp` is invoked by an address other than the EntryPoint itself.
+
+**Possible Solutions:**
+
+1. Do not call `innerHandleOp` directly from outside the EntryPoint.
+2. Ensure you are calling the EntryPoint’s main methods (`handleOps`, etc.) rather than internal helper methods.
+
+***
+
+## AA93 invalid paymasterAndData
+
+The `paymasterAndData` field is invalid, too short, or not formatted correctly.
+
+**Possible Solutions:**
+
+1. Ensure that `paymasterAndData` includes the paymaster address and any additional data as required by the paymaster.
+2. Verify that the `paymasterAndData` length meets the minimum expected size.
+
+***
+
+## AA94 gas values overflow
+
+One or more gas-related fields in the `UserOperation` exceed the maximum allowed value (overflows the 120-bit range the EntryPoint uses internally).
+
+**Possible Solutions:**
+
+1. Ensure that all gas-related parameters (`verificationGasLimit`, `callGasLimit`, `paymasterVerificationGasLimit`, `paymasterPostOpGasLimit`, `preVerificationGas`, `maxFeePerGas`, `maxPriorityFeePerGas`) fit within a 120-bit unsigned integer.
+2. Use smaller values for gas parameters to avoid overflow.
+
+***
+
+## AA95 out of gas
+
+The entire operation (or a sub-call) ran out of gas. This is usually due to too low gas limits passed to `handleOps`.
+
+**Possible Solutions:**
+
+1. Increase the gas limit provided to the bundler or `handleOps` call.
+2. Optimize your code to require less gas.
+
+***
+
+## AA96 invalid aggregator
+
+The aggregator address is invalid. For example, it might be the special `address(1)` marker used internally or not meet aggregator requirements.
+
+**Possible Solutions:**
+
+1. Use a proper aggregator address that implements the `IAggregator` interface.
+2. Check that you’re not using reserved addresses that are disallowed by the EntryPoint.
diff --git a/fern/wallets/pages/concepts/intro-to-account-kit.mdx b/fern/wallets/pages/concepts/intro-to-account-kit.mdx
new file mode 100644
index 000000000..9aca495cd
--- /dev/null
+++ b/fern/wallets/pages/concepts/intro-to-account-kit.mdx
@@ -0,0 +1,102 @@
+---
+title: Intro to Smart Wallets
+description: Introduction to Smart Wallets and Account Abstraction
+slug: wallets/concepts/intro-to-account-kit
+---
+
+Smart Wallets is your all-in-one toolkit for building zero-friction sign-up and transaction flows. But what’s **really** happening under the hood?
+
+Smart Wallets abstracts away the complexity of smart accounts, but as a builder it’s useful to have a foundational understanding of how it works. This way, you can make informed decisions and unlock the full potential of account abstraction.
+
+Unlike other embedded wallet providers that only solve sign-up and key management, Smart Wallets goes further by streamlining the transaction flow with features like gas sponsorship. How? Through **Account Abstraction** and **smart accounts** 🚀 **Let’s break it down.**
+
+## Smart accounts: programmable wallets, not EOAs
+
+
+
+ With Smart Wallets, you’ll deploy a **Smart Contract Account (SCA)** for
+ each user instead of an Externally Owned Account (EOA). This smart account
+ will securely store the user's assets, such as tokens or NFTs.
+
+
+
+
+
+
+
+#### What is a smart account?
+
+Unlike EOAs, SCAs are programmable and can include logic. When we refer to SCAs, or smart accounts, we are typically talking about [ERC-4337](https://www.alchemy.com/overviews/what-is-account-abstraction) smart accounts.
+
+Smart Wallets comes with enterprise-grade, audited smart account implementations. We recommend using [Modular Account v2](/wallets/smart-contracts/modular-account-v2/overview). Learn more and [choose](/wallets/smart-contracts/choosing-a-smart-account) the smart account that best fits your needs or bring your own smart account.
+
+#### Why smart accounts?
+
+Smart accounts unlock powerful Account Abstraction features like gas sponsorship and transaction batching enabling you to create seamless transaction experiences (more on this [later](#gas-manager-sponsor-gas)).
+
+Smart accounts provide flexibility, security, and additional functionality over EOAs. Features like social recovery, two-factor authentication, multi-owner support, and ownership transfer become possible with smart accounts.
+
+In Smart Wallets, this concept will manifest as a [Smart Contract Account](/wallets/resources/types#smartcontractaccount).
+
+## Signer: web2 login and key management
+
+
+
+ A **Signer** is a service (e.g., Turnkey or Magic) or application (e.g., MetaMask) that manages a private key and signs transactions. The signature is only valid if the signer is an owner of the smart account.
+
+
+
+
+
+
+
+Using Smart Wallets, you can secure a user’s Smart Account with email, social login, or passkeys, using our non-custodial Signer infrastructure. Smart accounts support advanced use cases, such as multiple owners and ownership transfer, offering more utility than EOAs.
+
+With this setup, users can sign-up, log in, and sign transactions using familiar web2 user experiences.
+
+In Smart Wallets, this concept will manifest as a [Smart Account Signer](/wallets/signer/what-is-a-signer).
+
+## Bundler: transactions → user operations
+
+
+
+ With Smart Wallets, [sending transactions](/wallets/transactions/send-transactions) is as simple as sending "normal" EOA transactions. However, under the hood, you're actually sending [**User Operations (UOs)**](https://www.alchemy.com/overviews/user-operations) — a standardized pseudo-transaction object — to a **Bundler**.
+
+
+
+
+
+
+
+A User Operation (UO) is an ABI-encoded struct that describes a transaction to be sent on behalf of a user. Since SCAs cannot initiate transactions on chains without native account abstraction support, they send UOs instead. The Bundler will gather UOs from multiple smart accounts (senders) and bundle them into a single on-chain transaction.
+
+A huge benefit of smart accounts is the ability to [batch transactions](/docs/wallets/transactions/send/batch-user-operations) in one UO creating simpler user experiences. For example, you can now approve and swap in one click, rather than signing, waiting, and sending multiple transactions.
+
+In Smart Wallets, this concept will manifest as a [Bundler Client](/wallets/resources/types#bundlerclient), but for simplicity you may only need a [Smart Account Client](/wallets/concepts/smart-account-client).
+
+## Gas Manager: sponsor gas
+
+
+
+ With gas sponsorship, your users won't need to worry about having assets in their accounts to pay for gas. Using [Smart Wallets](/docs/wallets/transactions/sponsor-gas), simply configure a gas manager policy, insert your policy ID into the SDK, and let our Gas Manager handle the rest.
+
+
+
+
+
+
+
+## Conclusion
+
+Smart Wallets makes it easy to build a web2 user experience from sign-up to transaction. It includes everything you need to bring users onchain:
+
+* Non-custodial **Signer** to authenticate with web2 login
+* Secure and flexible **smart accounts** to store assets
+* **Gas Manager** to sponsor gas
+* Reliable and scalable **Bundler** to land user operations
+
+Account Abstraction might seem complex, but Smart Wallets abstracts away the intricacies, allowing you to focus on building great user experiences.
+
+**Now, get started with the [quickstart](/wallets/react/quickstart)!**
+
+Want to learn more? Explore our [Account Abstraction education hub](https://www.alchemy.com/learn/account-abstraction)
diff --git a/fern/wallets/pages/concepts/middleware.mdx b/fern/wallets/pages/concepts/middleware.mdx
new file mode 100644
index 000000000..b1675200c
--- /dev/null
+++ b/fern/wallets/pages/concepts/middleware.mdx
@@ -0,0 +1,31 @@
+---
+title: Middleware
+description: What is Middleware?
+slug: wallets/concepts/middleware
+---
+
+The [Smart Account Client](/wallets/concepts/smart-account-client) is extended with a series of middleware. When building user operations for signing and sending, the flow can be pretty involved.
+Sending a UO requires you to get the latest nonce for an account, check if the account is deployed to set the `initCode` or `factory` and `factoryData` in the user operation, estimate fees, estimate gas, and then sign the user operation.
+If you want to use a paymaster, you need to generate some `dummyPaymasterAndData` to use during gas estimation, and after estimating gas you can request gas sponsorship from your paymaster.
+Middleware allows you to avoid having to write the same flows over and over when all you want to do is call a contract with some data or send some ETH to a destination.
+
+## Middleware order
+
+
+ When using the [React](/wallets/react/quickstart),
+ [Core](/wallets/core/overview), or
+ [Infra](/wallets/reference/account-kit/infra) packages, the Smart Account
+ Clients are already configured to use the appropriate middleware for your
+ needs. If you want to sponsor gas, the Smart Account Clients in these packages
+ abstract the relevant middleware away from you since all you need is a
+ `policyId` to sponsor gas with Alchemy.
+
+
+As mentioned above, the client can be configured with a series of middleware that always run in the same order:
+
+1. `dummyPaymasterAndData` - Generates a dummy paymaster and data for gas estimation if using a paymaster (default: noop)
+2. `feeEstimator` - Estimates the fees for a user operation. If you are using our RPCs, it's important to use the [`alchemyFeeEstimator`](/wallets/reference/account-kit/core/variables/alchemyFeeEstimator) middleware.
+3. `gasEstimator` - Estimates the gas limits for a user operation. The default middleware calls the underlying bundler RPC to `eth_estimateUserOperationGas`.
+4. `customMiddleware` - Allows you define custom middleware to run before requesting sponsorship if there are any additional steps you need to take before requesting sponsorship. (default: noop)
+5. `paymasterAndData` - Requests a gas sponsorship from a paymaster. (default: noop)
+6. `userOperationSimulator` - Simulates a user operation to check if it will be successful. (default: noop)
diff --git a/fern/wallets/pages/concepts/smart-account-client.mdx b/fern/wallets/pages/concepts/smart-account-client.mdx
new file mode 100644
index 000000000..b5a443f5d
--- /dev/null
+++ b/fern/wallets/pages/concepts/smart-account-client.mdx
@@ -0,0 +1,228 @@
+---
+title: Configure client
+description: Configure smart wallet client
+slug: wallets/concepts/smart-account-client
+---
+
+A smart wallet client is the main interface used to interact with smart wallets and take actions like sending transactions, batching transactions, swapping, sponsoring gas, and more.
+
+## How It Works
+
+The [`SmartWalletClient`](https://github.com/alchemyplatform/aa-sdk/blob/b8dad097dc537985334efe0c1cc2af4e79dc0050/account-kit/wallet-client/src/client/index.ts#L34) is a EIP-1193 compatible extension of `viem`'s [Client](https://viem.sh/docs/clients/custom#build-your-own-client) which allows you to interact with smart wallets.
+
+## Prerequisites
+
+* [API key](https://dashboard.alchemy.com/apps)
+* (Optional) [A gas policyID](https://dashboard.alchemy.com/gas-manager/policy/create)
+* [Smart wallets installed and configured in your project](/wallets/react/setup)
+
+## Implementation
+
+
+
+ The [`useSmartAccountClient`](/docs/wallets/reference/account-kit/react/hooks/useSmartAccountClient) hook provides access to your smart account client using the API key and policy ID settings from \[`createConfig`]/docs/wallets/react/quickstart/ui-customization).
+
+ React hooks only support Alchemy Signer, so ensure you [set up your environment](/docs/wallets/react/quickstart/existing-project) and app integration before using the client.
+
+
+ Don't call methods like `client.sendUserOperation()` directly on the client. Instead, use React hooks like [`useSendCalls`](/docs/wallets/reference/account-kit/react/hooks/useSendCalls) which provide better state management and error handling.
+
+
+ ```tsx twoslash
+ import React from "react";
+ import { useSmartAccountClient, useSendCalls } from "@account-kit/react";
+
+ const { client } = useSmartAccountClient({});
+
+ const { sendCalls, isSendingCalls, sendCallsResult } = useSendCalls({
+ client,
+ });
+ ```
+
+
+
+ Use the [`createSmartWalletClient`](/docs/wallets/reference/account-kit/wallet-client/functions/createSmartWalletClient) function to create a smart wallet client.
+
+ 1. Replace the placeholders
+ a. API key from your Alchemy dashboard
+ b. Policy ID from your gas policy
+ c. Signer from [authentication](/docs/wallets/authentication/login-methods/email-otp#step-4-check-authentication-status) or your own signer
+ 2. Create the initial client using [`createSmartWalletClient`](/docs/wallets/reference/account-kit/wallet-client/functions/createSmartWalletClient)
+ 3. Generate your smart account by calling `requestAccount()`
+ 4. Create the final client with your account attached
+
+ ```ts twoslash title="client.ts"
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { alchemy, sepolia } from "@account-kit/infra";
+ import {
+ createSmartWalletClient,
+ type SmartWalletClientParams,
+ } from "@account-kit/wallet-client";
+
+ const clientParams: SmartWalletClientParams = {
+ transport: alchemy({ apiKey: "your-alchemy-api-key"}),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner("0x-your-wallet-private-key"),
+ policyId: "your-policy-id",
+ };
+
+ const clientWithoutAccount = createSmartWalletClient(clientParams);
+
+ const account = await clientWithoutAccount.requestAccount();
+
+ export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+ });
+ ```
+
+
+
+ When using the wallet [API](/docs/wallets/smart-wallet-quickstart/api), you don't need to define a client - send requests directly to the API endpoints.
+
+
+
+## Advanced
+
+
+ By default, the Smart Wallet Client will use [ModularAccountV2](/docs/wallets/smart-contracts/modular-account-v2/overview)(MAv2). This is the cheapest and most advanced Smart Account, but you can specify other smart contract account types as needed. Learn more about the different smart accounts [here](/docs/wallets/smart-contracts/choosing-a-smart-account).
+
+
+ Changing the account type will deploy a different account. If you've already
+ deployed an account for a user and want to change the underlying account type,
+ you'll need to upgrade it. Learn how to upgrade
+ [here](/docs/wallets/smart-contracts/other-accounts/modular-account/upgrading-to-modular-account).
+
+
+ **React and React Native**
+
+ Override the default account type using the `type` parameter:
+
+ * `type` (string) - Defines the smart account type. Options:
+ * `"ModularAccountV2"` (recommended and default)
+ * `"LightAccount"`
+ * `"MultiOwnerLightAccount"`
+
+ ```tsx twoslash
+ import React from "react";
+ import { useSmartAccountClient } from "@account-kit/react";
+
+ const { client } = useSmartAccountClient({
+ type: "LightAccount",
+ });
+ ```
+
+ **Javascript**
+
+ Specify the account type when calling `requestAccount` using the `creationHint` parameter. See all parameter [options](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account#request.body.requestAccountRequest.creationHint).
+
+ ```
+ const account = await clientWithoutAccount.requestAccount(
+ creationHint: {
+ accountType: "sma-b"
+ },
+ );
+
+ export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+ });
+ ```
+
+ **API**
+
+ Pass an [`accountType`](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) to `creationHint` when calling `wallet_requestAccount`.
+
+ **Using 7702**
+
+ To use EIP-7702, please see [this guide](https://www.alchemy.com/docs/wallets/transactions/using-eip-7702). You'll need to set additional parameters on the client.
+
+
+
+ By default, account addresses are deterministically generated from the signer address. To connect to an existing account that doesn't match the deterministic address of your signer, specify the account address when creating the client.
+
+ **React & React Native**
+
+ ```tsx twoslash
+ import React from "react";
+ import { useSmartAccountClient } from "@account-kit/react";
+
+ const { client } = useSmartAccountClient({
+ accountAddress: "0xYOUR_SMART_ACCOUNT_ADDR",
+ });
+ ```
+
+ **Javascript**
+
+ Pass the address to the `createSmartWalletClient` directly rather than calling `requestAccount`.
+
+ ```
+ export const client = createSmartWalletClient({
+ ...clientParams,
+ account: "0xYOUR_SMART_ACCOUNT_ADDR",
+ });
+ ```
+
+ **API**
+
+ Pass the account address directly when preparing calls instead of calling wallet\_requestAccount. See this [guide](/docs/wallets/transactions/send-transactions#API) and skip step 2.
+
+
+
+ Because the Smart Wallet Clients are extensions of viem's clients, they support extensions via the `.extend` method. The base client already includes a [number of actions](https://github.com/alchemyplatform/aa-sdk/tree/v4.x.x/aa-sdk/core/src/actions/smartAccount) by default. You can find additional details about these actions in the [`@aa-sdk/core` documentation](/docs/wallets/reference/aa-sdk/core).
+
+
+
+ Typically, the smart account client uses the default account or the account passed into the client constructor for all of the actions you perform with the client - also known as **account hositing**.
+
+ If you want to manage multiple instances of an account but want to use one client for all of them, you can pass an account to the client on every action.
+
+ ```ts
+ import {
+ createAlchemySmartAccountClient,
+ sepolia,
+ alchemy,
+ } from "@account-kit/infra";
+ import { createLightAccount } from "@account-kit/smart-contracts";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { http } from "viem";
+ import { generatePrivateKey } from "viem/accounts";
+
+ // with account hoisting
+ const transport = alchemy({ apiKey: "your-api-key" });
+ const hoistedClient = createAlchemySmartAccountClient({
+ transport,
+ chain: sepolia,
+ account: await createLightAccount({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ chain: sepolia,
+ transport,
+ }),
+ });
+
+ const signature = await hoistedClient.signMessage({ message: "Hello world! " });
+
+ // without account hoisting
+ const nonHoistedClient = createAlchemySmartAccountClient({
+ transport,
+ chain: sepolia,
+ });
+
+ const lightAccount = await createLightAccount({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ chain: sepolia,
+ transport,
+ });
+
+ const signature2 = await nonHoistedClient.signMessage({
+ message: "Hello world! ",
+ account: lightAccount,
+ });
+ ```
+
+
+## Next Steps
+
+* [Send transactions](/wallets/transactions/send-transactions)
+* [Sponsor gas](/wallets/transactions/sponsor-gas)
+* [Batch transactions](/wallets/transactions/send-batch-transactions)
diff --git a/fern/wallets/pages/core/overview.mdx b/fern/wallets/pages/core/overview.mdx
new file mode 100644
index 000000000..63a190152
--- /dev/null
+++ b/fern/wallets/pages/core/overview.mdx
@@ -0,0 +1,27 @@
+---
+title: Other Javascript Frameworks
+description: How to use Smart Wallets with other Javascript Frameworks
+slug: wallets/core/overview
+---
+
+We don't currently have plans to support other Javascript front-end frameworks such as Vue, Angular, or Svelte. However, there are a number of different ways to integrate Smart Wallets within your application if you're using one of these Javascript frameworks.
+
+
+ If you are using a different Javascript framework and would like to contribute
+ support for it, we welcome PRs!
+
+
+## Using `@account-kit/core`
+
+The [React](/wallets/react/quickstart) package is built on top of the `@account-kit/core` package. This package is the core state management library for all of the utilities found within the React package. All of the guides in this section will focus on
+how you can leverage this package to build your own integration with Smart Wallets, while still leveraging all of the reactive utilities that the React package provides.
+
+## Using the lower level SDKs
+
+`@account-kit/core` is itself a higher level wrapper around the three lower level SDKs:
+
+* [`@account-kit/infra`](/wallets/reference/account-kit/infra) - this package contains all of the Smart Account Client and Gas Manager definitions you need to interact with our Bundler and Paymaster services.
+* [`@account-kit/signer`](/wallets/signer/what-is-a-signer) - this package contains all of the utilities you need to instantiate and interact with our Signer service. This allows you to provision wallets for your users that can be used as owners on Smart Contract Accounts
+* [`@account-kit/smart-contracts`](/wallets/reference/account-kit/smart-contracts) - this package contains all definitions for the Smart Contracts we have built and allows you to provision and deploy smart contracts for your users seamlessly.
+
+Using these packages directly offers you the most control over your stack, but requires significantly more work to get started.
diff --git a/fern/wallets/pages/core/quickstart.mdx b/fern/wallets/pages/core/quickstart.mdx
new file mode 100644
index 000000000..85ec9a660
--- /dev/null
+++ b/fern/wallets/pages/core/quickstart.mdx
@@ -0,0 +1,146 @@
+---
+title: Core Quickstart
+description: Learn how to get started with the Account Kit Core package
+slug: wallets/core/quickstart
+---
+
+If you're not using React, but still want a number of the abstractions that make the React package so easy to use, you can leverage the `@account-kit/core` package directly.
+
+In this guide, we'll walk you through how to use this package to send a user operation, while using the reactive utilities exported by this package.
+
+## Install packages
+
+**Prerequisites**
+
+* minimum Typescript version of 5
+
+**Installation**
+
+To get started, you'll need to install the required packages. We also install the `infra` package because it contains the necessary `Chain` definitions that makes it easier to setup
+your a Bundler client.
+
+
+ ```bash yarn
+ yarn add @account-kit/core @account-kit/infra
+ ```
+
+ ```bash npm
+ npm i -s @account-kit/core @account-kit/infra
+ ```
+
+
+## Get your Environment Variables
+
+1. Create an app in the [dashboard](https://dashboard.alchemy.com/signup) and copy the **API Key**.
+
+2. Create a **`configuration`** in the [`Smart Wallets` dashboard](https://dashboard.alchemy.com/services/smart-wallets/configuration) to enable login methods.
+
+3. Create a policy in your [gas manager dashboard](https://dashboard.alchemy.com/services/gas-manager/configuration) to set rules for sponsorship.
+
+If your setup allows, store both the policy and API key in a `.env` so you can use them in the following steps. Otherwise you can hold onto them for the next section where you will make use of them!
+
+## Create a config
+
+Now, you're ready to create a config. The config we create should be a static object that you can import anywhere into your application. It contains all of the state that the functions within
+this package use.
+
+
+
+## Authenticate the user
+
+Before you can create a Smart Account instance for your users, you need to authenticate them with the user. Depending on what framework you're using this will look different, but using
+email based auth as an example you would:
+
+1. collect the user's email
+2. call the `authenticate` method on the signer
+3. handle the redirect from the user's email and pass the bundle to the signer to complete login
+
+
+ ```ts example.ts
+ import { config } from "./config";
+ import { getSigner } from "@account-kit/core";
+
+ const signer = getSigner(config);
+
+ if (!signer) {
+ // this can happen if your rendering this on the server
+ // the signer instance is only available on the client
+ throw new Error("Signer not found");
+ }
+
+ // authenticate the user with email
+ await signer.authenticate({
+ type: "email",
+ email: "user@email.com",
+ });
+
+ // once the user has clicked on the email and been redirected back to your site
+ const bundle = new URLSearchParams(window.location.search).get("bundle");
+ if (!bundle) {
+ throw new Error("No bundle found in URL");
+ }
+ await signer.authenticate({ type: "email", bundle });
+ ```
+
+
+
+
+## Send a user operation
+
+Now that you have your config, you can send user operations by leveraging the underlying smart account client.
+
+
+ ```ts example.ts
+ import { watchSmartAccountClient } from "@account-kit/core";
+ import { config } from "./config";
+
+ let clientState;
+
+ // The watch smart account client will handle all of the possible state changes
+ // that can impact this client:
+ // - Signer status
+ // - Account instantiation
+ // - Chain changes
+ const clientSubscription = watchSmartAccountClient(
+ {
+ type: "LightAccount",
+ },
+ config,
+ )((clientState_) => {
+ clientState = clientState_;
+ });
+
+ if (clientState == null || clientState.isLoadingClient) {
+ console.log("Loading...");
+ }
+
+ const client = clientState.client;
+
+ await client.sendUserOperation({
+ uo: {
+ target: "0xtarget",
+ data: "0x",
+ value: 0n,
+ },
+ });
+ ```
+
+
+
+
+The key thing here is the `watchSmartAccountClient` method which allows you to subscribe to the state of signer and underlying account to give you a stable instance of the Smart Account Client. How you store the `clientState` variable will depend largely on
+your framework, but the above example should give you a good starting point.
+
+## Next steps
+
+Now that you have basic authentication and user operations working, you can explore additional features:
+
+
+
+ Learn how to configure your app to work with multiple blockchain networks.
+
+
+
+ Learn how to configure Account Kit for server-side rendered applications.
+
+
diff --git a/fern/wallets/pages/features.mdx b/fern/wallets/pages/features.mdx
new file mode 100644
index 000000000..f2b0ba0a9
--- /dev/null
+++ b/fern/wallets/pages/features.mdx
@@ -0,0 +1,38 @@
+---
+title: Features
+description: Smart Wallet features and capabilities.
+slug: wallets/resources/features
+---
+
+# Smart Wallet Features
+
+Leverage the [#1 smart wallet provider](https://www.bundlebear.com/erc4337-factories/all) to deploy and grow your app to millions of users. [Save costs](https://www.alchemy.com/blog/the-most-affordable-smart-wallet) as you scale and never worry about going down with 99.99% uptime.
+
+Don’t see a feature you’re looking for? [Get in touch with us](mailto:smart-wallets@alchemy.com).
+
+| Feature | Description |
+| ------------------------- | --------------------------------------------------------------------------------- |
+| **Social Login** | Google, Apple, Discord, Twitter authentication |
+| **Email/SMS** | Email and phone number based wallet creation |
+| **Gas Sponsorship** | Sponsor gas fees with flexible policies |
+| **Batching transactions** | Group and execute multiple actions in a single onchain transaction |
+| **APIs** | Wallet APIs for server side wallet management and transaction sending. |
+| **Multi-owner accounts** | Allow multiple independent addresses to control and manage the same smart wallet |
+| **Token Balances** | Getting token balances, metadata and more |
+| **Spending Limits** | Daily, weekly, or per-transaction limits |
+| **Session Keys** | Temporary keys for specific operations |
+| **Social Recovery** | Recover wallets through trusted contacts |
+| **EOA connection** | Allow web3 native users to bring their existing EOA without additional connectors |
+| **White-labeled UI** | Customize the wallet interface to match your brand’s look and feel. |
+| **BYO JWT Auth** | Bring your existing authentication system to secure wallets (JWT + OIDC) |
+| **EIP-7702 Support** | Upgrade EOA to smart wallets |
+| **Multi-factor Auth** | Adds an extra layer of security by requiring multiple forms of verification |
+| **Passkeys** | Onchain and offchain passkey authentication |
+| **Prices API** | Fetch current and historical token prices |
+| **Webhooks** | Onchain event notifications |
+| **Modular Plugins** | ERC-6900 modular wallets for advanced functionality. |
+| **Alchemy Bridge** | A secure and efficient cross-chain bridge |
+| **Multi-signature** | Multiple signature requirements for transactions |
+| **Ethereum** | Ethereum mainnet and testnets |
+| **EVM L2s** | All Ethereum compatible mainnet and testnets |
+| **Solana** | Solana mainnet and testnet |
diff --git a/fern/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx b/fern/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx
new file mode 100644
index 000000000..1fbdda9bb
--- /dev/null
+++ b/fern/wallets/pages/gas-manager-admin-api/gas-coverage-api-endpoints/paymaster-api-endpoints.mdx
@@ -0,0 +1,21 @@
+---
+title: Gas Coverage API Endpoints
+description: The Gas Coverage API Endpoints allows you to sponsor gas fees for your users, removing the biggest barrier to entry.
+subtitle: The Gas Coverage API Endpoints allows you to sponsor gas fees for your users, removing the biggest barrier to entry.
+url: https://alchemy.com/docs/reference/paymaster-api-endpoints
+slug: reference/paymaster-api-endpoints
+---
+
+## What is the Gas Coverage API?
+
+The Gas Coverage API is a set of methods that allow you to sponsor gas for your users' transactions, abstracting away the concept of "gas" from them.
+
+## Methods
+
+| Method | Description |
+| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [alchemy_requestGasAndPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) | **\[Start here / Recommended]** Requests gas sponsorship and gas estimates for a `UserOperation` on EVM networks. If approved, returns: 1. `paymaster` and `paymasterData` 2. gas and fee estimates. |
+| [alchemy_requestPaymasterAndData](/reference/alchemy-requestpaymasteranddata) | **\[To be deprecated]** Requests gas sponsorship for a `UserOperation` on EVM networks. If approved, returns`paymaster` and `paymasterData`. |
+| [pm_getPaymasterStubData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-stub-data) | Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation on EVM networks. |
+| [pm_getPaymasterData](/reference/pm_getpaymasterdata) | Returns values to be used in paymaster-related fields of a signed user operation on EVM networks. These are not stub values and will be included in a signed user operation as part of an `eth_sendUserOperation` call. |
+| [alchemy_requestFeePayer](/reference/alchemy-requestfeepayer) | Request gas sponsorship for a `transaction` on Solana. Returns the `serializedTransaction`, including the `feePayer` signature. |
diff --git a/fern/wallets/pages/gas-manager-admin-api/gas-manager-admin-api-endpoints/gas-manager-admin-api-endpoints.mdx b/fern/wallets/pages/gas-manager-admin-api/gas-manager-admin-api-endpoints/gas-manager-admin-api-endpoints.mdx
new file mode 100644
index 000000000..716c76d58
--- /dev/null
+++ b/fern/wallets/pages/gas-manager-admin-api/gas-manager-admin-api-endpoints/gas-manager-admin-api-endpoints.mdx
@@ -0,0 +1,32 @@
+---
+title: Gas Manager Admin API Endpoints
+description: The Gas Manager Admin API endpoints allows you to programmatically create, update and manage gas policies.
+subtitle: The Gas Manager Admin API endpoints allows you to programmatically create, update and manage gas policies.
+url: https://alchemy.com/docs/reference/gas-manager-admin-api-endpoints
+slug: reference/gas-manager-admin-api-endpoints
+---
+
+## What is the Gas Manager Admin API?
+
+The Gas Manager Admin API is a REST API with endpoints that allow you to programmatically create, update and manage gas policies. Programmatically managing gas policies is for "power" users with complex use cases who need to manage this process dynamically through code instead of incrementally in the Alchemy Dashboard.
+
+## How to Use the Gas Manager Admin API to Create a Policy
+
+To use Gas Manager Admin API, follow these steps:
+
+1. **Create an Alchemy app**: to create gas policies, you first need to create an Alchemy app [through your Alchemy Dashboard](https://dashboard.alchemy.com/).
+2. **Create access keys for your account**: To call Gas Manager Admin APIs, an authorization header is required in all the requests. This `auth` header should be your access key. Follow this [guide for creating access keys](/docs/how-to-create-access-keys).
+3. **Create the policy**: Next, make a request to the [Create Policy](/reference/create-policy) endpoint with your policy rules.
+
+## Methods
+
+| Method | Description |
+| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Create Policy](/reference/create-policy) | Creates a new gas manager policy with the specified rules. Returns the policy id of a successfully created policy. |
+| [Get Policy](/reference/get-policy) | Returns policy information by id. |
+| [Delete Policy](/reference/delete-policy) | Deletes a policy by id. |
+| [Replace Policy](/reference/replace-policy) | Accepts a rules object and replaces all rules in an existing policy by id. |
+| [Get All Policies](/reference/get-all-policies) | Accepts an app id and returns all policies under that app. |
+| [Update Policy Status](/reference/update-policy-status) | Modifies the status of a policy to either "active" or "inactive". |
+| [Get Policy Stats](/reference/get-policy-stats) | Returns stats about a policy specified by ID, including signatures mined, signatures expired, signatures pending, USD mined, and USD pending. |
+| [Get Sponsorships](/reference/get-sponsorships) | Returns a list of sponsorships associated with the specified policy ID. The results are paginated. |
diff --git a/fern/wallets/pages/gas-manager-admin-api/gas-manager-errors.mdx b/fern/wallets/pages/gas-manager-admin-api/gas-manager-errors.mdx
new file mode 100644
index 000000000..86e74f2f8
--- /dev/null
+++ b/fern/wallets/pages/gas-manager-admin-api/gas-manager-errors.mdx
@@ -0,0 +1,54 @@
+---
+title: Gas Manager Errors
+description: Learn about the most common Gas Manager errors.
+subtitle: Learn about the most common Gas Manager errors.
+url: https://alchemy.com/docs/wallets/reference/gas-manager-errors
+slug: wallets/reference/gas-manager-errors
+---
+
+### Invalid Policy ID. Please ensure you're sending requests with the api key associated with the policy's app, and that the policy is active.
+
+Gas manager policies can only be tied to one app. To resolve this issue ensure:
+
+* you are using the correct policy ID and the API key associated with the app for which the Gas Manager policy is configured
+* the policy is active.
+
+### This userOp's cost will put the team over their MAX token limit for this network.
+
+This userOp will cause you to exceed your sponsorship limit. Increase your limit by buying gas credits in USD through the [Gas Manager Dashboard](https://dashboard.alchemy.com/gas-manager).
+
+### Policy's max spend per spender exceeded.
+
+The total amount sponsored for this sender has reached the limit set in the policy. Review your policy and adjust the max spend per address.
+
+### Policy's max count per spender exceeded.
+
+The number of userOps sponsored for this sender has reached the limit set in the policy. Review your policy and adjust the max operations per address.
+
+### Policy max count exceeded.
+
+The number of userOps sponsored has reached the limit set in the policy. Review your policy and adjust the max operations per policy.
+
+### Policy max spend global exceeded.
+
+You’ve reached the maximum dollar amount for this policy. Review your policy and adjust the max spend per policy.
+
+### User operation cost exceeds specified spend limit
+
+The gas required for this userOp exceeds the max dollar amount you are willing to sponsor per userOp, as specified in the Gas Manager policy. Adjust the max spend per UO in your policy.
+
+### sender isn't in policy's allowlist
+
+If the allowlist is not empty, only sender addresses included in the allowlist can be sponsored.
+
+### sender address in policy's blocklist
+
+Any address included in the blocklist is banned from receiving sponsorship.
+
+### policy hasn't started
+
+The policy is not active because it has not started. Check the policy's start date.
+
+### policy has ended
+
+The policy is not active because it has ended. Check the policy's end date.
diff --git a/fern/wallets/pages/gas-manager-admin-api/gas-manager-faqs.mdx b/fern/wallets/pages/gas-manager-admin-api/gas-manager-faqs.mdx
new file mode 100644
index 000000000..028f8dc2e
--- /dev/null
+++ b/fern/wallets/pages/gas-manager-admin-api/gas-manager-faqs.mdx
@@ -0,0 +1,185 @@
+---
+title: FAQs
+description: Frequently asked questions about Gas Manager
+subtitle: Frequently asked questions about Gas Manager
+url: https://alchemy.com/docs/wallets/reference/gas-manager-faqs
+slug: wallets/reference/gas-manager-faqs
+---
+
+## Usage Limits
+
+### How much gas can I sponsor?
+
+You can sponsor as much gas as you want. We front the gas for you up to a base limit based on your tier, and add it to your monthly bill. Note that both gas sponsorship and ERC-20 gas payments contribute towards your limit.
+If you need more gas you can easily boost your limits by buying gas manager credits in USD via our [Gas Manager Dashboard](https://dashboard.alchemy.com/gas-manager). This is a one time purchase that will be applied to your bill at the end of the month and will roll over until it is used up.
+
+| Tier | Mainnet Base Limit | Testnet Limit | Team Policy Creation Limit |
+| ---------- | ------------------ | -------------------------- | -------------------------- |
+| Free | N/A | unlimited free sponsorship | 10 Policies |
+| PAYG | $100 / Month | unlimited free sponsorship | 10 Policies |
+| Enterprise | Custom | unlimited free sponsorship | Custom |
+
+### Will I get alerted if I get close to my limit?
+
+Yes! To safeguard against hitting sponsorship limits, we've implemented alerts that notify the team's billing admins via email when gas manager usage reaches 50%, 75%, 90%, and 100% of the sponsorship limit.
+
+### Can the sponsored amount exceed my limits?
+
+Our Gas Manager is designed to handle a large number of sponsorship requests quickly and efficiently. However, because of the way it processes these requests in batches, there may be brief periods where the actual spending slightly exceeds the limits you've set. This is rare but possible, especially when the system is handling a high volume of requests. Rest assured, we're actively working to minimize these occurrences and if you have concerns, please file a support ticket via our [Discord server](https://discord.com/channels/735965332958871634/1115787488838033538).
+
+## Fee Logic
+
+To provide its services, Alchemy's Gas Abstraction API charges fees on gas sponsorships and ERC-20 gas payments.
+
+| Tier | Mainnet Fee | Testnet Fee |
+| ---------- | ---------------------- | -------------------------- |
+| Free | N/A | unlimited free sponsorship |
+| PAYG | 8% of Gas Fees Covered | unlimited free sponsorship |
+| Enterprise | Custom | unlimited free sponsorship |
+
+## Gas payments with any token
+
+### Which ERC-20 tokens are supported by the Gas Manager?
+
+The ERC-20 Gas Manager supports any token supported by our [Token Prices By Address API](https://alchemy.com/docs/reference/get-token-prices-by-address).
+
+### Which ERC20 tokens can I enable via the Gas Manager dashboard?
+
+Below is a list of the tokens you can currently enable in your policy via our Gas Manager dashboard:
+
+
+ **Want to support custom tokens?** You can either contact us at
+ wallets@alchemy.com or use our [Admin
+ APIs](https://www.alchemy.com/docs/wallets/api/gas-manager-admin-api/admin-api-endpoints/create-policy)
+ to enable more ERC-20 tokens.
+
+
+* ETH\_MAINNET
+
+ * USDC: [**`0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48`**](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48)
+ * USDT: `0xdac17f958d2ee523a2206206994597c13d831ec7`
+ * WLD: `0x163f8c2467924be0ae7b5347228cabf260318753`
+ * wBTC: `0x2260fac5e5542a773aa44fbcfedf7c193bc2c599`
+ * wETH: `0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc`
+ * DAI: `0x6b175474e89094c44da98b954eedeac495271d0f`
+ * USDe: `0x4c9edd5852cd905f086c759e8383e09bff1e68b3`
+ * SUKU: `0x0763fdCCF1aE541A5961815C0872A8c5Bc6DE4d7`
+
+* ETH\_SEPOLIA
+
+ * USDC: [**`0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238`**](https://sepolia.etherscan.io/address/0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238)
+
+* BASE\_MAINNET
+
+ * USDC: [**`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`**](https://basescan.org/token/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913)
+ * USDT: `0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2`
+ * wETH: `0x4200000000000000000000000000000000000006`
+
+* BASE\_SEPOLIA
+
+ * USDC: [**`0x036CbD53842c5426634e7929541eC2318f3dCF7e`**](https://base-sepolia.blockscout.com/address/0x036CbD53842c5426634e7929541eC2318f3dCF7e)
+
+* ARB\_MAINNET
+
+ * USDC: [**`0xaf88d065e77c8cC2239327C5EDb3A432268e5831`**](https://arbiscan.io/token/0xaf88d065e77c8cC2239327C5EDb3A432268e5831)
+ * USDT: `0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9`
+ * wETH: `0x82af49447d8a07e3bd95bd0d56f35241523fbab1`
+ * DAI: `0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1`
+ * USDe: `0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34`
+
+* ARBNOVA\_MAINNET
+
+ * USDC: [`0x750ba8b76187092b0d1e87e28daaf484d1b5273b`](https://nova.arbiscan.io/token/0x750ba8b76187092b0d1e87e28daaf484d1b5273b)
+ * wETH: `0x722e8bdd2ce80a4422e880164f2079488e115365`
+
+* ARB\_SEPOLIA
+
+ * USDC: [**`0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d`**](https://sepolia.arbiscan.io/address/0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d)
+
+* OPT\_MAINNET
+
+ * USDC: [**`0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85`**](https://optimistic.etherscan.io/token/0x0b2c639c533813f4aa9d7837caf62653d097ff85)
+ * USDT: `0x94b008aa00579c1307b0ef2c499ad98a8ce58e58`
+ * WLD: `0xdC6fF44d5d932Cbd77B52E5612Ba0529DC6226F1`
+ * wBTC: `0x68f180fcce6836688e9084f035309e29bf0a2095`
+ * wETH: `0x4200000000000000000000000000000000000006`
+ * DAI: `0xda10009cbd5d07dd0cecc66161fc93d7c9000da1`
+ * USDe: `0x5d3a1ff2b6bab83b63cd9ad0787074081a52ef34`
+
+* OPT\_SEPOLIA
+
+ * USDC: [**`0x5fd84259d66Cd46123540766Be93DFE6D43130D7`**](https://sepolia-optimism.etherscan.io/address/0x5fd84259d66Cd46123540766Be93DFE6D43130D7)
+
+* MATIC\_MAINNET (Polygon mainnet)
+
+ * USDC: `0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359`
+ * USDT: `0xc2132d05d31c914a87c6611c10748aeb04b58e8f`
+ * wBTC: `0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6`
+ * wETH: `0x7ceb23fd6bc0add59e62ac25578270cff1b9f619`
+ * DAI: `0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063`
+ * SUKU: `0x60Ea918FC64360269Da4efBDA11d8fC6514617C6`
+
+* MATIC\_AMOY (Polygon amoy)
+
+ * USDC: [**`0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582`**](https://www.oklink.com/amoy/address/0x41e94eb019c0762f9bfcf9fb1e58725bfb0e7582)
+
+* WORLDCHAIN\_MAINNET
+
+ * USDC: [`0x79A02482A880bCE3F13e09Da970dC34db4CD24d1`](https://worldscan.org/address/0x79A02482A880bCE3F13e09Da970dC34db4CD24d1)
+ * WLD: [`0x2cfc85d8e48f8eab294be644d9e25c3030863003`](https://worldscan.org/address/0x2cFc85d8E48F8EAB294be644d9E25C3030863003)
+ * wBTC: [`0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3`](https://worldscan.org/address/0x03c7054bcb39f7b2e5b2c7acb37583e32d70cfa3)
+ * wETH: [`0x4200000000000000000000000000000000000006`](https://worldscan.org/address/0x4200000000000000000000000000000000000006)
+
+* MONAD\_TESTNET
+ * USDC: `0xf817257fed379853cDe0fa4F97AB987181B1E5Ea`
+ * USDT: `0x88b8E2161DEDC77EF4ab7585569D2415a1C1055D`
+ * wBTC: [`0xcf5a6076cfa32686c0Df13aBaDa2b40dec133F1d`](https://testnet.monadexplorer.com/address/0xcf5a6076cfa32686c0Df13aBaDa2b40dec133F1d)
+ * wETH: [`0xB5a30b0FDc5EA94A52fDc42e3E9760Cb8449Fb37`](https://testnet.monadexplorer.com/address/0xB5a30b0FDc5EA94A52fDc42e3E9760Cb8449Fb37)
+
+### How is the exchange rate for an ERC-20 token calculated?
+
+We use our [Token Prices By Address API](https://alchemy.com/docs/reference/get-token-prices-by-address) to determine the exchange rate for ERC-20 tokens. The exchange rate is locked in **at the moment the paymaster signs the userOp**, not when the userOp is mined on-chain.
+
+On testnets, since token prices are effectively $0, we use a reference token for pricing:
+
+* **Native gas token**: We use the corresponding price of the token on mainnet.
+* **ERC-20 token supported on the dashboard**: We use the token’s mainnet price.
+* **Custom ERC-20 token:** If you're enabling a custom token, you can choose which token to use as the pricing reference. Make sure to pick a price reference token that has the same decimals as the custom token.
+
+### What are the different ERC-20 transfer modes supported by the Paymaster?
+
+To allow the paymaster contract to transfer ERC-20 tokens from the user’s wallet, the user must first approve (`approve()`) the paymaster to spend tokens on their behalf. This typically requires a separate transaction, either paid for by the user with the native gas token or sponsored by the app builder.
+
+To eliminate the need for a separate approval transaction and improve UX, the Paymaster supports two ERC-20 transfer modes, configurable at the policy level:
+
+1. \[Recommended] Transfer ERC-20 tokens *after* userOp execution:
+ 1. We front the gas using the native gas token and transfer ERC-20 tokens from the user’s wallet to your receiving address after the userOp execution.
+ 2. No upfront allowance is required. You can batch the approval with the userOp, enabling the user to allow the paymaster to spend the ERC-20 token on their behalf (`approve()`) without a separate tx — greatly improving UX.
+ 3. If the post-execution ERC-20 transfer fails, the userOp will revert, but you’ll remain liable for the gas costs.
+ 4. You can run off-chain simulation to verify the approval is likely to succeed, but this does not guarantee on-chain success.
+2. Transfer ERC-20 tokens before userOp execution:
+ 1. We front the gas using the native gas token and execute the ERC-20 token transfer during `validatePaymasterUserOp`, before the userOp execution.
+ 2. This requires you (the developer) to ensure the paymaster already has sufficient allowance—either through a prior `approve()` transaction or a permit signature—*before* the UserOp is submitted. This approval must either be paid for by the user with the native gas token or sponsored by you.
+ 3. If the required allowance isn't in place when the userOp is submitted, the userOp will be rejected.
+ 4. This flow adds friction to the UX and is generally more gas intensive than the post-execution mode.
+
+### Where can I see userOps that used ERC-20 tokens for gas?
+
+You can view past userOps by selecting your policy in the **Gas Manager dashboard.** For every userOp, you will see:
+
+* userOpHash
+* Timestamp
+* ERC-20 token used
+* Amount of ERC-20 tokens spent and its USD equivalent
+* User address
+* Network
+
+You can also use our [Get Sponsorships API](https://www.alchemy.com/docs/node/gas-manager-admin-api/gas-manager-admin-api-endpoints/gas-manager-admin-api-endpoints/get-sponsorships) to fetch the full history programmatically.
+
+## What are the deployment addresses of the Gas Manager (Paymaster)?
+
+You can find the deployment addresses [here](https://github.com/Jam516/BundleBear/blob/main/models/erc4337/labels/erc4337_labels_paymasters.sql).
+
+## What is the encoding of `paymasterAndData`?
+
+`paymasterAndData` follows the format enforced by ERC-4337, which consists of a 20-byte address followed by an arbitrary-length data segment. For Alchemy’s paymaster contract, the data segment is formatted as a 32-byte packed time range validity followed by a 65-byte long signature.
diff --git a/fern/wallets/pages/guide-template.mdx b/fern/wallets/pages/guide-template.mdx
new file mode 100644
index 000000000..febb12936
--- /dev/null
+++ b/fern/wallets/pages/guide-template.mdx
@@ -0,0 +1,77 @@
+---
+title: title [Action-oriented, outcome-focused title]
+description: Short description of the guide
+slug: wallets/...
+---
+
+\[1-2 sentences describing the developer outcome]
+
+* This should answer what this feature/guide is and when to use it
+* Remember to focus on developer outcomes, not implementation details
+* If more in-depth explanation is needed - that info should be in an explainer doc and linked to or advanced section
+
+## How It Works
+
+1-2 paragraphs describing the how it works
+
+\[Any technical deep details that are **required** to successfully test] - this should NOT be explainers or deep dives that expose complexity. Only required info
+
+\[Call out any defaults we are using or assumptions we are making - point to advanced section for followups on these points]
+
+## Prerequisites
+
+List any setup or knowledge required:
+
+* Alchemy project + dashboard access
+* Relevant SDK or API setup
+* Auth keys or config initialized
+* Assumptions or constraints on framework or version
+
+Example:
+
+* API key from your [dashboard](https://dashboard.alchemy.com/apps)
+* [Smart Wallets installed and configured in your project](/docs/wallets/pages/react/setup.mdx).
+* [A "pay gas with any token" policy](https://dashboard.alchemy.com/gas-manager/policy/create).
+
+## Implementation
+
+
+
+ Required SDK version: ^v4.59.1
+
+
+
+
+
+ Required SDK version: ^v4.59.1
+
+
+
+
+
+ See the [`wallet_prepareCalls` API
+ reference](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+ for full descriptions of the parameters used in the following examples.
+
+
+
+
+
+## Advanced
+
+
+ \[Details about advanced options / implementations / explainers]
+
+
+
+ \[Details about advanced options / implementations / explainers]
+
+
+## Next Steps
+
+* Build more: \[Next logical How-to Guide or step]
+ * e.g. now that you’ve logged users in with email, send a transaction.
+* Learn more: \[Recipe that includes this step]
+ * Reference: \[API Reference]
+* Explanations\*\*:\*\* \[Conceptual background or technical deep dive]
+* \[Link to related troubleshooting]
diff --git a/fern/wallets/pages/index.mdx b/fern/wallets/pages/index.mdx
new file mode 100644
index 000000000..18ca30c98
--- /dev/null
+++ b/fern/wallets/pages/index.mdx
@@ -0,0 +1,166 @@
+---
+title: Smart Wallets
+description: Build zero-friction user onboarding and transactions end-to-end with one SDK.
+slug: wallets
+layout: overview
+hide-toc: true
+---
+
+
+
+
+
+
+
+ Build an onchain app from scratch with wallets and transactions.
+
+
+ See Smart Wallets in action with an interactive demo.
+
+
+
+## Everything You Need for Onchain Applications
+
+
+
+ Email, social, biometric, or EOA login.
+
+
+ Remove gas fees for users.
+
+
+ Multiple transactions in 1 click on EVM & Solana.
+
+
+ Pre-built UI components or fully whitelabel.
+
+
+ Advanced permissions and automations.
+
+
+
+## Frameworks
+
+
+
+ Pre-built React components and hooks.
+
+
+ Native mobile wallet experiences.
+
+
+ Framework-agnostic implementation.
+
+
+ Server-side wallet management.
+
+
+
+## Common Starting Places
+
+
+
+ Build an onchain app from scratch with wallets and transactions.
+
+
+ Upgrade to smart wallets using EIP-7702 or direct wagmi integration.
+
+
+ Add wallet and transaction functionality to existing web2 applications.
+
+
+ Server-side applications with signing and sending on your backend.
+
+
+
+## Resources
+
+
+
+ Troubleshoot issues or get in touch.
+
+
+ End-to-end guides for common features.
+
+
+ Save costs as you scale.
+
+
diff --git a/fern/wallets/pages/infra/quickstart.mdx b/fern/wallets/pages/infra/quickstart.mdx
new file mode 100644
index 000000000..65572fb8d
--- /dev/null
+++ b/fern/wallets/pages/infra/quickstart.mdx
@@ -0,0 +1,69 @@
+---
+title: Quickstart
+description: Get started with Alchemy's ERC-4337 infrastructure
+slug: wallets/infra/quickstart
+---
+
+Smart Wallets is composed of some lower-level libraries if you're looking for further customization of your stack or want more control over how you build your application.
+One of these libraries is `@account-kit/infra` which allows you to interact with our infrastructure directly, while bringing your own smart contracts or signer.
+In this guide, we'll cover how to get started with `@account-kit/infra` and send a user operation. For smart contracts, we'll leverage `@account-kit/smart-contracts` to use `LightAccount`,
+but you're free to use any smart contract you'd like.
+
+## Installation
+
+**Prerequisites**
+
+* minimum Typescript version of 5
+
+**Installation**
+
+
+ ```bash yarn
+ yarn add @account-kit/infra @account-kit/smart-contracts
+ ```
+
+ ```bash npm
+ npm i -s @account-kit/infra @account-kit/smart-contracts
+ ```
+
+
+## Get your Alchemy API Key
+
+
+
+## Create a Gas Manager policy
+
+If you want to sponsor gas, then you'll also want to create a gas policy in the Gas Manager dashboard.
+
+
+
+## Create a client
+
+Now that you have an API key and a Policy ID, you can create a [Smart Account Client](/wallets/concepts/smart-account-client) to interact with the infrastructure.
+
+
+ Replace `YOUR_API_KEY` and `YOUR_POLICY_ID` with the key and Policy ID created
+ above.
+
+
+
+
+## Send a user operation
+
+The last step is to send a user operation using the client you just created.
+
+
+ ```ts example.ts
+ import { client } from "./client";
+
+ const { hash } = await client.sendUserOperation({
+ uo: {
+ target: "0xTARGET_ADDRESS",
+ data: "0x",
+ value: 0n,
+ },
+ });
+ ```
+
+
+
diff --git a/fern/wallets/pages/low-level-infra/bundler/api-endpoints.mdx b/fern/wallets/pages/low-level-infra/bundler/api-endpoints.mdx
new file mode 100644
index 000000000..a6b1bae90
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/bundler/api-endpoints.mdx
@@ -0,0 +1,7 @@
+---
+title: Bundler API Endpoints
+description: The Bundler API Endpoints allow you to interact with the lowest level of the account abstraction stack, giving users full control over their User Operations.
+subtitle: The Bundler API Endpoints allow you to interact with the lowest level of the account abstraction stack.
+---
+
+
diff --git a/fern/wallets/pages/low-level-infra/bundler/overview.mdx b/fern/wallets/pages/low-level-infra/bundler/overview.mdx
new file mode 100644
index 000000000..90fe49e3f
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/bundler/overview.mdx
@@ -0,0 +1,12 @@
+---
+title: Bundler Overview
+description: Raw EIP-4337 Bundler APIs for advanced developers
+slug: wallets/transactions/low-level-infra/bundler/overview
+---
+
+The Bundler is a key component in the ERC-4337 Account Abstraction stack, responsible for collecting, validating, and bundling UserOperations (UserOps) into transactions that are submitted to the EntryPoint contract on the blockchain. In Alchemy's Account Kit, the Bundler APIs provide direct access to our high-performance, scalable bundler infrastructure (powered by Rundler), enabling developers to interact with a production-grade ERC-4337 bundler for reliable on-chain submission of UserOps.
+These APIs are part of the `@account-kit/infra` library, allowing advanced customization when building smart wallet applications. They follow the standard ERC-4337 JSON-RPC endpoints and support versions v0.6 and v0.7 of the protocol, with optimizations for gas estimation, validation, and error handling to ensure UserOps land efficiently while protecting against attacks.
+
+For full control, bring your own smart contracts or signers while leveraging Alchemy's bundler for scalability. If you prefer a higher-level abstraction, use the Wallet APIs or aa-sdk instead.
+
+
diff --git a/fern/wallets/pages/low-level-infra/bundler/sdk.mdx b/fern/wallets/pages/low-level-infra/bundler/sdk.mdx
new file mode 100644
index 000000000..63f5c7262
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/bundler/sdk.mdx
@@ -0,0 +1,93 @@
+**Required SDK version**: ^v4.59.1
+
+## Using the `@account-kit/infra` Library for UserOperation Management
+
+The `@account-kit/infra` library enables direct interaction with Alchemy's ERC-4337 bundler for advanced UserOperation management. The `alchemyFeeEstimator` function leverages underlying APIs, including `rundler_maxPriorityFeePerGas` and `eth_estimateUserOperation`, to estimate gas fees for UserOperations. Below are examples demonstrating how to estimate and send a UserOperation and how to retrieve a UserOperation by its hash using low-level Bundler APIs.
+
+
+
+```ts title="config.ts"
+import { alchemy, sepolia } from "@account-kit/infra";
+
+const YOUR_API_KEY = "";
+export const YOUR_PRIVATE_KEY = "";
+export const chain = sepolia;
+
+export const transport = alchemy({
+ apiKey: YOUR_API_KEY,
+});
+```
+
+```ts title="estimateAndSendUserOperation.ts"
+import { alchemyFeeEstimator } from "@account-kit/infra";
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { entryPoint07Address } from "viem/account-abstraction";
+import {
+ chain,
+ transport
+ YOUR_PRIVATE_KEY,
+} from "./config.ts";
+
+export async function estimateAndSendUserOperation() {
+ const client = await createModularAccountV2Client({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(YOUR_PRIVATE_KEY),
+ chain,
+ transport,
+ feeEstimator: alchemyFeeEstimator(transport),
+ });
+
+ try {
+ let uo = await client.buildUserOperation({
+ uo: {
+ data: "0x",
+ target: "0x0000000000000000000000000000000000000000",
+ },
+ });
+ const uoWithSig = await client.signUserOperation({ uoStruct: uo });
+ const sendResult = await client.sendRawUserOperation(
+ uoWithSig,
+ entryPoint07Address,
+ );
+ await client.waitForUserOperationTransaction({
+ hash: sendResult,
+ retries: {
+ intervalMs: 100,
+ maxRetries: 600,
+ multiplier: 0,
+ },
+ });
+ const receipt = await client.getUserOperationReceipt(sendResult);
+ console.log("UserOperation receipt:", receipt);
+ } catch (error) {
+ console.error("Error processing UserOperation:", error);
+ }
+}
+```
+
+```ts title="getUserOperationByHash.ts"
+import { createAlchemyPublicRpcClient } from "@account-kit/infra";
+import { chain, transport } from "./config.ts";
+import type { Hash } from "viem";
+
+export async function getUserOperationByHash(uoHash: Hash) {
+ const client = createAlchemyPublicRpcClient({
+ chain,
+ transport,
+ });
+ try {
+ let userOp = await client.getUserOperationByHash(uoHash);
+ console.log("User Operation: ", userOp);
+ } catch (error) {
+ console.error("Error processing UserOperation:", error);
+ }
+}
+```
+
+
+
+
+ Make sure that you environment is set with your correct `ALCHEMY_API_KEY` and
+ `PRIVATE_KEY`. These examples assume familiarity with ERC-4337 and proper
+ configuration of the EntryPoint contract (`entryPoint07Address`).
+
diff --git a/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/api-endpoints.mdx b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/api-endpoints.mdx
new file mode 100644
index 000000000..26d2e0e34
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/api-endpoints.mdx
@@ -0,0 +1,8 @@
+---
+title: Gas Sponsorship API Endpoints
+description: The Gas Sponsorship API Endpoints allows you to sponsor gas fees for your users, removing the biggest barrier to entry.
+subtitle: The Gas Coverage API Endpoints allows you to sponsor gas fees for your users, removing the biggest barrier to entry.
+slug: wallets/low-level-infra/gas-manager/gas-sponsorship/api-endpoints
+---
+
+
diff --git a/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/basic-gas-sponsorship.mdx b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/basic-gas-sponsorship.mdx
new file mode 100644
index 000000000..6552e519d
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/basic-gas-sponsorship.mdx
@@ -0,0 +1,73 @@
+---
+title: Basic Gas Sponsorship
+description: The Gas Manager allows you to sponsor gas fees for your users on EVM networks, removing the biggest barrier to entry.
+subtitle: The Gas Manager allows you to sponsor gas fees for your users on EVM networks, removing the biggest barrier to entry.
+url: https://alchemy.com/docs/reference/how-to-sponsor-gas-on-evm
+slug: wallets/low-level-infra/gas-manager/gas-sponsorship/using-sdk/basic-gas-sponsorship
+---
+
+Gas fees are a significant barrier to entry for new users of your app. With the Gas Manager, you can remove this barrier by sponsoring gas fees for your users' transactions, allowing users to transact without holding the native gas token of the chain.
+
+We make it easy for you to sponsor gas for any transaction: you don’t need to hold any tokens, we front the gas for you and add it to your monthly bill.
+
+
+ **\[Recommended]** Use our [SDK](https://www.alchemy.com/docs/wallets) to
+ create and use wallets. The SDK handles all complexity for you, making
+ development faster and easier.
+
+
+If you want to use APIs directly, follow these steps.
+
+### 1. Create a Gas Manager Policy
+
+A gas manager policy defines which transactions (userOps) are eligible for gas sponsorship. You can customize the policy with the following rules:
+
+* **Spending rules**: limit the amount of money or the number of userOps that can be sponsored by this policy
+
+* **Custom rules**: sponsor gas only for certain actions (ex: swaps) or certain users (ex: power users of your app) by making a request to your server to verify sponsorship eligibility. To enable, add the following details on your policy:
+
+ * **URL**: the URL the Gas Manager will make a POST request to every time you request gas sponsorship
+
+ * **Sponsor on error or timeout**: when selected, the userOp will be sponsored in the event of an error or timeout.
+
+ * **Request Payload**
+
+ * `userOperation`: The userOp object.
+ * `policyId`: The ID of your policy.
+ * `chainId`: The ID of the network where the operation is being performed.
+ * `webhookData`: Additional data you wish to include in the request, such as proof of humanity.
+
+ Here is an example request body:
+
+ ```json
+ {
+ "userOperation": {}, // structure depends on EntryPoint version
+ "policyId": "",
+ "chainId": "",
+ "webhookData": ""
+ }
+ ```
+
+ * **Expected Result**: Your endpoint should respond with a `200` status code and a JSON body (`{ "approved": true | false }`) indicating whether to the userOp passes the custom rules. Anything other than the above will be treated as error, and the Gas Manager will sponsor the userOp only if `approveOnFailure` is `true`.
+
+* **Allowlist**: restrict wallet addresses that are eligible for sponsorship. The policy will only sponsor gas for userOps that were sent by addresses on this list.
+
+* **Blocklist**: ban certain addresses from receiving sponsorship under this policy.
+
+* **Sponsorship expiry period**: this is the period for which the Gas Manager signature will remain valid once it is generated.
+
+To learn more about policy configuration, refer to the guide on [setting up a gas manager policy](/docs/setup-a-gas-manager-policy).
+
+Once you have decided on policy rules for your app, create a policy in the [Gas Manager dashboard](https://dashboard.alchemy.com/gas-manager/policy/create/?a=api-docs).
+
+Now you should have a Gas policy created with a policy id you can use to sponsor gas for your users.
+
+### 2. Get Gas Manager's signature
+
+When sending a userOp, you can specify the `paymaster` and `paymasterData` fields in the `userOp` object. These fields are related to the signature of the Gas Manager that will sponsor the userOp.
+
+You can get these fields through [`alchemy_requestGasAndPaymasterAndData`](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) using your gas policy id, the API key of the app associated with the policy, and a userOp. The Gas Manager signature will be generated if and only if the userOp satisfies the rules defined in your gas policy.
+
+### 3. Send the sponsored userOp
+
+Once you get the `paymaster` and `paymasterData` fields, you can use them in your userOp when you call [`eth_sendUserOperation`](/reference/eth-senduseroperation). The Gas Manager will pay for the gas of the userOp when it is mined.
diff --git a/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/conditional-gas-sponsorship.mdx b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/conditional-gas-sponsorship.mdx
new file mode 100644
index 000000000..4877671ef
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/conditional-gas-sponsorship.mdx
@@ -0,0 +1,68 @@
+---
+title: Conditional Gas Sponsorship
+description: Step-by-step guide to sponsor gas for select transactions and users.
+url: https://alchemy.com/docs/reference/conditional-gas-sponsorship
+slug: wallets/low-level-infra/gas-manager/gas-sponsorship/using-sdk/conditional-gas-sponsorship
+---
+
+In this guide, you’ll learn how to enable **conditional gas sponsorship** to sponsor gas only for select transactions or users. Ex:
+
+* Sponsor gas only for swaps or txs that interact with your contracts.
+* Sponsor gas only for high-ROI users such as power users of your app or users with proof of humanity.
+
+### 1. Create a Webhook
+
+The webhook decides whether a given transaction should be sponsored. Every time you request gas sponsorship, the Gas Manager will make a POST request to your webhook.
+Your server responds with approval or rejection based on your custom rules.
+
+**Request Payload**
+
+* `userOperation`: The userOp object.
+* `policyId`: The ID of your policy.
+* `chainId`: The ID of the network.
+* `webhookData`: Optional additional data you wish to include in the request, such as proof of humanity.
+
+Here is an example request body:
+
+```json
+{
+ "userOperation": {}, // structure depends on EntryPoint version
+ "policyId": "",
+ "chainId": "",
+ "webhookData": ""
+}
+```
+
+**Expected Result**: Your endpoint should respond with `200` status code and the following JSON body:
+
+```json
+{
+ "approved": true // true to sponsor the tx, false to reject it
+}
+```
+
+### 2. Create a Gas Manager Policy
+
+A gas manager policy defines which transactions (userOps) are eligible for gas sponsorship. To enable conditional gas sponsorship, you'll need to fill in the `Custom Rules` section:
+
+* **Custom rules**: sponsor gas only for certain actions or users by making a request to your server to verify sponsorship eligibility. To enable, add the following details on your policy:
+
+ * **URL**: the webhook URL the Gas Manager will make a POST request to every time you request gas sponsorship
+ * **Sponsor on error or timeout**: when selected, the userOp will be sponsored in the event of an error or timeout.
+
+You can create a policy via the [Gas Manager dashboard](https://dashboard.alchemy.com/gas-manager/policy/create/?a=api-docs) or our [Create policy API](https://www.alchemy.com/docs/wallets/api/gas-manager-admin-api/admin-api-endpoints/create-policy).
+
+### 3. Get Gas Manager's signature
+
+
+ **\[Recommended]** Use our [SDK](https://www.alchemy.com/docs/wallets) to
+ create and use wallets. The SDK handles all complexity for you, making
+ development faster and easier.
+
+
+Use the `alchemy_requestGasAndPaymasterAndData` endpoint to get the Gas Manager's signature (`paymaster` and `paymasterData`). You'll need to pass your policy id, the API key of the app associated with the policy, the userOp, and optional webhookData that will be passed to your webhook for decision-making.
+The Gas Manager will make a request to your webhook. If approved and the userOp satisfied any additional rules defined in the policy, the Gas Manager will return the signature.
+
+### 4. Send the sponsored userOp
+
+Once you get the `paymaster` and `paymasterData` fields, you can use them in your userOp when you call [`eth_sendUserOperation`](/reference/eth-senduseroperation). The Gas Manager will pay for the gas of the userOp when it is mined.
diff --git a/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/pay-gas-with-any-erc20-token.mdx b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/pay-gas-with-any-erc20-token.mdx
new file mode 100644
index 000000000..04c48db3a
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/gas-manager/gas-sponsorship/using-sdk/pay-gas-with-any-erc20-token.mdx
@@ -0,0 +1,238 @@
+---
+title: Pay Gas with Any ERC20 Token
+description: Learn how to enable gas payments with ERC-20 tokens.
+subtitle: Learn how to enable gas payments with ERC-20 tokens.
+url: https://alchemy.com/docs/reference/how-to-pay-gas-with-any-token
+slug: wallets/low-level-infra/gas-manager/gas-sponsorship/using-sdk/pay-gas-with-any-erc20-token
+---
+
+Gas fees paid in the native gas token can feel foreign to users that primarily hold stablecoins or your app’s own token.
+With our smart wallet, you can enable your users to pay gas with ERC-20 tokens beyond the native gas token, like USDC or your own custom tokens, streamlining the user experience.
+
+
+ **How it works:** We front the gas using the network’s native gas token and
+ transfer the ERC-20 tokens from the user’s wallet to a wallet you control. The
+ equivalent USD amount and the admin fee is then added to your monthly invoice.
+
+
+
+ **\[Recommended]** Use our [SDK](https://www.alchemy.com/docs/wallets) to
+ create and use wallets. The SDK handles all complexity for you, making
+ development faster and easier.
+
+
+If you want to use APIs directly, follow these steps.
+
+## Steps
+
+### 1. Get an API key
+
+* Get you API Key by creating an app in your [Alchemy Dashboard](https://dashboard.alchemy.com/apps)
+* Make sure you enable the networks you are building on under the Networks tab
+
+### 2. Create a Gas Manager policy
+
+To enable your users to pay gas using an ERC-20 token, you need to create a “Pay gas with any token” Policy via the [Gas Manager dashboard](https://dashboard.alchemy.com/gas-manager/policy/create). You can customize the policy with the following:
+
+* Receiving address: an address of your choosing where the users' ERC20 tokens will be sent to as they pay for gas (this is orchestrated by the paymaster contract and happens automatically at the time of the transaction).
+* Tokens: the tokens the user should be able to pay gas with. Learn more [here](/reference/gas-manager-faqs).
+* ERC-20 transfer mode: choose when the user's token payment occurs.
+ * \[Recommended] After: No upfront allowance is required. The user signs an approval inside the same user operation batch, and the paymaster pulls the token *after* the operation has executed. If that post-execution transfer fails, the entire user operation is reverted and you still pay the gas fee.
+ * Before: You (the developer) must ensure the paymaster already has sufficient allowance—either through a prior `approve()` transaction or a permit signature—*before* the UserOperation is submitted. If the required allowance isn't in place when the user operation is submitted, it will be rejected upfront.
+* Sponsorship expiry period: this is the period for which the Gas Manager signature and ERC-20 exchange rate will remain valid once generated.
+
+Now you should have a Gas policy created with a policy id you can use to enable gas payments with ERC-20 tokens.
+
+### 3. Get Gas Manager’s signature
+
+When sending a userOperation, you can specify the `paymaster` and `paymasterData` fields in the **`userOp`** object. These fields are related to the signature of the Gas Manager that enables the user to pay for gas with ERC-20 tokens.
+
+You can get these fields through [`alchemy_requestGasAndPaymasterAndData`](/docs/wallets/api/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) using your Gas Manager Policy id, the API key of the app associated with the policy, a userOperation, the address of the EntryPoint contract, and the address of the ERC-20 token. You can find an example script below.
+
+### 4. Send the userOp
+
+Once you get the `paymaster` and `paymasterData` fields, you can use them in your userOperation when you call [`eth_sendUserOperation`](https://www.alchemy.com/docs/wallets/api-reference/bundler-api/bundler-api-endpoints/eth-send-user-operation). You can find an example script below.
+
+## Example script
+
+```ts twoslash
+import { ethers } from "ethers";
+
+// --- Constants ---
+
+// Address of the ERC-4337 EntryPoint contract
+const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789";
+
+// ABI for the EntryPoint contract, specifically for the getNonce function
+const ENTRYPOINT_ABI = [
+ {
+ type: "function",
+ name: "getNonce",
+ inputs: [
+ { name: "sender", type: "address", internalType: "address" },
+ { name: "key", type: "uint192", internalType: "uint192" },
+ ],
+ outputs: [
+ {
+ name: "nonce",
+ type: "uint256",
+ internalType: "uint256",
+ },
+ ],
+ stateMutability: "view",
+ },
+] as const;
+
+// Alchemy RPC URL for Sepolia testnet
+const ALCHEMY_RPC_URL = "YOUR_ALCHEMY_RPC_URL";
+// Alchemy Gas Manager RPC URL for Sepolia testnet
+const ALCHEMY_GAS_MANAGER_URL = "YOUR_ALCHEMY_GAS_MANAGER_URL";
+
+// Policy ID for the Alchemy Gas Manager
+const ALCHEMY_POLICY_ID = "YOUR_POLICY_ID";
+
+// Address of the ERC20 token to be used for gas payment
+const ERC20_TOKEN_ADDRESS = "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238"; // USDC
+
+// --- Types ---
+
+interface UserOperation {
+ sender: string;
+ nonce: string;
+ initCode: string;
+ callData: string;
+ signature: string;
+ paymasterAndData?: string;
+ preVerificationGas?: string;
+ verificationGasLimit?: string;
+ callGasLimit?: string;
+ maxFeePerGas?: string;
+ maxPriorityFeePerGas?: string;
+}
+
+interface GasAndPaymasterData {
+ paymasterAndData: string;
+ preVerificationGas: string;
+ verificationGasLimit: string;
+ callGasLimit: string;
+ maxFeePerGas: string;
+ maxPriorityFeePerGas: string;
+}
+
+// --- Ethers.js Setup ---
+
+// Initialize a JSON RPC provider
+const provider = new ethers.JsonRpcProvider(ALCHEMY_RPC_URL);
+
+// Create an ethers.js contract instance for the EntryPoint contract
+const entryPoint = new ethers.Contract(
+ ENTRYPOINT_ADDRESS,
+ ENTRYPOINT_ABI,
+ provider,
+);
+
+// --- Alchemy API Functions ---
+
+/**
+ * Requests gas fee estimations and paymaster data from Alchemy.
+ * This function constructs and sends a request to the 'alchemy_requestGasAndPaymasterAndData' RPC method.
+ */
+async function requestGasAndPaymaster(
+ uo: UserOperation,
+): Promise {
+ const body = JSON.stringify({
+ id: 1,
+ jsonrpc: "2.0",
+ method: "alchemy_requestGasAndPaymasterAndData",
+ params: [
+ {
+ policyId: ALCHEMY_POLICY_ID,
+ userOperation: {
+ sender: uo.sender,
+ nonce: uo.nonce,
+ initCode: uo.initCode,
+ callData: uo.callData,
+ },
+ erc20Context: {
+ tokenAddress: ERC20_TOKEN_ADDRESS,
+ },
+ entryPoint: ENTRYPOINT_ADDRESS,
+ dummySignature: uo.signature,
+ },
+ ],
+ });
+
+ const options = {
+ method: "POST",
+ headers: { accept: "application/json", "content-type": "application/json" },
+ body,
+ };
+
+ const res = await fetch(ALCHEMY_GAS_MANAGER_URL, options);
+ const jsonRes = await res.json();
+ console.log("Alchemy Gas and Paymaster Response:", jsonRes);
+ return jsonRes.result;
+}
+
+/**
+ * Sends a user operation to the bundler via Alchemy.
+ * This function constructs and sends a request to the 'eth_sendUserOperation' RPC method.
+ */
+async function sendUserOperation(uo: UserOperation): Promise {
+ const body = JSON.stringify({
+ id: 1,
+ jsonrpc: "2.0",
+ method: "eth_sendUserOperation",
+ params: [uo, ENTRYPOINT_ADDRESS],
+ });
+
+ const options = {
+ method: "POST",
+ headers: { accept: "application/json", "content-type": "application/json" },
+ body,
+ };
+
+ const res = await fetch(ALCHEMY_GAS_MANAGER_URL, options);
+ const jsonRes = await res.json();
+ console.log("Alchemy Send UserOperation Response:", jsonRes);
+}
+
+// --- Main Script Execution ---
+
+// Define the initial user operation object
+// This object contains the core details of the transaction to be executed.
+const userOp: UserOperation = {
+ sender: "0xYOUR_SMART_ACCOUNT_ADDRESS", // Smart account address
+ nonce: "0x", // Initial nonce (will be updated)
+ initCode: "0x", // Set to "0x" if the smart account is already deployed
+ callData: "0xYOUR_CALL_DATA", // Encoded function call data
+ signature: "0xYOUR_DUMMY_SIGNATURE", // Dummy signature, should be replaced after requesting paymaster data
+};
+
+// IIFE (Immediately Invoked Function Expression) to run the async operations
+(async () => {
+ // Fetch the current nonce for the sender address from the EntryPoint contract
+ const nonce = BigInt(await entryPoint.getNonce(userOp.sender, 0));
+ userOp.nonce = "0x" + nonce.toString(16); // Update userOp with the correct nonce
+
+ console.log("Fetching paymaster data and gas estimates...");
+ // Request paymaster data and gas estimations from Alchemy
+ const paymasterAndGasData = await requestGasAndPaymaster(userOp);
+
+ // Combine the original userOp with the data returned by Alchemy (paymasterAndData, gas limits, etc.)
+ const userOpWithGas: UserOperation = { ...userOp, ...paymasterAndGasData };
+
+ console.log(
+ "Final UserOperation with Gas and Paymaster Data:",
+ JSON.stringify(userOpWithGas, null, 2),
+ );
+ console.log("EntryPoint Address used for submission: ", ENTRYPOINT_ADDRESS);
+
+ // The script currently stops here. Uncomment the line below to actually send the UserOperation.
+ // Make sure your account is funded with the ERC20 token and has approved the paymaster.
+ return; // Intentionally stopping before sending for review. Remove this line to proceed.
+
+ // userOpWithGas.signature = await sign(userOpWithGas);
+ // await sendUserOperation(userOpWithGas);
+})();
+```
diff --git a/fern/wallets/pages/low-level-infra/gas-manager/policy-management/api-endpoints.mdx b/fern/wallets/pages/low-level-infra/gas-manager/policy-management/api-endpoints.mdx
new file mode 100644
index 000000000..07018015b
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/gas-manager/policy-management/api-endpoints.mdx
@@ -0,0 +1,8 @@
+---
+title: Gas Manager Admin API Endpoints
+description: The Gas Manager Admin API Endpoints allows you to programmatically manage your gas manager policies.
+subtitle: The Gas Manager Admin API Endpoints allows you to programmatically manage your gas manager policies.
+slug: wallets/low-level-infra/gas-manager/policy-management/api-endpoints
+---
+
+
diff --git a/fern/wallets/pages/low-level-infra/overview.mdx b/fern/wallets/pages/low-level-infra/overview.mdx
new file mode 100644
index 000000000..7d72554f1
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/overview.mdx
@@ -0,0 +1,18 @@
+---
+title: Low-level Infrastructure Overview
+description: Raw EIP-4337 APIs for advanced developers
+slug: wallets/transactions/low-level-infra/overview
+---
+
+# Lower Level Infra Overview
+
+Smart Wallets is composed of some lower-level libraries if you're looking for further customization of your stack or want more control over how you build your application and use third party providers.
+One of these libraries is `@account-kit/infra` which allows you to interact with our infrastructure directly, while bringing your own smart contracts or signer.
+
+If you do not need customization, we highly recommend that you use our Wallet APIs to simplify development.
+
+
+
+
+
+
diff --git a/fern/wallets/pages/low-level-infra/quickstart.mdx b/fern/wallets/pages/low-level-infra/quickstart.mdx
new file mode 100644
index 000000000..7180c0e04
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/quickstart.mdx
@@ -0,0 +1,62 @@
+---
+title: Quickstart
+description: Get started with Alchemy's ERC-4337 infrastructure
+slug: wallets/low-level-infra/quickstart
+---
+
+In this guide, we'll cover how to get started with `@account-kit/infra` and send a user operation. For smart contracts, we'll leverage `@account-kit/smart-contracts` to use `ModularAccountV2`,
+but you're free to use any smart contract you'd like.
+
+## Prerequisites
+
+* API key from your [dashboard](https://dashboard.alchemy.com/apps)
+* minimum Typescript version of 5 and the packages below from aa-sdk
+
+**Installation**
+
+
+ ```bash yarn
+ yarn add @account-kit/infra @account-kit/smart-contracts @aa-sdk/core viem
+ ```
+
+ ```bash npm
+ npm i -s @account-kit/infra @account-kit/smart-contracts @aa-sdk/core viem
+ ```
+
+
+## Create a Gas Manager policy
+
+If you want to sponsor gas, then you'll also want to create a gas policy in the Gas Manager dashboard.
+
+
+
+## Create a client
+
+Now that you have an API key and a Policy ID, you can create a [Smart Account Client](/wallets/concepts/smart-account-client) to interact with the infrastructure.
+
+
+ Replace `YOUR_API_KEY` and `POLICY_ID` with the key and Policy ID created
+ above.
+
+
+
+
+## Send a user operation
+
+The last step is to send a user operation using the client you just created.
+
+
+ ```ts example.ts
+ import { getClient } from "./client";
+
+ const client = await getClient();
+
+ const { hash } = await client.sendUserOperation({
+ uo: {
+ target: "0xTARGET_ADDRESS",
+ data: "0x",
+ value: 0n,
+ },
+ });
+ ```
+
diff --git a/fern/wallets/pages/low-level-infra/third-party-infrastructure/bundlers.mdx b/fern/wallets/pages/low-level-infra/third-party-infrastructure/bundlers.mdx
new file mode 100644
index 000000000..adc400c5a
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/third-party-infrastructure/bundlers.mdx
@@ -0,0 +1,77 @@
+---
+title: Third Party Bundlers
+description: Learn how to use a different RPC provider with Smart Wallets
+slug: wallets/low-level-infra/third-party-infrastructure/bundlers
+---
+
+The `SmartAccountClient` within `@aa-sdk/core` is unopinionated about which bundler you use, so you can connect to any RPC provider really simply.
+
+## Usage
+
+If we look at the example for creating a `SmartAccountClient`:
+
+```ts twoslash
+import { createSmartAccountClient } from "@aa-sdk/core";
+import { http } from "viem";
+import { sepolia } from "viem/chains";
+
+const client = createSmartAccountClient({
+ transport: http("https://polygon-mumbai.g.alchemy.com/v2/demo"),
+ chain: sepolia,
+});
+```
+
+You can see that we set the `transport` to `http("https://polygon-mumbai.g.alchemy.com/v2/demo")`. You can swap out that the url in the `http` function to
+any other provider's URL.
+
+
+ Depending on your provider, you may have to pass in custom logic for the
+ `gasEstimator` and `feeEstimator` properties when calling
+ `createSmartAccountClient`. Consult with your provider on what the correct
+ logic is.
+
+
+## Splitting Bundler traffic and Node RPC traffic
+
+It might be the case that you want to use a different RPC provider for your bundler traffic and your node traffic. This is a common use case, and you can do this by leveraging the [`split`](/wallets/reference/aa-sdk/core/functions/split) transport and passing it to your `createSmartAccountClient` call. For example:
+
+### Using Alchemy Bundler and Gas Manager with 3rd Party Node RPCs
+
+If you want to split your node traffic from Alchemy's Bundler traffic, you can do this with the `alchemyTransport`
+
+```ts twoslash
+import { alchemy } from "@account-kit/infra";
+
+const alchemyTransport = alchemy({
+ alchemyConnection: { apiKey: "your-api-key" },
+ nodeRpcUrl: "YOUR_NODE_RPC_URL",
+});
+// now use this transport in a client
+```
+
+### Using two different 3rd Party Bundler and Node RPCs
+
+```ts twoslash
+import { split } from "@aa-sdk/core";
+import { createPublicClient, http } from "viem";
+
+const bundlerMethods = [
+ "eth_sendUserOperation",
+ "eth_estimateUserOperationGas",
+ "eth_getUserOperationReceipt",
+ "eth_getUserOperationByHash",
+ "eth_supportedEntryPoints",
+];
+
+const clientWithSplit = createPublicClient({
+ transport: split({
+ overrides: [
+ {
+ methods: bundlerMethods,
+ transport: http("BUNDLER_RPC_URL"),
+ },
+ ],
+ fallback: http("OTHER_RPC_URL"),
+ }),
+});
+```
diff --git a/fern/wallets/pages/low-level-infra/third-party-infrastructure/chains.mdx b/fern/wallets/pages/low-level-infra/third-party-infrastructure/chains.mdx
new file mode 100644
index 000000000..c233d790e
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/third-party-infrastructure/chains.mdx
@@ -0,0 +1,35 @@
+---
+title: Third Party Chains
+description: Learn how to use a 3rd Party Chain
+slug: wallets/low-level-infra/third-party-infrastructure/chains
+---
+
+If you're using a chain that isn't supported by us or we only support Account Abstraction methods for those chains, then you can use `createSmartAccountClient` or one of non-alchemy `create*Client` methods from `@account-kit/smart-contracts`
+with a `split` transport to route your traffic accordingly.
+
+## AA only chains example
+
+For AA only chains, the `alchemyTransport` allows you to specify both the Alchemy Connection object as well as pass in a Node RPC URL, allowing you to split traffic between Alchemy's Bundler and Paymaster RPC and your Node RPC provider.
+
+```ts twoslash
+import { zora, alchemy } from "@account-kit/infra";
+import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+
+const smartAccountClient = createLightAccountAlchemyClient({
+ transport: alchemy({
+ alchemyConnection: {
+ apiKey: "ALCHEMY_API_KEY",
+ },
+ nodeRpcUrl: "ZORA_NODE_RPC_URL",
+ }),
+ chain: zora,
+ signer: LocalAccountSigner.generatePrivateKeySigner(),
+});
+```
+
+## Non-Alchemy chains example
+
+To use non-Alchemy supported chains, you can use the `createSmartAccountClient` method from `@aa-sdk/core` or any of the non-Alchemy `create*Client` methods exported from `@account-kit/smart-contracts` with a `chain` definition for your chain and a `transport` pointing to your RPC provider.
+
+See [`createSmartAccountClient`](/wallets/reference/aa-sdk/core/functions/createSmartAccountClient) for more information.
diff --git a/fern/wallets/pages/low-level-infra/third-party-infrastructure/paymasters.mdx b/fern/wallets/pages/low-level-infra/third-party-infrastructure/paymasters.mdx
new file mode 100644
index 000000000..3594badfa
--- /dev/null
+++ b/fern/wallets/pages/low-level-infra/third-party-infrastructure/paymasters.mdx
@@ -0,0 +1,35 @@
+---
+title: Third Party Paymasters
+description: Learn how to use a 3rd party Paymaster with Smart Wallets
+slug: wallets/low-level-infra/third-party-infrastructure/paymasters
+---
+
+The `SmartAccountClient` within `@aa-sdk/core` is unopinionated about which paymaster you use, so you can connect to any paymaster really simply. Configuration is done using the `paymasterAndData` config option when you call `createSmartAccountClient`.
+
+## Usage
+
+```ts twoslash
+import { createSmartAccountClient } from "@aa-sdk/core";
+import { http } from "viem";
+import { sepolia } from "viem/chains";
+
+const chain = sepolia;
+const client = createSmartAccountClient({
+ chain,
+ transport: http("RPC_URL"),
+ // sets the dummy paymasterAndData with paymaster address appended with some dummy paymasterData
+ // that looks like a valid paymasterData
+ dummyPaymasterAndData: async (userop) => ({
+ ...userop,
+ paymasterAndData: `0x`,
+ }),
+ paymasterAndData: async (userop, opts) => {
+ // call your paymaster here to sponsor the userop
+ // leverage the `opts` field to apply any overrides
+ return {
+ ...userop,
+ paymasterAndData: "0xresponsefromprovider",
+ };
+ },
+});
+```
diff --git a/fern/wallets/pages/migration-guide.mdx b/fern/wallets/pages/migration-guide.mdx
new file mode 100644
index 000000000..9603144cf
--- /dev/null
+++ b/fern/wallets/pages/migration-guide.mdx
@@ -0,0 +1,369 @@
+---
+outline: deep
+title: Migration Guide
+description: How to upgrade through breaking changes of the aa-sdk and account-kit
+slug: wallets/migration-guide
+---
+
+Below are the steps to migrate your project from older versions of the `aa-sdk` and `account-kit` to the latest version.
+
+## Migrating to Version V4.X.X
+
+### Packages
+
+All of the v3 packages have been renamed or removed. The new packages are as follows:
+
+* `@alchemy/aa-core` -> `@aa-sdk/core`
+* `@alchemy/aa-ethers` -> `@aa-sdk/ethers`
+* `@alchemy/aa-alchemy` -> split into `@account-kit/signer`, `@account-kit/infra`, `@account-kit/core`, `@account-kit/react`
+* `@alchemy/aa-accounts` -> `@account-kit/smart-contracts`
+* `@alchemy/aa-signers` -> removed
+
+### Smart Account Signers
+
+The `@alchemy/aa-signers` package has been deprecated. The signers in that package are still compatible with v4, but they will not be receiving future support and the interfaces are not guaranteed to work with future versions of the SDK.
+If you want to integrate a 3rd party signer, it's still very easy if the signer exposes an EIP-1193 interface. See [this guide](/wallets/third-party/signers/privy).
+
+
+ We still support all Signers in the SDK!
+
+ `@account-kit/core` and `@account-kit/react` are the only packages that are opinionated on which signer you can attach to a smart account. However, if you're using `@aa-sdk/*` or `@account-kit/infra` with `@account-kit/smart-contracts`, you can
+ use the guide above to integrate with any signer of your choice.
+
+
+### Alchemy Transport
+
+The new `Transport` type: `AlchemyTransport` has been added. This impacts how Alchemy clients, middleware, and configs are created.
+
+For Smart Account Clients, the `create*AlchemyClient` methods have been updated to take a new `transport` property (of type `AlchemyTransport`) instead of `rpcUrl` or `apiKey` directly.
+
+The `alchemyFeeEstimator` and `alchemyUserOperationSimulator` middleware methods now no longer take in a `ClientWithAlchemyMethods` (returned by `createAlchemyPublicRpcClient`) and instead take in an `AlchemyTransport` as well.
+
+The `AlchemyTransport` is a type of `viem` transport that makes it easier to configure your communication with Alchemy RPC. It supports splitting traffic between Alchemy's smart account infra and other Node providers (if needed). The `AlchemyTransport` has been added to `@account-kit/infra` and is exported as `alchemy`.
+
+Creating a config with `@account-kit/core` or `@account-kit/react` has been updated to accept an `AlchemyTransport` and the parameters of `createConfig` have been simplified as a result. You will now need to replace your `rpcUrl` or `apiKey` params with `transport: alchemy({...})`.
+
+For more detailed examples, see the relevant Quickstart guides, depending on which package you are using. If you don't know where to start, checkout the [React Quickstart](/wallets/react/quickstart).
+
+### Hooks: `useSendTransaction` and `useSendTransactions` removed
+
+These methods have been removed since `useSendUserOperation` can be used to send transactions. If you were using `useSendTransaction` or `useSendTransactions`, you can replace them with
+`useSendUserOperation` and pass in `waitForTxn: true` so that all `sendUserOperation` calls will wait for the transaction to be mined.
+
+### Utils: `verifyEIP6492Signature` removed
+
+This method has been removed. Use [`verifyMessage`](https://viem.sh/docs/actions/public/verifyMessage) from viem.
+
+### Utils: `defineReadOnly` removed
+
+This method is no longer used internally and has been removed. The reference impl can be found within `ethers.js`.
+
+### Utils: `getChain` removed
+
+This method was not super efficient and maintainable because it was importing all the chains from viem. That meant that if you used this method, it risked increasing your bundle size massively. Now all methods require a `Chain` instead of `chainId` in some places.
+
+### Ethers: `Chain` required param
+
+Certain methods required a `chainId` which would be converted into a `Chain` using the above method. This has been removed for the reasons above.
+
+### Alchemy `Chain` defs
+
+The Alchemy `Chain` definitions have been moved from `@aa-sdk/core` to `@account-kit/infra`.
+
+### Ethers: `getPublicErc4337Client` → `getBundlerClient`
+
+This method on the `AccountSigner` has been renamed.
+
+### Accounts: Default LightAccount Version Is Now v2.0.0
+
+If you were creating LightAccounts for users without explicitly specifying the version (ie. in `createLightAccount` or `useAccount` or `useSmartAccountClient`), you should manually specify the previous default of `v1.1.0`.
+
+### Accounts: `create*Client` methods now have the account parameters un-nested
+
+Previously, the `create*Client` (ie. `createLightAccountClient`) methods used to have an `account` parameter which took in the params for the underlying account. This is resulted in different APIs for both the corresponding `create*AlchemyClient` (ie. `createLightAccountAlchemyClient`) which had these as top-level parameters.
+If you were using `createLightAccountClient` or similar methods, you should now pass the account parameters as top-level parameters.
+
+### Accounts: `getNonce` -> `getAccountNonce`
+
+Due to a clash in the typing of viem's `Account` type, the `getNonce` method has been renamed to `getAccountNonce`.
+
+### Viem Version
+
+The version of viem has been updated to `2.20.0` and been added as a peer dependency. If you have viem listed as a dependency, make sure it is updated to `2.20.0`.
+
+### Dropped CJS Support
+
+Due to our dependency on `wagmi` in the `core` and `react` packages, those packages no longer support `cjs` builds. To keep things consistent across all of our packages, we've dropped CJS support in all the packages in this repo. See [this](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) guide on how to migrate from CJS to ESM.
+
+## Migrating to Version 3.X.X
+
+This version update brings a lot of breaking changes. As we began to support Modular Accounts and their interfaces, we realized that the previous version of the SDK was not as flexible as it could have been to handle the modularity of the new account interfaces.
+To address this, version 3.x.x of the SDK switches to an approach more idiomatic to `viem`.
+
+### Viem Version
+
+We have updated our dependency to viem v2.x.x. This means you will need to update your project to use >= v2.5.0 of viem.
+
+### Client: `SmartAccountProvider` → `SmartAccountClient`
+
+The biggest change is that the `SmartAccountProvider` class has been removed and replaced with a `SmartAccountClient` type that extends `viem`'s [`Client`](https://viem.sh/docs/clients/custom). To get started with the new clients, you can do the following:
+
+### Client: removed `sign*With6492` methods
+
+The `signMessageWith6492` and `signTypedDataWith6492` methods have been removed from the `SmartAccountClient`. Now, all clients will always use 6492 when signing messages. However, if you need to sign messages without 6492, you can still use the `client.account.signMessage` methods.
+
+### Client: `checkGasSponsorshipEligibility` return type updated
+
+The `checkGasSponsorshipEligibility` methods now returns an object that includes the eligibility status of a transaction as well as the `UserOperationStruct` that the eligibility is for. This now ensures that checking for sponsorship doesn't cause you to use up your
+gas sponsorship limits. If your transaction is eligible, then you can sign the UO with `client.signUserOperation` and send it with `client.sendRawUserOperation`.
+
+```ts
+import { SmartAccountProvider } from "@aa-sdk/core"; // [!code --]
+import { getDefaultEntryPointAddress } from "@aa-sdk/core"; // [!code --]
+import { http } from "viem"; // [!code ++]
+import { sepolia } from "@aa-sdk/core";
+
+const provider = new SmartAccountProvider({ // [!code --]
+const client = createSmartAccountClient({ // [!code ++]
+ rpcProvider: "RPC_URL", // [!code --]
+ transport: http("RPC_URL"), // [!code ++]
+ chain: sepolia,
+ entryPointAddress: getDefaultEntryPointAddress(sepolia), // [!code --]
+});
+```
+
+### Client: Removal of `with*` middleware override functions
+
+The `SmartAccountProvider` in previous versions had a number of `with*` functions that mapped to the corresponding middleware functions on the provider.
+The concept of the middlewares is still present in this version, but their configuration has been moved to the `SmartAccountClient` creator. For example,
+
+```ts
+import { SmartAccountProvider } from "@aa-sdk/core"; // [!code --]
+import { getDefaultEntryPointAddress } from "@aa-sdk/core"; // [!code --]
+import { http } from "viem"; // [!code ++]
+import { sepolia } from "@aa-sdk/core";
+
+const provider = new SmartAccountProvider({ // [!code --]
+const client = createSmartAccountClient({ // [!code ++]
+ rpcProvider: "RPC_URL", // [!code --]
+ transport: http("RPC_URL"), // [!code ++]
+ chain: sepolia,
+ entryPointAddress: getDefaultEntryPointAddress(sepolia), // [!code --]
+}).withGasEstimator(async () => ({ // [!code --]
+ gasEstimator: async (struct) => ({ // [!code ++]
+ ...struct, // [!code ++]
+ callGasLimit: 0n,
+ preVerificationGas: 0n,
+ verificationGasLimit: 0n,
+ }), // [!code ++]
+});
+```
+
+### Client: Signature Changes on Methods
+
+To support the various ways of connecting to a smart account, the signatures of the methods on `SmartAccountClient` have changed. Almost all methods now have an optional param for `account` and have been converted into single argument functions that take an object with the their properties instead.
+
+### Account: `BaseSmartContractAccount` → `SmartContractAccount`
+
+The next big change is the removal of the class-based `BaseSmartContractAccount` that all accounts extended from. This has been replaced with a `SmartContractAccount` type that extends `viem`'s [`Account`](https://v1.viem.sh/docs/accounts/custom.html), and instantiation of an account is now an `async` action. To get started with the new accounts (using `LightAccount` as an example), you will have to make the following changes:
+
+```ts
+import {
+ LightSmartContractAccount, // [!code --]
+ createLightAccount, // [!code ++]
+ getDefaultLightAccountFactoryAddress, // [!code --]
+} from "@account-kit/smart-contracts";
+import {
+ LocalAccountSigner,
+ type Hex,
+} from "@aa-sdk/core";
+import { sepolia } from "@aa-sdk/core";
+
+const chain = sepolia;
+
+const account = new LightSmartContractAccount({ // [!code --]
+const account = await createLightAccount({ // [!code ++]
+ rpcClient: client, // [!code --]
+ transport: http("RPC_URL"), // [!code ++]
+ signer,
+ chain,
+ factoryAddress: getDefaultLightAccountFactoryAddress(chain), // [!code --]
+ });
+```
+
+### Account: Connecting to a Smart Account
+
+In earlier versions, a provider could not be used with a smart account until it was connected to one using `.connect`. In version 3.x.x, you have the option of keeping the two disconnected and passing the account to the client methods directly. You also have the option to hoist the account
+so that you don't have to pass the account to every method.
+
+#### Option 1: Passing the Account to the Client Methods
+
+```ts
+import { createLightAccount } from "@account-kit/smart-contracts";
+import {
+ createBundlerClient,
+ createSmartAccountClientFromExisting
+ LocalAccountSigner,
+ type Hex,
+} from "@aa-sdk/core";
+import { sepolia } from "@aa-sdk/core";
+import { custom, http } from "viem";
+
+const chain = sepolia;
+
+const client = createBundlerClient({
+ chain,
+ transport: http("JSON_RPC_URL"),
+});
+
+// [!code focus:99]
+// createSmartAccountClientFromExisting is a helper method that allows you
+// to reuse a JSON RPC client to create a Smart Account client.
+const smartAccountClient = createSmartAccountClientFromExisting({
+ client,
+});
+
+const account = await createLightAccount({
+ signer,
+ chain,
+ transport: custom(client),
+});
+
+const { hash } = await smartAccountClient.sendUserOperation({
+ uo: {
+ target: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
+ data: "0x",
+ value: 10n,
+ },
+ account, // [!code ++]
+});
+```
+
+#### Option 2: Hoisting the Account
+
+Hoisting the account is similar to using `.connect` in previous versions. You simply create your client with an account passed in to it.
+
+```ts
+import { createLightAccount } from "@account-kit/smart-contracts";
+import {
+ createBundlerClient,
+ createSmartAccountClientFromExisting
+ LocalAccountSigner,
+ type Hex,
+} from "@aa-sdk/core";
+import { sepolia } from "@aa-sdk/core";
+import { http, custom } from "viem";
+
+const chain = sepolia;
+
+const client = createBundlerClient({
+ chain,
+ transport: http("JSON_RPC_URL"),
+});
+
+// [!code focus:99]
+const account = await createLightAccount({
+ signer,
+ transport: custom(client),
+ chain,
+});
+
+const smartAccountClient = createSmartAccountClientFromExisting({
+ account, // [!code ++]
+ client,
+});
+
+const { hash } = await smartAccountClient.sendUserOperation({
+ uo: {
+ target: "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
+ data: "0x",
+ value: 10n,
+ },
+ account, // [!code --]
+});
+```
+
+### Account: Custom Accounts
+
+In prior versions, using your own wallet implementations required that you extend `BaseSmartContractAccount`. In version 3.x.x, you can use the `toSmartContractAccount` method which will allow you to use any account with `SmartAccountClient`. `toSmartContractAccount` has the form of:
+
+```ts
+type toSmartContractAccount = <
+ Name extends string = string,
+ TTransport extends Transport = Transport,
+>({
+ transport,
+ chain,
+ source,
+ entryPointAddress,
+ accountAddress,
+ getAccountInitCode,
+ signMessage,
+ signTypedData,
+ encodeBatchExecute,
+ encodeExecute,
+ getDummySignature,
+ signUserOperationHash,
+ encodeUpgradeToAndCall,
+}: ToSmartContractAccountParams) => Promise<
+ SmartContractAccount
+>;
+```
+
+### Account: SimpleAccount and NaniAccount initialization params
+
+`chain` and `transport` have been added to the constructor and `rpcClient` has been removed.
+
+### Account: SimpleAccount and LightAccount initialization params
+
+`index` is now called `salt`
+
+### Signer: `signTypedData` signature change
+
+The `signTypedData` method found on `SmartAccountSigner` has been updated to match the signature found on `SmartContractAccount` and viem's `Account`.
+
+```ts
+(params: SignTypedDataParams) => Promise; // [!code --]
+
+<
+ const TTypedData extends TypedData | { [key: string]: unknown },
+ TPrimaryType extends string = string,
+>(
+ params: TypedDataDefinition,
+) => Promise;
+```
+
+### Ethers: Removed Methods
+
+The `with*` methods have been removed from the Provider and Signer classes.
+The `connect` methods has been removed in favor of immutable properties on the Provider and Signer classes. See updated AccountSigner constructor below.
+
+### Ethers: `getPublicErc4337Client` → `getBundlerClient`
+
+The `getPublicErc4337Client` method has been renamed to `getBundlerClient` to match the naming found in `aa-core`.
+
+### Ethers: Updated Signer Adapter constructor
+
+The `AccountSigner` now takes in a `SmartContractAccount` as a param in its constructor.
+
+### Core: Transition from ~~`Percent`~~ to `Multiplier` api and types
+
+The `Percent` type and `PercentSchema` have been removed in favor of the `Multiplier` type and `MultiplierSchema`.
+
+Going forward when using the feeOptions, you can specify the `Multiplier` type instead of a `Percent`. The `Multiplier` type is a number that represents direct multiplication of the estimation. For example, `0.1` is 10% of the estimated value and `1` is 100% of the estimated value.
+
+```ts
+createModularAccountAlchemyClient({
+ ...
+ opts: {
+ ...
+ // The maxFeePerGas and maxPriorityFeePerGas estimated values will now be multipled by 1.5
+ feeOptions: {
+ // This was previously { percent: 50n }
+ maxFeePerGas: { multiplier: 1.5 },
+ // This was previously { percent: 25n }
+ maxPriorityFeePerGas: { multiplier: 1.25 },
+ },
+ },
+ });
+```
diff --git a/fern/wallets/pages/overview/sdk-concepts/intro-to-account-kit.mdx b/fern/wallets/pages/overview/sdk-concepts/intro-to-account-kit.mdx
new file mode 100644
index 000000000..49f090c4d
--- /dev/null
+++ b/fern/wallets/pages/overview/sdk-concepts/intro-to-account-kit.mdx
@@ -0,0 +1,102 @@
+---
+title: Intro to Smart Wallets
+description: Introduction to Smart Wallets and Account Abstraction
+slug: wallets/concepts/intro-to-account-kit
+---
+
+Smart Wallets is your all-in-one toolkit for building zero-friction sign-up and transaction flows. But what's **really** happening under the hood?
+
+Smart Wallets abstracts away the complexity of smart accounts, but as a builder it's useful to have a foundational understanding of how it works. This way, you can make informed decisions and unlock the full potential of account abstraction.
+
+Unlike other embedded wallet providers that only solve sign-up and key management, Smart Wallets goes further by streamlining the transaction flow with features like gas sponsorship. How? Through **Account Abstraction** and **smart accounts** 🚀 **Let's break it down.**
+
+## Smart accounts: programmable wallets, not EOAs
+
+
+
+ With Smart Wallets, you'll deploy a **Smart Contract Account (SCA)** for
+ each user instead of an Externally Owned Account (EOA). This smart account
+ will securely store the user's assets, such as tokens or NFTs.
+
+
+
+
+
+
+
+#### What is a smart account?
+
+Unlike EOAs, SCAs are programmable and can include logic. When we refer to SCAs, or smart accounts, we are typically talking about [ERC-4337](https://www.alchemy.com/overviews/what-is-account-abstraction) smart accounts.
+
+Smart Wallets comes with enterprise-grade, audited smart account implementations. We recommend using [Modular Account v2](/wallets/smart-contracts/modular-account-v2/overview). Learn more and [choose](/wallets/smart-contracts/choosing-a-smart-account) the smart account that best fits your needs or bring your own smart account.
+
+#### Why smart accounts?
+
+Smart accounts unlock powerful Account Abstraction features like gas sponsorship and transaction batching enabling you to create seamless transaction experiences (more on this [later](#gas-manager-sponsor-gas)).
+
+Smart accounts provide flexibility, security, and additional functionality over EOAs. Features like social recovery, two-factor authentication, multi-owner support, and ownership transfer become possible with smart accounts.
+
+In Smart Wallets, this concept will manifest as a [Smart Contract Account](/wallets/resources/types#smartcontractaccount).
+
+## Signer: web2 login and key management
+
+
+
+ A **Signer** is a service (e.g., Turnkey or Magic) or application (e.g., MetaMask) that manages a private key and signs transactions. The signature is only valid if the signer is an owner of the smart account.
+
+
+
+
+
+
+
+[Using Smart Wallets](/wallets/react/getting-started), you can secure a user's Smart Account with email, social login, or passkeys, using our non-custodial Signer infrastructure. Smart accounts support advanced use cases, such as multiple owners and ownership transfer, offering more utility than EOAs.
+
+With this setup, users can sign-up, log in, and sign transactions using familiar web2 user experiences.
+
+In Smart Wallets, this concept will manifest as a [Smart Account Signer](/wallets/signer/what-is-a-signer).
+
+## Bundler: transactions → user operations
+
+
+
+ With Smart Wallets, [sending transactions](/wallets/transactions/send-transactions) is as simple as sending "normal" EOA transactions. However, under the hood, you're actually sending [**User Operations (UOs)**](https://www.alchemy.com/overviews/user-operations) — a standardized pseudo-transaction object — to a **Bundler**.
+
+
+
+
+
+
+
+A User Operation (UO) is an ABI-encoded struct that describes a transaction to be sent on behalf of a user. Since SCAs cannot initiate transactions on chains without native account abstraction support, they send UOs instead. The Bundler will gather UOs from multiple smart accounts (senders) and bundle them into a single on-chain transaction.
+
+A huge benefit of smart accounts is the ability to [batch transactions](/wallets/transactions/send-batch-transactions) in one UO creating simpler user experiences. For example, you can now approve and swap in one click, rather than signing, waiting, and sending multiple transactions.
+
+In Smart Wallets, this concept will manifest as a [Bundler Client](/wallets/resources/types#bundlerclient), but for simplicity you may only need a [Smart Account Client](/wallets/concepts/smart-account-client).
+
+## Gas Manager: sponsor gas
+
+
+
+ With gas sponsorship, your users won't need to worry about having assets in their accounts to pay for gas. Using [Smart Wallets](/wallets/transactions/sponsor-gas), simply configure a gas manager policy, insert your policy ID into the SDK, and let our Gas Manager handle the rest.
+
+
+
+
+
+
+
+## Conclusion
+
+Smart Wallets makes it easy to build a web2 user experience from sign-up to transaction. It includes everything you need to bring users onchain:
+
+* Non-custodial **Signer** to authenticate with web2 login
+* Secure and flexible **smart accounts** to store assets
+* **Gas Manager** to sponsor gas
+* Reliable and scalable **Bundler** to land user operations
+
+Account Abstraction might seem complex, but Smart Wallets abstracts away the intricacies, allowing you to focus on building great user experiences.
+
+**Now, get started with the [quickstart](/wallets/react/quickstart)!**
+
+Want to learn more? Explore our [Account Abstraction education hub](https://www.alchemy.com/learn/account-abstraction)
diff --git a/fern/wallets/pages/overview/supported-chains.mdx b/fern/wallets/pages/overview/supported-chains.mdx
new file mode 100644
index 000000000..52328ee5c
--- /dev/null
+++ b/fern/wallets/pages/overview/supported-chains.mdx
@@ -0,0 +1,62 @@
+---
+title: Supported Chains
+---
+
+Alchemy Smart Wallets are supported on all major chains! Login and authentication is chain-agnostic, so our SDK works everywhere out of the box.
+
+To send transactions and sponsor gas, we provide infrastructure (bundler and gas manager) on the chains listed below. You can even use multiple chains at once in the same app — see our [multi-chain guide](/wallets/recipes/multi-chain-setup) for details.
+
+Ensure to import chains from '@account-kit/infra'.
+
+## Need another chain?
+
+We’re constantly expanding support and can quickly spin up new chains on request. This is the best way to ensure everything works seamlessly with Smart Wallets.
+
+
+ Request support by reaching out to wallets@alchemy.com or filling out this
+ [form](https://alchemy.chilipiper.com/router/wallet-services-chain-support-requests).
+
+
+
+ Alchemy Smart Wallets will be supported on **Monad mainnet** and **MegaETH
+ mainnet** Day 1 (coming soon).
+
+
+The chain identifiers (e.g. base-mainnet) are used for RPC endpoint URLs:
+https://NETWORK_IDENTIFIER.g.alchemy.com/v2/API_KEY.
+
+
+
+| Chain | Mainnet | Testnet | Bundler | Gas Sponsorship |
+| ------------- | ---------------------------- | ---------------------------- | ------- | --------------- |
+| AnimeChain | ✅ 69000 (anime-mainnet) | ✅ 6900 (anime-sepolia) | ✅ | ✅ |
+| Arbitrum Nova | ✅ 42170 (arbnova-mainnet) | ❌ | ✅ | ✅ |
+| Arbitrum One | ✅ 42161 (arb-mainnet) | ✅ 421614 (arb-sepolia) | ✅ | ✅ |
+| Base | ✅ 8453 (base-mainnet) | ✅ 84532 (base-sepolia) | ✅ | ✅ |
+| BNB | ✅ 56 (bnb-mainnet) | ✅ 97 (bnb-testnet) | ✅ | ✅ |
+| BeraChain | ✅ 80094 (berachain-mainnet) | ✅ 80084 (berachain-bartio) | ✅ | ✅ |
+| Celo | ✅ 42220 (celo-mainnet) | ✅ 44787 (celo-alfajores) | ✅ | ✅ |
+| Ethereum | ✅ 1 (eth-mainnet) | ✅ 11155111 (eth-sepolia) | ✅ | ✅ |
+| Frax | ✅ 252 (frax-mainnet) | ❌ | ✅ | ✅ |
+| Hyperliquid | ✅ 999 (hyperliquid-mainnet) | ✅ 998 (hyperliquid-testnet) | ✅ | ✅ |
+| Ink | ✅ 57073 (ink-mainnet) | ✅ 763373 (ink-sepolia) | ✅ | ✅ |
+| Monad | ❌ | ✅ 10143 (monad-testnet) | ✅ | ✅ |
+| opBNB | ✅ 204 (opbnb-mainnet) | ✅ 5611 (opbnb-testnet) | ✅ | ✅ |
+| Optimism | ✅ 10 (opt-mainnet) | ✅ 11155420 (opt-sepolia) | ✅ | ✅ |
+| Polygon | ✅ 137 (polygon-mainnet) | ✅ 80002 (polygon-amoy) | ✅ | ✅ |
+| Polynomial | ✅ 8008 (polynomial-mainnet) | ✅ 8009 (polynomial-sepolia) | ✅ | ✅ |
+| Race | ✅ 6805 (race-mainnet) | ✅ 6806 (race-sepolia) | ✅ | ✅ |
+| Rise | ❌ | ✅ 11155931 (rise-testnet) | ✅ | ✅ |
+| Shape | ✅ 360 (shape-mainnet) | ✅ 11011 (shape-sepolia) | ✅ | ✅ |
+| Solana | ✅ (solana-mainnet) | ✅ solana-devnet | ❌ | ✅ |
+| Soneium | ✅ 1868 (soneium-mainnet) | ✅ 1946 (soneium-minato) | ✅ | ✅ |
+| Story | ✅ 1514 (story-mainnet) | ✅ 1315 (story-aeneid) | ✅ | ✅ |
+| UniChain | ✅ 130 (unichain-mainnet) | ✅ 1301 (unichain-sepolia) | ✅ | ✅ |
+| WorldChain | ✅ 480 (worldchain-mainnet) | ✅ 4801 (worldchain-sepolia) | ✅ | ✅ |
+| Zora | ✅ 7777777 (zora-mainnet) | ✅ 999999999 (zora-sepolia) | ✅ | ✅ |
+| Boba | ✅ 288 (boba-mainnet) | ✅ 28882 (boba-sepolia) | ✅ | ✅ |
+| Degen | ✅ 666666666 (degen-mainnet) | ❌ | ❌ | ❌ |
+| Openloot | ❌ | ✅ 905905 (openloot-sepolia) | ✅ | ✅ |
+| Tea | ❌ | ✅ 10218 (tea-sepolia) | ✅ | ✅ |
+
+
diff --git a/fern/wallets/pages/react-native/getting-started/app-integration.mdx b/fern/wallets/pages/react-native/getting-started/app-integration.mdx
new file mode 100644
index 000000000..5ada1e352
--- /dev/null
+++ b/fern/wallets/pages/react-native/getting-started/app-integration.mdx
@@ -0,0 +1,61 @@
+---
+title: Setup authentication to smart wallets on React Native
+description: Setup authentication to smart wallets on React Native
+slug: wallets/react-native/getting-started/app-integration
+---
+
+In this step you will get your authentication working so users can login with their smart wallets.
+
+## Setup your application
+
+First, configure your application and your API Key:
+
+1. Create an app in the [dashboard](https://dashboard.alchemy.com/signup) and copy the **API Key**.
+
+2. Create a **`configuration`** in the [`Smart Wallets` dashboard](https://dashboard.alchemy.com/services/smart-wallets/configuration) to enable login methods.
+
+Hold onto your API Key for the next step.
+
+## Setup the Alchemy Accounts Provider
+
+Now set up and configure the Alchemy Accounts Provider in your project.
+
+You can do this by wrapping the top level component (e.g. `_layout.tsx` in Expo or `App.tsx` in Bare React Native) in your app with the `AlchemyAccountsProvider` component from the `@account-kit/react-native` package.
+
+Here's an example of how to do this:
+
+
+
+
+
+
+
+## Choose your authentication
+
+Now that you have set up the Alchemy Accounts Provider, you can setup your authentication!
+
+You can choose from many authentication guides with react native instructions, like [this one on Email One-Time Passwords](/docs/wallets/authentication/login-methods/email-otp#react-native).
+
+## Next Steps
+
+Once you have authentication working, you can explore additional features:
+
+
+
+ Learn how to perform transactions by sending user operations with your smart wallet.
+
+
+{" "}
+
+
+ Enable gasless transactions by setting up gas sponsorship for your users.
+
+
diff --git a/fern/wallets/pages/react-native/getting-started/getting-started-expo.mdx b/fern/wallets/pages/react-native/getting-started/getting-started-expo.mdx
new file mode 100644
index 000000000..e91ba004c
--- /dev/null
+++ b/fern/wallets/pages/react-native/getting-started/getting-started-expo.mdx
@@ -0,0 +1,334 @@
+---
+title: Getting started with Smart Wallets on Expo
+description: A guide on integrating Smart Wallets within a React Native Expo application
+slug: wallets/react-native/getting-started/getting-started-expo
+---
+
+We would go through the steps to get your environment setup for using Smart Wallets within a React Native application on Expo.
+
+
+
+## Create a new Expo project
+
+If you don't have an Expo project setup, you can create one using the following command:
+
+```bash
+npx create-expo-app@latest
+```
+
+## Upgrade to the latest version of Expo
+
+The first thing we need to do is make sure we're on the latest version of Expo (SDK 52 or later). The reason for this is that we need React Native version 0.76 or higher because it has `TextEncoder` natively supported.
+
+For more information on upgrading an Expo project, check out the [Expo documentation](https://docs.expo.dev/workflow/upgrading-expo-sdk-walkthrough/).
+
+
+
+```bash npx
+npx expo install expo@latest
+```
+
+```bash yarn
+yarn expo install expo@latest
+```
+
+
+
+
+You can also use our [quickstart template](https://github.com/alchemyplatform/account-kit-expo-quickstart) to get started quickly.
+Simply run
+
+```bash
+npx create-expo-app@latest --template https://github.com/alchemyplatform/account-kit-expo-quickstart
+```
+
+
+
+Then you want to upgrade all dependencies to match the new Expo SDK version.
+
+```bash
+npx expo install --fix
+```
+
+## Set up shims
+
+Once we've got our Expo project setup and running, we need to setup a few shims so we can use crypto libraries in React Native.
+
+### Install shim dependencies
+
+
+
+```bash npm
+npm install --save node-libs-react-native crypto-browserify stream-browserify react-native-get-random-values
+```
+
+```bash yarn
+yarn add node-libs-react-native crypto-browserify stream-browserify react-native-get-random-values
+```
+
+
+
+### Register shim modules in Metro
+
+Create or edit your `metro.config.js` file in the root of your project so that it includes the following:
+
+```js metro.config.js
+// Learn more https://docs.expo.io/guides/customizing-metro
+const { getDefaultConfig } = require("expo/metro-config");
+const path = require("path");
+const fs = require("fs");
+const projectRoot = __dirname; // <-- Adjust this as fits your project setup
+
+// Add aliases for file-system import based modules
+const ALIASES = {
+ "@noble/hashes/crypto": path.resolve(
+ projectRoot,
+ "node_modules/@noble/hashes/crypto.js",
+ ),
+ "@sinclair/typebox": path.resolve(
+ projectRoot,
+ "node_modules/@sinclair/typebox/build/cjs/index.js",
+ ),
+};
+
+/** @type {import('expo/metro-config').MetroConfig} */
+const config = getDefaultConfig(projectRoot);
+// [!code focus:9]
+// The following code ensures we have the necessary
+// shims for crypto built into our project
+config.resolver.extraNodeModules = {
+ ...config.resolver.extraNodeModules,
+ ...require("node-libs-react-native"),
+ crypto: require.resolve("crypto-browserify"),
+ stream: require.resolve("stream-browserify"),
+};
+
+config.resolver.resolveRequest = (context, moduleName, platform) => {
+ if (ALIASES[moduleName]) {
+ return {
+ filePath: ALIASES[moduleName],
+ type: "sourceFile",
+ };
+ }
+
+ // Handle .js/.jsx extensions on TypeScript files
+ if (
+ (moduleName.startsWith(".") || moduleName.startsWith("/")) &&
+ (moduleName.endsWith(".js") || moduleName.endsWith(".jsx"))
+ ) {
+ const moduleFilePath = path.resolve(
+ context.originModulePath,
+ "..",
+ moduleName,
+ );
+
+ // if the file exists, we won't remove extension, and we'll fall back to normal resolution.
+ if (!fs.existsSync(moduleFilePath)) {
+ return context.resolveRequest(
+ context,
+ moduleName.replace(/\.[^/.]+$/, ""),
+ platform,
+ );
+ }
+ }
+
+ return context.resolveRequest(context, moduleName, platform);
+};
+
+// The `account-kit/react-native` and it's supoorting packages leverages package.json `exports` which is not (yet) suported by default in Metro.
+// we can enable this support using:
+config.resolver.unstable_enablePackageExports = true;
+config.resolver.unstable_conditionNames = [
+ "browser",
+ "require",
+ "react-native",
+];
+
+module.exports = config;
+```
+
+### Register global shims
+
+Import the following packages at the topmost entry point of your app so that libraries that depend on globals like `crypto` have access to them.
+
+If you are using `expo-router`, add the imports in your topmost `_layout.tsx` file in the `app` directory. However if you are using a different navigation library (e.g. `react-navigation`),
+add the imports in your topmost `App.tsx` file.
+
+```tsx App.tsx or app/_layout.tsx
+import "node-libs-react-native/globals.js";
+import "react-native-get-random-values";
+
+// rest of App.tsx
+```
+
+## Install Smart Wallets
+
+That's it! Now you can install the packages you want from Smart Wallets and start building your React Native Account Abstraction app.
+
+
+If you get an error about mismatched peer dependencies for React, you can use `--legacy-peer-deps` in your install commands to avoid this error.
+
+The `@account-kit/react-native` package is an ESM module. As such, have to add the following to your `tsconfig.json`'s `compilerOptions`:
+
+```json
+"module": "NodeNext",
+"moduleResolution": "nodenext",
+```
+
+
+
+
+
+```bash npm
+npm install --save @account-kit/react-native @account-kit/smart-contracts @account-kit/infra @account-kit/react-native-signer
+```
+
+```bash yarn
+yarn add @account-kit/react-native @account-kit/smart-contracts @account-kit/infra @account-kit/react-native-signer
+```
+
+
+
+### Add supporting dependencies
+
+To ensure the Signer package works correctly, you'll need to add the following dependencies to your project:
+
+
+
+```bash npm
+npm install --save react-native-mmkv zustand abitype react-native-inappbrowser-reborn viem wagmi @tanstack/react-query
+```
+
+```bash yarn
+yarn add react-native-mmkv zustand abitype react-native-inappbrowser-reborn viem wagmi @tanstack/react-query
+```
+
+
+
+
+The zustand library uses `import.meta` which is not supported in the latest version of Expo. To fix this, create a `babel.config.js` file with the following content:
+
+```js
+module.exports = function (api) {
+ api.cache(true);
+ return {
+ presets: [["babel-preset-expo", { unstable_transformImportMeta: true }]],
+ };
+};
+```
+
+
+
+## Set iOS minimum deployment target
+
+Since we require a minimum deployment target of iOS 17, you will need to instruct Expo to set this during pre-build. First, install `expo-build-properties` via:
+
+```bash
+npx expo install expo-build-properties
+```
+
+Then add the plugin to your `app.json`:
+
+```json
+// app.json
+{
+ "expo": {
+ "plugins": [
+ [
+ "expo-build-properties",
+ {
+ "ios": {
+ "deploymentTarget": "17.0"
+ }
+ }
+ ]
+ ]
+ }
+}
+```
+
+## Run a Prebuild!
+
+Now that we've got everything setup, we can run a prebuild to ensure the native modules are properly built and added to your project.
+
+
+
+```bash android
+npx expo prebuild --platform android
+```
+
+```bash ios
+npx expo prebuild --platform ios
+```
+
+
+
+## Run the app
+
+Because the app is using native modules, you cannot run it with expo go and instead need to use development builds. You can do this with the `android` and `ios` commands:
+
+
+
+```bash npm(android)
+npm run android
+```
+
+```bash yarn(android)
+yarn android
+```
+
+```bash npm(ios)
+npm run ios
+```
+
+```bash yarn(ios)
+yarn ios
+```
+
+
+
+## Common Issues
+
+### NotSupportedError: Cannot set "location"., js engine: hermes
+
+If you get this error, you can add the following to you `app.json` within the `expo` config:
+
+```json
+"extra": {
+ "router": {
+ "origin": false
+ }
+}
+```
+
+### Build error: androidx.browser:browser requires a higher Android Gradle Plugin (AGP) version or compileSdk version
+
+If you get this error when running the Android app, you can fix this by updating the `android/build.gradle` to include the following override:
+
+```gradle
+allprojects {
+ configurations.all {
+ resolutionStrategy {
+ // Force a specific version of androidx.browser
+ force 'androidx.browser:browser:1.8.0'
+ }
+ }
+}
+```
+
+Related issue: https://github.com/alchemyplatform/aa-sdk/issues/1534
+
+### `npm install` fails due to mismatching peer dependencies
+
+Our packages list a minimum version of React as 18.2.0, but the latest version of expo is on >=19. If you are using `npm install` it's likely you'll get errors about peer dependencies. To force a consistent version of react, you can add the following to your `package.json`:
+
+```json
+"overrides": {
+ "react": "19.0.0",
+ "react-dom": "19.0.0"
+}
+```
+
+## Next Steps
+
+Move on to [app integration](/docs/wallets/react-native/getting-started/app-integration)!
diff --git a/fern/wallets/pages/react-native/getting-started/getting-started-quickstart.mdx b/fern/wallets/pages/react-native/getting-started/getting-started-quickstart.mdx
new file mode 100644
index 000000000..de0b8507d
--- /dev/null
+++ b/fern/wallets/pages/react-native/getting-started/getting-started-quickstart.mdx
@@ -0,0 +1,47 @@
+---
+title: Getting started quickly with Smart Wallets on Expo
+description: A guide on configuring a template using Smart Wallets with a React Native Expo application
+slug: wallets/react-native/getting-started/getting-started-quickstart
+---
+
+## Project Setup
+
+Simply run:
+
+```bash
+npx create-expo-app@latest --template https://github.com/alchemyplatform/account-kit-expo-quickstart
+```
+
+This will create a new Expo project with Smart Wallets setup. Next you will setup your API key and accounts configuration.
+
+1. Create an app in the [dashboard](https://dashboard.alchemy.com/signup) and copy the **API Key**.
+
+2. Create a **`configuration`** in the [`Smart Wallets` dashboard](https://dashboard.alchemy.com/services/smart-wallets/configuration) to enable login methods.
+
+Follow the repository's [latest README instructions](https://github.com/alchemyplatform/account-kit-expo-quickstart/blob/main/README.md) on how to set your API key and run the application.
+
+And you're off to the races!
+
+## Next Steps
+
+Next check out some additional features:
+
+
+
+ Learn how to perform transactions by sending user operations with your smart wallet.
+
+
+{" "}
+
+
+ Enable gasless transactions by setting up gas sponsorship for your users.
+
+
diff --git a/fern/wallets/pages/react-native/getting-started/getting-started-rn-bare.mdx b/fern/wallets/pages/react-native/getting-started/getting-started-rn-bare.mdx
new file mode 100644
index 000000000..2451b68d8
--- /dev/null
+++ b/fern/wallets/pages/react-native/getting-started/getting-started-rn-bare.mdx
@@ -0,0 +1,173 @@
+---
+title: Getting started with Smart Wallets on bare React Native
+description: A guide on integrating Smart Wallets within a Bare React Native application
+slug: wallets/react-native/getting-started/getting-started-rn-bare
+---
+
+
+ This guide assumes you already have a Bare React Native app and want to
+ integrate Smart Wallets. If you are starting a project from scratch, we
+ recommend following the [Expo
+ guide](/wallets/react-native/getting-started/getting-started-expo) instead.
+
+
+Here are the steps to get your environment setup for using Smart Wallets within a Bare React Native application.
+
+
+
+## Set up shims
+
+Once we've got our React Native project setup and running, we need to setup a few shims so we can use crypto libraries in React Native.
+
+### Install shim dependencies
+
+
+
+```bash npm
+npm install --save node-libs-react-native crypto-browserify stream-browserify react-native-get-random-values
+```
+
+```bash yarn
+yarn add node-libs-react-native crypto-browserify stream-browserify react-native-get-random-values
+```
+
+
+
+### Register shim modules in Metro
+
+Create or edit your `metro.config.js` file in the root of your project so that it includes the following:
+
+```js metro.config.js
+const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
+const path = require("path");
+const fs = require("fs");
+const projectRoot = __dirname; // <-- Adjust this as fits your project setup
+const config = getDefaultConfig(projectRoot);
+
+// Add aliases for file-system import based modules
+const ALIASES = {
+ "@noble/hashes/crypto": path.resolve(
+ projectRoot,
+ "node_modules/@noble/hashes/crypto.js",
+ ),
+ "@sinclair/typebox": path.resolve(
+ projectRoot,
+ "node_modules/@sinclair/typebox/build/cjs/index.js",
+ ),
+};
+
+config.resolver.extraNodeModules = {
+ ...config.resolver.extraNodeModules,
+ ...require("node-libs-react-native"),
+ crypto: require.resolve("crypto-browserify"),
+ stream: require.resolve("stream-browserify"),
+};
+
+config.resolver.resolveRequest = (context, moduleName, platform) => {
+ if (ALIASES[moduleName]) {
+ return {
+ filePath: ALIASES[moduleName],
+ type: "sourceFile",
+ };
+ }
+
+ // Handle .js/.jsx extensions on TypeScript files
+ if (
+ (moduleName.startsWith(".") || moduleName.startsWith("/")) &&
+ (moduleName.endsWith(".js") || moduleName.endsWith(".jsx"))
+ ) {
+ const moduleFilePath = path.resolve(
+ context.originModulePath,
+ "..",
+ moduleName,
+ );
+
+ // if the file exists, we won't remove extension, and we'll fall back to normal resolution.
+ if (!fs.existsSync(moduleFilePath)) {
+ return context.resolveRequest(
+ context,
+ moduleName.replace(/\.[^/.]+$/, ""),
+ platform,
+ );
+ }
+ }
+
+ return context.resolveRequest(context, moduleName, platform);
+};
+
+// Important to allow importing package exports
+config.resolver.unstable_enablePackageExports = true;
+
+config.resolver.unstable_conditionNames = [
+ "browser",
+ "require",
+ "react-native",
+];
+
+module.exports = config;
+```
+
+### Register global shims
+
+Import the following packages at the top of your `index.js` file so that libraries that depend on globals like `crypto` have access to them.
+
+```js index.js
+import "node-libs-react-native/globals.js";
+import "react-native-get-random-values";
+
+// rest of index.js
+```
+
+## Install Smart Wallets and build!
+
+That's it! Now you can install the packages you want from Smart Wallets and start building your React Native Account Abstraction app.
+
+
+
+```bash npm
+npm install -s @account-kit/react-native @account-kit/smart-contracts @account-kit/infra
+```
+
+```bash yarn
+yarn add @account-kit/react-native @account-kit/smart-contracts @account-kit/infra
+```
+
+
+
+
+The `@account-kit/react-native` package is an ESM module. As such, you might have to add the following to your `tsconfig.json`'s `compilerOptions`:
+
+```json
+"module": "NodeNext",
+"moduleResolution": "nodenext",
+```
+
+
+
+### Add supporting dependencies
+
+To ensure the Signer package works correctly, you'll need to add the following dependencies to your project:
+
+
+
+```bash npm
+npm install react-native-mmkv zustand abitype react-native-inappbrowser-reborn
+```
+
+```bash yarn
+yarn add react-native-mmkv zustand abitype react-native-inappbrowser-reborn
+```
+
+
+
+## Build and run your project
+
+Now that we've got everything setup, we can build our project!
+
+```bash
+npx react-native run-android
+```
+
+## Next Steps
+
+Move on to [app integration](/docs/wallets/react-native/getting-started/app-integration)!
diff --git a/fern/wallets/pages/react-native/overview.mdx b/fern/wallets/pages/react-native/overview.mdx
new file mode 100644
index 000000000..24ee4378b
--- /dev/null
+++ b/fern/wallets/pages/react-native/overview.mdx
@@ -0,0 +1,53 @@
+---
+title: Using within React Native applications
+description: A guide on integrating Smart Wallets within a React Native application
+slug: wallets/react-native/overview
+---
+
+import { ExpoIcon } from "../../components/icons/ExpoIcon";
+import { ReactNativeIcon } from "../../components/icons/ReactNativeIcon";
+
+We've built a simple example for reference purposes [here](https://github.com/alchemyplatform/aa-sdk/tree/main/examples/react-native-expo-example). This was built using Expo, but the same principles should apply to a bare React Native app as well.
+
+## Getting Started
+
+### Get up and running quickly using our Quickstart Template
+
+You can quickly spin up a new React Native (Expo) project setup with Account kit by using our [quickstart template](https://github.com/alchemyplatform/account-kit-expo-quickstart). Follow along with this guide:
+
+}
+ title="Quickstart with Expo"
+ href="/wallets/react-native/getting-started/getting-started-quickstart"
+>
+ Get started quickly using Smart Wallets on React Native with Expo
+
+
+### Adding Smart Wallets to a new or existing React Native project?
+
+
+ If you are starting a fresh project, we highly recommend using Expo. You can
+ easily create a new Expo project by running `npx create-expo-app@latest` from
+ your terminal. Visit the [Expo
+ docs](https://docs.expo.dev/get-started/create-a-project/) for more
+ information.
+
+
+Whether you're using Expo or a bare React Native app, follow our guides to get started with using Smart Wallets in your project.
+
+
+ }
+ title="Expo (recommended)"
+ href="/wallets/react-native/getting-started/getting-started-expo"
+ >
+ Get started with using Smart Wallets in your Expo project
+
+ }
+ title="Bare React Native"
+ href="/wallets/react-native/getting-started/getting-started-rn-bare"
+ >
+ Get started with using Smart Wallets in your bare React Native project
+
+
diff --git a/fern/wallets/pages/react/add-passkey.mdx b/fern/wallets/pages/react/add-passkey.mdx
new file mode 100644
index 000000000..6315d3ad1
--- /dev/null
+++ b/fern/wallets/pages/react/add-passkey.mdx
@@ -0,0 +1,65 @@
+---
+title: Add Passkey
+description: Learn how to add a passkey to your users' accounts with Smart Wallets.
+slug: wallets/react/add-passkey
+---
+
+You may have noticed in the [Demo App](https://demo.alchemy.com/) that you can allow a user to log in with an existing passkey, but there's no way to sign-up with a passkey. This guide will outline
+how to add a passkey to a user's account after they've signed up.
+
+If you're not sure how to authenticate your users, see [this guide](/wallets/react/getting-started).
+
+## Add a passkey on sign up
+
+The easiest way to add a passkey to users is right when they signup! This can be done by updating your `ui` config in the `createConfig` call to enable the UI components to prompt the user for a passkey.
+
+```ts twoslash config.ts
+// @noErrors
+import { createConfig } from "@account-kit/react";
+import { sepolia } from "@account-kit/infra";
+
+export const config = createConfig(
+ {
+ chain: sepolia,
+ apiKey: "ALCHEMY_API_KEY",
+ },
+ {
+ illustrationStyle: "outline",
+ auth: {
+ sections: [[{ type: "email" }], [{ type: "passkey" }]],
+ addPasskeyOnSignup: true, // [!code ++]
+ },
+ },
+);
+```
+
+Now, when a user signs up, they will be prompted to create a passkey.
+
+## Add a passkey later
+
+With the above config, the user will only be prompted when they first create their account in your app. If you want to add the ability to add a passkey elsewhere in your app, for example in a settings page, you can use the [`useAddPasskey`](/wallets/reference/account-kit/react/hooks/useAddPasskey) hook.
+
+
+ This hook requires that the user already be authenticated or else it will
+ throw an error!
+
+
+```tsx twoslash
+import React from "react";
+import { useAddPasskey } from "@account-kit/react";
+
+export default function MyComponent() {
+ const { addPasskey, isAddingPasskey } = useAddPasskey();
+
+ return (
+ {
+ addPasskey();
+ }}
+ >
+ Add Passkey
+
+ );
+}
+```
diff --git a/fern/wallets/pages/react/connectors/customization.mdx b/fern/wallets/pages/react/connectors/customization.mdx
new file mode 100644
index 000000000..7309a0690
--- /dev/null
+++ b/fern/wallets/pages/react/connectors/customization.mdx
@@ -0,0 +1,94 @@
+---
+title: Styling Connectors
+description: Customize external wallet connectors including ordering and features wallets
+slug: wallets/react/connectors/customization
+---
+
+### External wallets (EVM + Solana)
+
+Use `configForExternalWallets()` to specify external wallets you want to support, then add to your UI configuration. This allows you to feature wallets, merge EVM/Solana variants by name, and enable WalletConnect.
+
+```ts twoslash
+import {
+ createConfig,
+ cookieStorage,
+ configForExternalWallets,
+} from "@account-kit/react";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { Connection } from "@solana/web3.js";
+
+export const externalWalletsConfig = configForExternalWallets({
+ // Preferred order (case-insensitive). Use "wallet_connect" for WalletConnect.
+ wallets: ["wallet_connect", "phantom", "metamask", "coinbase wallet"],
+ // Which chains to surface (filter to ["evm"] or ["svm"] if needed)
+ chainType: ["svm", "evm"],
+ // EVM-only
+ walletConnectProjectId: "your-project-id",
+ // Built-in Featured section controls
+ hideMoreButton: false,
+ numFeaturedWallets: 4,
+});
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ storage: cookieStorage,
+ solana: {
+ connection: new Connection(
+ `https://solana-devnet.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`,
+ ),
+ adapters: externalWalletsConfig.adapters,
+ // optional
+ policyId: process.env.NEXT_PUBLIC_SOLANA_POLICY_ID,
+ },
+ connectors: externalWalletsConfig.connectors,
+ },
+ {
+ auth: {
+ sections: [
+ [{ type: "email" }],
+ [
+ { type: "passkey" },
+ { type: "social", authProviderId: "google", mode: "popup" },
+ ],
+ [
+ // Spread the pre-configured UI for external wallets
+ { type: "external_wallets", ...externalWalletsConfig.uiConfig },
+ ],
+ ],
+ },
+ },
+);
+```
+
+You can still pass EVM `connectors` and Solana `adapters` directly into `createConfig()` without the helper.
+
+## How it behaves
+
+How the external wallets UI behaves:
+
+* Featured: uses your `wallets` order; EVM then Solana for the same name (counts once); capped by `numFeaturedWallets`
+* All Wallets: same ordering; detected wallets not in `wallets` are appended
+* Filtering: respects `chainType` (e.g., `["svm"]` hides EVM + WalletConnect)
+* WalletConnect: EVM‑only; shown when `walletConnectProjectId` is set and `chainType` includes `"evm"`
+
+## Programmatic usage
+
+Programmatic flows when you want to bypass the modal:
+
+```ts twoslash
+import { useConnect } from "@account-kit/react";
+
+const { connect, connectors } = useConnect();
+const metaMask = connectors.find((c) => c.id === "metaMask");
+await connect({ connector: metaMask! });
+```
+
+```ts twoslash
+import { useSolanaWallet } from "@account-kit/react";
+
+const { select, wallets } = useSolanaWallet();
+const phantom = wallets.find((w) => w.name.toLowerCase() === "phantom");
+if (phantom) await select(phantom.adapter.name);
+```
diff --git a/fern/wallets/pages/react/customization/tailwind-setup.mdx b/fern/wallets/pages/react/customization/tailwind-setup.mdx
new file mode 100644
index 000000000..19ebfcc42
--- /dev/null
+++ b/fern/wallets/pages/react/customization/tailwind-setup.mdx
@@ -0,0 +1,229 @@
+---
+title: Tailwind CSS Setup
+description: Complete guide to setting up Tailwind CSS with UI components
+slug: wallets/react/tailwind-setup
+---
+
+To use pre-built UI components for user login Tailwind CSS must be configured in your project. This guide walks you through the complete setup process for both new and existing Tailwind installations.
+
+## Prerequisites
+
+* React 18+
+* TypeScript 5+
+* Node.js 16+
+
+## Quick Setup (New Tailwind Installation)
+
+If you don't have Tailwind CSS in your project yet, follow these steps:
+
+### 1. Install Dependencies
+
+
+ ```bash yarn
+ yarn add -D tailwindcss @tailwindcss/postcss postcss
+ ```
+
+ ```bash npm
+ npm install -D tailwindcss @tailwindcss/postcss postcss
+ ```
+
+ ```bash pnpm
+ pnpm add -D tailwindcss @tailwindcss/postcss postcss
+ ```
+
+
+### 2. Configure PostCSS
+
+Create a `postcss.config.mjs` file in your project root:
+
+```js postcss.config.mjs
+export default {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+```
+
+### 3. Create Tailwind Config
+
+Create a `tailwind.config.ts` file using the following Tailwind plugin:
+
+```ts tailwind.config.ts
+import { withAccountKitUi } from "@account-kit/react/tailwind";
+
+export default withAccountKitUi(
+ {
+ // Your existing Tailwind config (if any)
+ content: [
+ "./src/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ },
+ {
+ // Account Kit UI theme customizations (optional)
+ // See customization guide below for available options
+ },
+);
+```
+
+### 4. Import Tailwind Styles
+
+Create or update your global CSS file to include Tailwind:
+
+
+
+ ```css app/globals.css
+ @import "tailwindcss";
+ @config '../tailwind.config.ts';
+ ```
+
+
+
+ ```css app/globals.css
+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;
+ ```
+
+
+
+## Existing Tailwind Installation
+
+If you already have Tailwind CSS configured in your project:
+
+### 1. Update Your Tailwind Config
+
+Wrap your existing configuration with Account Kit's plugin:
+
+```ts tailwind.config.ts
+import { withAccountKitUi } from "@account-kit/react/tailwind";
+
+export default withAccountKitUi(
+ {
+ // Your existing Tailwind config - DON'T replace, just wrap!
+ content: [
+ "./src/**/*.{js,ts,jsx,tsx,mdx}",
+ // ... your existing content patterns
+ ],
+ theme: {
+ // ... your existing theme customizations
+ },
+ plugins: [
+ // ... your existing plugins
+ ],
+ },
+ {
+ // Account Kit UI theme customizations
+ },
+);
+```
+
+### 2. Update CSS Import (Tailwind v4 only)
+
+If using Tailwind v4, update your CSS to reference the config:
+
+```css app/globals.css
+@import "tailwindcss";
+@config '../tailwind.config.ts';
+```
+
+## Framework-Specific Setup
+
+### Next.js
+
+For Next.js projects, make sure to import your global CSS in `app/layout.tsx`:
+
+```tsx app/layout.tsx
+import "./globals.css";
+
+// ... rest of your layout
+```
+
+### Create React App
+
+Import your global CSS in `src/index.js` or `src/index.tsx`:
+
+```tsx src/index.tsx
+import "./index.css";
+
+// ... rest of your index file
+```
+
+### Vite
+
+Import your global CSS in `src/main.tsx`:
+
+```tsx src/main.tsx
+import "./index.css";
+
+// ... rest of your main file
+```
+
+## Testing Your Setup
+
+To verify Tailwind is working correctly with Account Kit:
+
+1. Start your development server
+2. Add a simple Smart Wallets UI component to test:
+
+```tsx
+import { useAuthModal } from "@account-kit/react";
+
+export function TestComponent() {
+ const { openAuthModal } = useAuthModal();
+
+ return (
+
+ Test Auth Modal
+
+ );
+}
+```
+
+3. The button should render with pre-built styling
+
+## Troubleshooting
+
+### Styles Not Loading
+
+If UI components appear unstyled:
+
+1. **Verify CSS import**: Make sure you're importing your global CSS file
+2. **Check config path**: Ensure the `@config` path in your CSS matches your config file location
+3. **Restart dev server**: Sometimes a restart is needed after config changes
+4. **Verify content paths**: Make sure your Tailwind content array includes all component files
+
+### Build Errors
+
+If you encounter build errors:
+
+1. **TypeScript config**: Ensure your `tsconfig.json` includes the Tailwind config file
+2. **PostCSS version**: Make sure you're using compatible PostCSS versions
+3. **Import paths**: Verify all import paths in your config are correct
+
+### Dark Mode Issues
+
+If dark mode isn't working correctly:
+
+1. **Color sets**: Ensure you're using `createColorSet` for colors that should change between modes
+2. **CSS variables**: Check that CSS custom properties are being generated correctly
+3. **Theme detection**: Verify your app has proper dark mode detection
+
+## Next Steps
+
+After setting up Tailwind CSS:
+
+* [Customize your theme](/wallets/react/customization/theme) with colors and styling
+* [Set up authentication](/wallets/react/quickstart) in your React app
+* [Use UI components](/wallets/react/ui-components) for pre-built authentication flows
+* [Explore React hooks](/wallets/react/react-hooks) for custom UI implementations
+
+## Need Help?
+
+If you're still having issues with Tailwind setup:
+
+1. Check the [interactive demo](https://demo.alchemy.com/) for working configuration examples
+2. Review the [troubleshooting guide](/wallets/resources/faqs)
+3. Join our [Discord community](https://discord.gg/alchemy) for support
diff --git a/fern/wallets/pages/react/customization/theme.mdx b/fern/wallets/pages/react/customization/theme.mdx
new file mode 100644
index 000000000..6a79811c2
--- /dev/null
+++ b/fern/wallets/pages/react/customization/theme.mdx
@@ -0,0 +1,248 @@
+---
+title: Custom theme
+description: Customize the theme of your Smart Wallets app
+slug: wallets/react/customization/theme
+---
+
+UI components are fully customizable so you can match your app's branding.
+
+What you can customize in the pre-built authentication component:
+
+* Overall theme: colors, radius, illustrations, custom css
+* Header: text, logo, custom component
+* External wallet connectors: featured wallets, specify order, chain filtering (EVM/Solana), WalletConnect, and more via `configForExternalWallets()`. See [EOA connectors](/wallets/react/login-methods/eoa-login) for more details.
+
+
+ **Need to set up Tailwind first?** See the [complete Tailwind setup
+ guide](/wallets/react/tailwind-setup) for installation and configuration
+ instructions.
+
+
+
+ The easiest way to configure your UI components, is using the [Demo
+ App](https://demo.alchemy.com/).
+
+ Preview your branding and autogenerate a styled component using the [Demo
+ App](https://demo.alchemy.com/). You can simply copy and paste the code into
+ your project.
+
+
+# Theme
+
+Theme customizations and styling are passed through `withAccountKitUi`, the Account Kit Tailwind plugin, in your `tailwind.config.js` file.
+
+## Colors
+
+The [Account Kit Theme](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/react/src/tailwind/types.ts) object passed to `withAccountKitUi` supports an overridable `colors` object which accepts a set of color values. Each color is a key-value pair where the key is the name of the color and the value is an object containing the `light` and `dark` mode value to use.
+
+### Border colors
+
+* `active` - the color of the border when the input is focused
+* `static` - the color of the border when the input is not focused
+* `critical` - the color of the border when the input is in an error state
+
+### Button colors
+
+These colors affect the background of buttons
+
+* `btn-primary` - the color of the primary button
+* `btn-secondary` - the color of the secondary button
+* `btn-auth` - the color of the authentication button
+
+### Foreground colors
+
+These colors primarily affect the text color of the components
+
+* `fg-primary` - the color of the primary text
+* `fg-secondary` - the color of the secondary text
+* `fg-tertiary` - the color of the tertiary text
+* `fg-invert` - the color of the inverted text
+* `fg-disabled` - the color of the disabled text
+* `fg-accent-brand` - your brand color
+* `fg-critical` - the color of the critical text
+
+### Surface colors
+
+These colors affect the background of various components (eg. modal, inputs, etc)
+
+* `bg-surface-default` - the default background color
+* `bg-surface-subtle` - a subtle background color
+* `bg-surface-inset` - the background color of inset components
+* `bg-surface-critical` - the background color of critical components
+* `bg-surface-error` - the background color of error components
+* `bg-surface-success` - the background color of success components
+* `bg-surface-warning` - the background color of warning components
+
+### Example
+
+```ts twoslash
+import { withAccountKitUi, createColorSet } from "@account-kit/react/tailwind";
+
+export const tailwindConfig = withAccountKitUi(
+ {
+ content: [],
+ // your tailwind config
+ },
+ {
+ colors: {
+ active: createColorSet("#94A3B8", "#94A3B8"),
+ },
+ },
+);
+```
+
+### Customizing Multiple Colors
+
+You can customize multiple color properties at once for comprehensive theming:
+
+```ts tailwind.config.ts
+import { withAccountKitUi, createColorSet } from "@account-kit/react/tailwind";
+
+export default withAccountKitUi(
+ {
+ content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
+ },
+ {
+ colors: {
+ "btn-primary": createColorSet("#3b82f6", "#1d4ed8"),
+ "fg-accent-brand": createColorSet("#3b82f6", "#60a5fa"),
+ active: createColorSet("#94a3b8", "#94a3b8"),
+ },
+ },
+);
+```
+
+### Available Color Variables
+
+Account Kit supports these color customizations:
+
+**Border Colors:**
+
+* `active` - Border color when input is focused
+* `static` - Border color when input is not focused
+* `critical` - Border color for error states
+
+**Button Colors:**
+
+* `btn-primary` - Primary button background
+* `btn-secondary` - Secondary button background
+* `btn-auth` - Authentication button background
+
+**Text Colors:**
+
+* `fg-primary` - Primary text color
+* `fg-secondary` - Secondary text color
+* `fg-tertiary` - Tertiary text color
+* `fg-invert` - Inverted text color
+* `fg-disabled` - Disabled text color
+* `fg-accent-brand` - Brand accent color
+* `fg-critical` - Critical/error text color
+
+**Background Colors:**
+
+* `bg-surface-default` - Default background
+* `bg-surface-subtle` - Subtle background
+* `bg-surface-inset` - Inset component background
+* `bg-surface-critical` - Critical component background
+* `bg-surface-error` - Error component background
+* `bg-surface-success` - Success component background
+* `bg-surface-warning` - Warning component background
+
+## Borders
+
+Similar to colors, the Account Theme object passed to `withAccountKitUi` supports an overridable `borderRadius` field to customize the border radius size. The default value is `sm` which is an `8px` border radius.
+
+```ts twoslash
+import { withAccountKitUi } from "@account-kit/react/tailwind";
+
+export const tailwindConfig = withAccountKitUi(
+ {
+ content: [],
+ // your tailwind config
+ },
+ {
+ borderRadius: "md",
+ },
+);
+```
+
+The available options are:
+
+* `none` (0px)
+* `xs` (4px)
+* `sm` (8px)
+* `md` (16px)
+* `lg` (24px)
+
+## Illustration styles
+
+Unlike colors and border radius, illustration styles are not passed through the Tailwind plugin.
+
+Customize the illustration style of various icons used in the components by passing one of the [enum values](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/react/src/types.ts) to `illustrationStyle` in your `uiConfig` when you call `createConfig`.
+
+```ts twoslash
+import { createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "YOUR_KEY" }),
+ chain: sepolia,
+ },
+ {
+ // ... other ui config options
+ illustrationStyle: "outline",
+ },
+);
+```
+
+# Branding the header
+
+To customize the header, you can override either of the `header` or `hideSignInText` options of the `auth` config when calling `createConfig`.
+
+## Changing the header text
+
+The auth modal, by default, shows a `Sign in` text header.
+
+In this example, we hide the default header text and replace it with our own.
+
+```tsx twoslash
+// @jsx: react-jsx
+import { createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ },
+ {
+ auth: {
+ header: "Sign in with your account", // [!code ++]
+ hideSignInText: true, // [!code ++]
+ },
+ },
+);
+```
+
+## Adding an icon
+
+In this example, we leave the `Sign in` text in the modal and add an icon above it.
+
+```tsx twoslash
+// @jsx: react-jsx
+import { createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ },
+ {
+ auth: {
+ header: , // [!code ++]
+ },
+ },
+);
+```
diff --git a/fern/wallets/pages/react/getting-started/existing-project.mdx b/fern/wallets/pages/react/getting-started/existing-project.mdx
new file mode 100644
index 000000000..222e4c946
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/existing-project.mdx
@@ -0,0 +1,167 @@
+---
+title: App Integration
+description: Learn how to integrate Alchemy Smart Wallets into your existing React application with embedded wallets and authentication.
+text: App Integration
+link: /react/quickstart/existing-project
+slug: wallets/react/quickstart/existing-project
+---
+
+## Initializing Alchemy Provider
+
+Wrap your application with the Alchemy Provider to enable embedded wallet functionality.
+
+### 1. Create a file: `providers.tsx`
+
+```tsx app/providers.tsx
+"use client";
+import { config, queryClient } from "@/config";
+import {
+ AlchemyAccountProvider,
+ AlchemyAccountsProviderProps,
+} from "@account-kit/react";
+import { QueryClientProvider } from "@tanstack/react-query";
+import { PropsWithChildren } from "react";
+
+export const Providers = (
+ props: PropsWithChildren<{
+ initialState?: AlchemyAccountsProviderProps["initialState"];
+ }>,
+) => {
+ return (
+
+
+ {props.children}
+
+
+ );
+};
+```
+
+### 2. Update your `layout.tsx`
+
+```tsx app/layout.tsx
+import { config } from "@/config";
+import { cookieToInitialState } from "@account-kit/core";
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import { headers } from "next/headers";
+import "./globals.css";
+import { Providers } from "./providers";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "My App with Embedded Wallets",
+ description: "My app with Alchemy Smart Wallets integration",
+};
+
+export default async function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ const headersList = await headers();
+ const initialState = cookieToInitialState(
+ config,
+ headersList.get("cookie") ?? undefined,
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+```
+
+### 3. Add authentication to your app
+
+Now you can use the Alchemy React components to add wallet authentication anywhere in your app.
+
+**Example page with login functionality**
+
+```tsx app/page.tsx
+"use client";
+import {
+ useAuthModal,
+ useLogout,
+ useSignerStatus,
+ useUser,
+} from "@account-kit/react";
+
+export default function Home() {
+ const user = useUser();
+ const { openAuthModal } = useAuthModal();
+ const signerStatus = useSignerStatus();
+ const { logout } = useLogout();
+
+ return (
+
+ {signerStatus.isInitializing ? (
+ <>Loading...>
+ ) : user ? (
+
+
Success!
+ You're logged in as {user.email ?? "anon"}.
+ logout()}
+ >
+ Log out
+
+
+ ) : (
+
+ Login
+
+ )}
+
+ );
+}
+```
+
+Now that you have basic authentication working, you can explore additional features:
+
+
+
+ Learn how to perform transactions by sending user operations with your smart wallet.
+
+
+{" "}
+
+
+ Customize and style the UI components to match your application's design.
+
+
+{" "}
+
+
+ Enable gasless transactions by setting up gas sponsorship for your users.
+
+
+
+ Enhance security by implementing multi-factor authentication for your users.
+
+
diff --git a/fern/wallets/pages/react/getting-started/initialization.mdx b/fern/wallets/pages/react/getting-started/initialization.mdx
new file mode 100644
index 000000000..3ec108ae1
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/initialization.mdx
@@ -0,0 +1,44 @@
+---
+title: Initialization
+description: Build Alchemy Smart Wallets in a new app
+slug: wallets/react/installation
+---
+
+In this doc and the three following we will build a Next.js application with Alchemy Smart Wallets from scratch. If you're using any other tech stack, follow along with the key points of integration and adjust as needed!
+
+## Create a new Next.js app
+
+Start by initializing a new [Next.js app](https://nextjs.org/). Run:
+
+```bash
+npx create-next-app@latest \
+ --typescript \
+ --tailwind \
+ --app
+```
+
+This command passes in flags to use Typescript, Tailwind and the next app router. The following docs in this guide will expect you to use these.
+
+## Install Dependencies
+
+You will need these three libraries:
+
+* **@account-kit/infra**: Core interfaces and functions for Smart Wallets ([learn more](/docs/wallets/reference/account-kit/infra))
+* **@account-kit/react**: React Hooks, components and utilities for Smart Wallets ([learn more](/docs/wallets/reference/account-kit/react))
+* **@tanstack/react-query**: A required async state library to make smart wallet react hooks easier to use ([learn more about this library's motivation here](https://tanstack.com/query/latest/docs/framework/react/overview))
+
+Go ahead and install them to the project with a single command:
+
+
+ ```bash npm
+ npm install @account-kit/infra @account-kit/react @tanstack/react-query
+ ```
+
+ ```bash pnpm
+ pnpm add @account-kit/infra @account-kit/react @tanstack/react-query
+ ```
+
+ ```bash yarn
+ yarn add @account-kit/infra @account-kit/react @tanstack/react-query
+ ```
+
diff --git a/fern/wallets/pages/react/getting-started/quickstart-at-top.mdx b/fern/wallets/pages/react/getting-started/quickstart-at-top.mdx
new file mode 100644
index 000000000..a7846b9c8
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/quickstart-at-top.mdx
@@ -0,0 +1,9 @@
+---
+title: React Quickstart
+description: Learn how to get started with Alchemy Smart Wallets in React.
+text: Send user operations
+link: /react/send-user-operations
+slug: wallets/quickstart
+---
+
+
diff --git a/fern/wallets/pages/react/getting-started/quickstart-content.mdx b/fern/wallets/pages/react/getting-started/quickstart-content.mdx
new file mode 100644
index 000000000..fa9f4de75
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/quickstart-content.mdx
@@ -0,0 +1,66 @@
+Create a new app with embedded wallets, social login, and gasless transactions.
+
+
+
+> You can also follow the [Quickstart in the Dashboard](https://dashboard.alchemy.com/services/smart-wallets/quickstart).
+
+## 1. Clone template repo
+
+```bash
+npx create-next-app my-smart-wallets-app -e https://github.com/alchemyplatform/smart-wallets-quickstart
+cd my-smart-wallets-app
+```
+
+## 2. Set up environment
+
+Once you have your project cloned down and you are in the `my-smart-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
+NEXT_PUBLIC_ALCHEMY_API_KEY=YOUR_ALCHEMY_API_KEY
+NEXT_PUBLIC_ALCHEMY_POLICY_ID=YOUR_PAYMASTER_POLICY_ID
+```
+
+Remember: you must create a [`Configuration`](https://dashboard.alchemy.com/services/smart-wallets/configuration) and a [`Paymaster Policy`](https://dashboard.alchemy.com/services/gas-manager/configuration) if you want to use Smart Wallets.
+
+
+ Both the `Configuration` and the `Policy ID` must be linked to the application
+ behind your API Key!
+
+
+## 3. Run app
+
+In your terminal, run:
+
+```bash
+npm run dev
+```
+
+Your `localhost:3000` should now display the following:
+
+
+
+## 4. Log in and view the app
+
+You can use a service like [Temp Mail](https://temp-mail.org/en/) to test logins with throwaway email accounts.
+
+> Note: Social login will not work just yet!
+
+Once you use an email, you will receive a 6-digit authentication code. Copy-paste it into the following screen:
+
+
+
+Once you have signed in, you can mint your own NFT! 🎉
+
+
+
+## 5. Next steps
+
+You're all set to [take your app to the next level with transactions!](/docs/wallets/transactions/send-transactions)
+
+In the Dashboard, you can set your app up with:
+
+* [Swaps!](/docs/wallets/transactions/swap-tokens)
+* Social login methods
+* Gas sponsorship
+* Customized styling
diff --git a/fern/wallets/pages/react/getting-started/quickstart-in-section.mdx b/fern/wallets/pages/react/getting-started/quickstart-in-section.mdx
new file mode 100644
index 000000000..51311ba5e
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/quickstart-in-section.mdx
@@ -0,0 +1,9 @@
+---
+title: React Quickstart
+description: Learn how to get started with Alchemy Smart Wallets in React.
+text: Send user operations
+link: /react/send-user-operations
+slug: wallets/react/quickstart
+---
+
+
diff --git a/fern/wallets/pages/react/getting-started/setup.mdx b/fern/wallets/pages/react/getting-started/setup.mdx
new file mode 100644
index 000000000..332e5fe3d
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/setup.mdx
@@ -0,0 +1,20 @@
+---
+title: Environment Setup
+description: How to set up Alchemy Smart Wallets using the Alchemy Dashboard.
+slug: wallets/react/setup
+---
+
+To use Smart Wallets, you need to:
+
+1. Create an app in the [dashboard](https://dashboard.alchemy.com/signup) and copy the **API Key**.
+
+2. Create a **`configuration`** in the [`Smart Wallets` dashboard](https://dashboard.alchemy.com/services/smart-wallets/configuration) to enable login methods.
+
+3. Create a policy in your [gas manager dashboard](https://dashboard.alchemy.com/services/gas-manager/configuration) to set rules for sponsorship.
+
+Create a `.env` at root of your project:
+
+```
+NEXT_PUBLIC_ALCHEMY_API_KEY=YOUR_ALCHEMY_API_KEY
+NEXT_PUBLIC_ALCHEMY_POLICY_ID=YOUR_PAYMASTER_POLICY_ID
+```
diff --git a/fern/wallets/pages/react/getting-started/ui-customization.mdx b/fern/wallets/pages/react/getting-started/ui-customization.mdx
new file mode 100644
index 000000000..2e433fae8
--- /dev/null
+++ b/fern/wallets/pages/react/getting-started/ui-customization.mdx
@@ -0,0 +1,128 @@
+---
+title: UI Customization
+description: Learn how to customize the login UI for smart wallets
+text: UI Customization
+link: /react/quickstart/ui-customization
+slug: wallets/react/quickstart/ui-customization
+---
+
+
+ This guide assumes you're using Next.js, if you're using a different framework
+ or need more info checkout our [full tailwind setup
+ guide](/docs/wallets/react/tailwind-setup)
+
+
+Create two configuration files that will customize your authentication methods and UI styles.
+
+### Create your configuration files
+
+1. In your project root, create a `config.ts` file
+2. In your project root, create a `tailwind.config.ts` file
+
+### Basic configuration example
+
+Start with a basic configuration:
+
+**tailwind.config.ts**
+
+```ts tailwind.config.ts
+import { withAccountKitUi } from "@account-kit/react/tailwind";
+
+export default withAccountKitUi(
+ {
+ // Your existing Tailwind config (if already using Tailwind).
+ // If using Tailwind v4, this will likely be left empty.
+ },
+ {
+ // AccountKit UI theme customizations
+ },
+);
+```
+
+
+ **Important:** If your `tailwind.config.ts` already contains any existing
+ config information, be sure to wrap it with `withAccountKitUi` as shown above.
+ Don't replace your existing config - just wrap it!
+
+
+Update your `global.css` to include the config:
+
+```css global.css
+@import "tailwindcss";
+@config '../../tailwind.config.ts';
+```
+
+
+ **Note:** If still using Tailwind v3, skip this step as the
+ `tailwind.config.ts` file is used by default.
+
+
+**config.ts**
+
+```ts twoslash src/config.ts
+// @noErrors
+import { createConfig, cookieStorage } from "@account-kit/react";
+import { QueryClient } from "@tanstack/react-query";
+import { arbitrumSepolia, alchemy } from "@account-kit/infra";
+
+export const config = createConfig(
+ {
+ transport: alchemy({
+ apiKey: process.env.NEXT_PUBLIC_ALCHEMY_API_KEY,
+ }),
+ chain: arbitrumSepolia,
+ ssr: true,
+ storage: cookieStorage,
+ enablePopupOauth: true,
+ // For gas sponsorship
+ // Learn more here: https://www.alchemy.com/docs/wallets/transactions/sponsor-gas
+ policyId: process.env.NEXT_PUBLIC_ALCHEMY_POLICY_ID,
+ },
+ {
+ auth: {
+ sections: [
+ [{ type: "email" }],
+ [
+ { type: "passkey" },
+ { type: "social", authProviderId: "google", mode: "popup" },
+ ],
+ ],
+ addPasskeyOnSignup: true,
+ },
+ },
+);
+
+export const queryClient = new QueryClient();
+```
+
+
+ **Important:** The chain you import (like `arbitrumSepolia`) must come from
+ the `@account-kit/infra` package, not from `viem`. Additionally, make sure
+ this chain is enabled in both your Alchemy app and Smart Wallets config policy
+ in the dashboard.
+
+
+Note:
+You can add an `"external_wallets"` auth method to your config to allow connecting existing external EOAs (such as MetaMask) using our built-in connectors and WalletConnect. Learn more [here](/wallets/react/login-methods/eoa-login).
+
+```ts twoslash
+// @noErrors
+[
+ {
+ type: "external_wallets",
+ walletConnect: { projectId: "your-project-id" },
+ },
+];
+```
+
+### Customization
+
+Customize both configuration files by visiting our [demo app](https://demo.alchemy.com/) - use the interactive sandbox to explore different authentication methods and styling options. When ready, click 'Code preview' to export your customized configuration files.
+
+
+ You can also follow these links to learn more about [using UI
+ Components](/docs/wallets/react/ui-components) and
+ [theming](/docs/wallets/react/ui-components).
+
+
+Finally, to the last step: integration into the application.
diff --git a/fern/wallets/pages/react/login-methods/adding-and-removing-login-methods.mdx b/fern/wallets/pages/react/login-methods/adding-and-removing-login-methods.mdx
new file mode 100644
index 000000000..82ca72604
--- /dev/null
+++ b/fern/wallets/pages/react/login-methods/adding-and-removing-login-methods.mdx
@@ -0,0 +1,195 @@
+---
+title: Adding and Removing Login Methods
+description: Learn how to add and remove login methods to an account
+slug: wallets/signer/authentication/adding-and-removing-login-methods
+---
+
+If your user has already authenticated with email, social auth, or a passkey, you can add additional login methods to their account or remove currently enabled methods from their account. This is useful in the case that you want to give your users the ability to customize their login methods after their account is created.
+
+## Viewing the currently enabled auth methods
+
+To view the enabled auth methods for the currently logged in user, use the `useListAuthMethods` hook:
+
+```tsx twoslash
+import { useListAuthMethods } from "@account-kit/react";
+
+export default function MyPage() {
+ const { data: authMethods } = useListAuthMethods();
+
+ if (!authMethods) {
+ return
+ Passkey created at: {new Date(passkey.createdAt).toLocaleString()}
+
+ ))}
+
+ );
+}
+```
+
+## Setting email auth
+
+To set an account's email, use the `useSendVerificationCode` and `useSetEmail` hooks. Emails must be verified before they are set.
+
+
+ An account may have at most one email address associated with it. If an
+ account already has an email and you call this function, then the existing
+ email will be removed.
+
+
+```tsx twoslash
+import { useSetEmail, useSendVerificationCode } from "@account-kit/react";
+
+export default function MyPage() {
+ const { sendVerificationCode } = useSendVerificationCode();
+ const { setEmail } = useSetEmail();
+
+ return (
+ <>
+ {
+ sendVerificationCode({
+ type: "email",
+ contact: "user@example.com",
+ });
+ }}
+ >
+ Send verification code
+
+
+ setEmail({
+ verificationCode: "code user received in email",
+ })
+ }
+ >
+ Update email
+
+ >
+ );
+}
+```
+
+## Removing email auth
+
+To remove an account's email, use the `useRemoveEmail` hook.
+
+```tsx twoslash
+import { useRemoveEmail } from "@account-kit/react";
+
+export default function MyPage() {
+ const { removeEmail } = useRemoveEmail();
+
+ return removeEmail()}>Remove email;
+}
+```
+
+Note that you cannot remove the last auth method from an account. If removing email login would leave no auth methods, then this call will fail.
+
+## Adding OAuth providers
+
+To add an OAuth provider, use the `useAddOauthProvider` hook.
+
+```tsx twoslash
+import { useAddOauthProvider } from "@account-kit/react";
+
+export default function MyPage() {
+ const { addOauthProvider } = useAddOauthProvider();
+
+ return (
+
+ addOauthProvider({
+ authProviderId: "google",
+ mode: "popup",
+ })
+ }
+ >
+ Add Google
+
+ );
+}
+```
+
+When this button is clicked, the user will be prompted to log in to a social account. Once they do, that account will be added as an auth method to the current user.
+
+The options that can be passed to `addOauthProvider` match those which can be passed to `authenticate` when logging in with an OAuth provider. For information on these options and the required setup for enabling OAuth providers, see the [Social Login](/wallets/authentication/login-methods/social-login) documentation.
+
+## Removing OAuth providers
+
+To remove an OAuth provider, use the `removeOauthProvider()` hook, then pass the OAuth provider's provider id to `removeOauth`. To find the provider id, you can examine the value returned from `useListAuthMethods()`.
+
+```tsx twoslash
+import { useListAuthMethods, useRemoveOauthProvider } from "@account-kit/react";
+
+export default function MyPage() {
+ const { data: authMethods } = useListAuthMethods();
+ const { removeOauthProvider } = useRemoveOauthProvider();
+
+ if (!authMethods) {
+ return
Loading…
;
+ }
+
+ const removeFirstOauthProvider = () => {
+ removeOauthProvider(authMethods.oauthProviders[0].providerId);
+ };
+
+ return (
+ Remove OAuth provider
+ );
+}
+```
+
+Note that you cannot remove the last auth method from an account. If removing a social login would leave no auth methods, then this call will fail.
+
+## Adding passkeys
+
+To add a passkey, use the `useAddPasskey` hook.
+
+```tsx twoslash
+import { useAddPasskey } from "@account-kit/react";
+
+export default function MyPage() {
+ const { addPasskey } = useAddPasskey();
+
+ return addPasskey()}>Add passkey;
+}
+```
+
+This will prompt the user to create a passkey which will then be added as a login method to the account.
+
+## Removing passkeys
+
+To remove a passkey, use the `useRemovePasskey` hook and pass the passkey's authenticator id to the `removePasskey` function. To find the authenticator id, you can examine the value returned from `useListAuthMethods()`.
+
+```tsx twoslash
+import { useListAuthMethods, useRemovePasskey } from "@account-kit/react";
+
+export default function MyPage() {
+ const { data: authMethods } = useListAuthMethods();
+ const { removePasskey } = useRemovePasskey();
+
+ if (!authMethods) {
+ return
Loading…
;
+ }
+
+ const removeFirstPasskey = () => {
+ removePasskey(authMethods.passkeys[0].authenticatorId);
+ };
+
+ return Remove passkey;
+}
+```
+
+Note that you cannot remove the last auth method from an account. If removing a passkey would leave no auth methods, then this call will fail.
diff --git a/fern/wallets/pages/react/login-methods/eoa-login.mdx b/fern/wallets/pages/react/login-methods/eoa-login.mdx
new file mode 100644
index 000000000..3085511e7
--- /dev/null
+++ b/fern/wallets/pages/react/login-methods/eoa-login.mdx
@@ -0,0 +1,186 @@
+---
+title: Connect external wallets
+description: How to connect external wallets on EVM and Solana
+slug: wallets/react/login-methods/eoa-login
+---
+
+# Overview
+
+Connectors let users authenticate with existing **external wallets**. We support both **EVM** (e.g., MetaMask, Coinbase, WalletConnect) and **Solana** (e.g., Phantom) wallets via UI components or custom UI. and can surface them together in your auth modal.
+
+* EVM EOAs behave as regular wallets (no smart wallet features like sponsorship).
+* You can optionally use an EVM EOA as a smart wallet owner to unlock smart wallet features like sponsorship and batching.
+* Solana wallets are external wallets; you can enable sponsored gas with a policy.
+
+## Pre-built UI connectors
+
+* Auto-detect browser installed wallet extensions.
+* Optionally add WalletConnect for other EVM wallets.
+* Configure once with `configForExternalWallets()` and pass into the UI components using `createConfig()`.
+
+You can fully customize wallet connector UI to define features wallets, ordering, and more. See how [here](/docs/wallets/react/connectors/customization).
+
+### Detect and display EVM + Solana wallets
+
+Use the helper to generate EVM connectors, Solana adapters, and UI customization in one place. Add to UI components by updating your `createConfig`.
+
+```ts twoslash [src/config.ts]
+// @noErrors
+import {
+ createConfig,
+ cookieStorage,
+ configForExternalWallets,
+} from "@account-kit/react";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { Connection } from "@solana/web3.js";
+
+// Keep external wallets settings in one place
+export const externalWalletsConfig = configForExternalWallets({
+ // Preferred order (case-insensitive). Use "wallet_connect" for WalletConnect.
+ wallets: ["wallet_connect", "phantom", "metamask", "coinbase wallet"],
+ // Surface both EVM and Solana wallets (filter to ["evm"] or ["svm"] if desired)
+ chainType: ["svm", "evm"],
+ // EVM-only WalletConnect support (omit to disable)
+ walletConnectProjectId: "your-project-id",
+ // Controls the built-in Featured section
+ hideMoreButton: false,
+ numFeaturedWallets: 4,
+});
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your_api_key" }),
+ chain: sepolia,
+ ssr: true,
+ storage: cookieStorage,
+ enablePopupOauth: true,
+ sessionConfig: {
+ expirationTimeMs: 1000 * 60 * 60, // 60 minutes (default is 15 min)
+ },
+ /**
+ * External wallets (EVM + Solana)
+ */
+ connectors: externalWalletsConfig.connectors,
+ solana: {
+ connection: new Connection(
+ `https://solana-devnet.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_API_KEY}`,
+ ),
+ adapters: externalWalletsConfig.adapters,
+ // optional gas sponsor for Solana
+ policyId: process.env.NEXT_PUBLIC_SOLANA_POLICY_ID,
+ },
+ },
+ {
+ // Authentication ui config - your customizations here
+ auth: {
+ sections: [
+ [{ type: "email" }],
+ [
+ { type: "passkey" },
+ { type: "social", authProviderId: "google", mode: "popup" },
+ { type: "social", authProviderId: "facebook", mode: "popup" },
+ ],
+ [{ type: "external_wallets", ...externalWalletsConfig.uiConfig }],
+ ],
+ addPasskeyOnSignup: true,
+ showSignInText: true,
+ },
+ },
+);
+```
+
+If you don't need customization, you can manually pass EVM `connectors` and Solana `adapters` directly into `createConfig()` without using the helper.
+
+### Wallet connect
+
+If you want to access other EVM providers via `WalletConnect`, provide a WalletConnect Project ID in your external wallets config. You can create a WalletConnect project ID [here](https://cloud.reown.com/sign-in).
+
+```ts twoslash
+// @noErrors
+export const externalWalletsConfig = configForExternalWallets({
+ wallets: ["wallet_connect", "metamask", "phantom"],
+ chainType: ["evm", "svm"],
+ walletConnectProjectId: "your-project-id",
+});
+```
+
+## Custom connectors
+
+If you don't want to use pre-built UI components, you can use React hooks to customize your EOA connection.
+
+### EVM connectors
+
+Use the [useConnect](https://www.alchemy.com/docs/wallets/reference/account-kit/react/hooks/useConnect) hook to allow users to connect their EOA via available connectors:
+
+```tsx twoslash
+// @noErrors
+import { useConnect } from "@account-kit/react";
+
+const { connectors, connect } = useConnect({
+ onSuccess: (data) => {
+ console.log("Connected!", data);
+ },
+ onError: (err) => {
+ console.error("Connection failed", err);
+ },
+});
+
+return (
+
+);
+```
+
+### Programmatic login with a Solana adapter
+
+Use the Solana wallet hook to select a specific adapter without showing the modal:
+
+```ts twoslash
+// @noErrors
+import { useSolanaWallet } from "@account-kit/react";
+
+const { select, wallets } = useSolanaWallet();
+const phantom = wallets.find((w) => w.name.toLowerCase() === "phantom");
+
+if (phantom) {
+ await select(phantom.adapter.name);
+}
+```
+
+## Bring in an EOA as a Smart Wallet Owner
+
+For local wallets or JSON-RPC wallets that support the [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) standard (like MetaMask, Coinbase Wallet, etc.), you can use `WalletClientSigner` from `@aa-sdk/core` to bring in these EOAs as your smart wallet owner. More info [here](https://www.alchemy.com/docs/wallets/third-party/signers/privy).
+By making your EOA an owner of a smart account, you will have access to AA feature through your new smart wallet.
+
+```ts twoslash
+// @noErrors
+import { WalletClientSigner, type SmartAccountSigner } from "@aa-sdk/core";
+import { createWalletClient, custom } from "viem";
+import { sepolia } from "viem/chains";
+import { createModularAccountV2 } from "@account-kit/smart-contracts";
+
+const externalProvider = window.ethereum; // or another EIP-1193 provider
+
+const walletClient = createWalletClient({
+ chain: sepolia,
+ transport: custom(externalProvider),
+});
+
+export const signer: SmartAccountSigner = new WalletClientSigner(
+ walletClient,
+ "json-rpc",
+);
+
+// Connect your signer to your smart account
+
+const account = await createModularAccountV2({
+ chain: sepolia,
+ transport: alchemyTransport,
+ signer: signer, // your EOA that you've brought in as an owner
+});
+```
diff --git a/fern/wallets/pages/react/login-methods/onchain-passkeys.mdx b/fern/wallets/pages/react/login-methods/onchain-passkeys.mdx
new file mode 100644
index 000000000..8f78b85d3
--- /dev/null
+++ b/fern/wallets/pages/react/login-methods/onchain-passkeys.mdx
@@ -0,0 +1,132 @@
+---
+title: "[NEW] On-chain Passkeys"
+description: How to use on-chain passkeys to authenticate users and send user operations
+slug: wallets/react/login-methods/onchain-passkeys
+---
+
+**This feature is in early access.**
+
+The WebAuthn Modular Account enables password-less authentication on-chain using **passkeys** (via WebAuthn), and is compatible with [Alchemy’s Account Kit](/wallets). This guide demonstrates how to register credentials, authenticate users, and send user operations using the `@account-kit/smart-contracts` package.
+
+Instead of on-device verification it's on-chain verification. Devs are responsible for generating the credentials attached to those biometric webauthn compatible passkeys and then using our signer.
+
+## Prerequisites
+
+* A frontend environment (React, Vite, Next.js, etc.)
+* Browser with WebAuthn and Credential Management API support
+* Install Alchemy Account Kit SDK smart contracts package (use the latest version - **at least 4.52.1**) and Viem
+
+
+ ```bash yarn
+ yarn add @account-kit/smart-contracts@4.52.1 @account-kit/infra@4.52.1 viem
+ ```
+
+ ```bash npm
+ npm add @account-kit/smart-contracts@4.52.1 @account-kit/infra@4.52.1 viem
+ ```
+
+ ```bash pnpm
+ pnpm add @account-kit/smart-contracts@4.52.1 @account-kit/infra@4.52.1 viem
+ ```
+
+
+## Example Workflow
+
+1. User registers a WebAuthn credential
+2. Credential ID and public key are stored locally
+3. On login, `navigator.credentials.get()` fetches the credential
+4. The app creates a client using the credential
+5. A user operation is signed and sent
+
+### Register WebAuthn Credential
+
+
+ **You are responsible for retaining your users’ public keys**. Public keys
+ generated by the WebAuthn specification are only retrievable **once** on the
+ initial creation of the credential. As a precaution, we strongly suggest
+ adding a secondary off-chain signer to use for account recovery
+
+
+```tsx twoslash
+import { createWebAuthnCredential } from "viem/account-abstraction";
+
+const credential = await createWebAuthnCredential({
+ name: "Credential Name",
+});
+
+// store credential id to public key mapping
+// NOTE: use of localStorage is for TESTING PURPOSES ONLY, NOT FOR PRODUCTION USE
+localStorage.setItem(credential.id, credential.publicKey); //credentialIdAsBase64Encoded -> publicKeyHex
+```
+
+### Login With Credential
+
+```tsx twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { alchemy, arbitrumSepolia } from "@account-kit/infra";
+
+const publicKeyRequest: PublicKeyCredentialRequestOptions = {
+ challenge: Uint8Array.fromHex("0x"), // Generate a random challenge
+ rpId: "localhost", // should match your dApp domain
+};
+
+// retrieve available passkeys for the provided domain
+const publicKeyCredential = await navigator.credentials.get({
+ publicKeyRequest,
+});
+
+if (publicKeyCredential) {
+ // verify that passkey with corresponding id exists on dApp
+ const publicKeyHex = localStorage.getItem(publicKeyCredential.id);
+ if (!publicKeyHex) throw new Error("Account does not exist");
+
+ // create client to send transactions on behalf of verified user
+ const accountClient = await createModularAccountV2Client({
+ policyId: "YOUR_POLICY_ID",
+ mode: "webauthn",
+ credential: {
+ id: publicKeyCredential.id,
+ publicKey: publicKeyHex,
+ },
+ rpId: "localhost",
+ chain: arbitrumSepolia,
+ transport: alchemy({ apiKey: "YOUR_ALCHEMY_API_KEY" }),
+ });
+}
+```
+
+
+ | Parameter | Type | Description |
+ | ------------ | ------------ | --------------------------------------------------- |
+ | `policyId` | `string` | Your account policy UUID from the Alchemy dashboard |
+ | `mode` | `"webauthn"` | Specifies credential mode |
+ | `credential` | `object` | `{ id: string, publicKey: Address }` |
+ | `rpId` | `string` | Relying Party ID (e.g., `localhost` or your domain) |
+ | `chain` | `Chain` | Network config (e.g., `arbitrumSepolia`) |
+ | `transport` | `Transport` | Alchemy or custom RPC transport |
+
+
+### Send User Operation
+
+```tsx
+const operation = await accountClient.sendUserOperation({
+ uo: {
+ target: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // Example: Vitalik's address
+ data: "0x", // No calldata
+ value: parseEther("0"),
+ },
+});
+```
+
+## React Native Integration
+
+Get your React Native environment set up by following these [docs](/wallets/react-native/overview). Once you’ve completed this setup, you can use the webauthn signer as detailed above!
+
+
+ `localStorage` is not available in React Native. Please use an alternative
+ storage method.{" "}
+
+
+## What's Next
+
+This is only the initial SDK release for on-chain passkeys. We are actively working on the DevX so your feedback will be greatly appreciated. If you have any questions or are interested in learning more, please reach out!
diff --git a/fern/wallets/pages/react/login-methods/passkey-login.mdx b/fern/wallets/pages/react/login-methods/passkey-login.mdx
new file mode 100644
index 000000000..fb2c64703
--- /dev/null
+++ b/fern/wallets/pages/react/login-methods/passkey-login.mdx
@@ -0,0 +1,146 @@
+---
+title: Passkey Login Authentication
+description: How to implement Passkey Login authentication in your React app
+slug: wallets/react/login-methods/passkey-login
+---
+
+If a user has added a passkey to their account, or they initially signed up with a passkey, you can easily authenticate them using that passkey. This provides a secure, passwordless authentication experience.
+
+You can implement Passkey Login authentication in two ways:
+
+* [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
+* [Custom UI](#custom-ui) - Complete control over the user experience
+
+## Pre-built UI Components
+
+Smart Wallets provides pre-built UI components that handle the entire Passkey Login authentication flow with minimal code.
+
+### Step 1: Add Authentication Components to Your Page
+
+Before configuring your authentication, first add one of the pre-built components to your application:
+
+
+
+Or:
+
+
+
+### Step 2: Configure Passkey Login in UI Components
+
+After adding the components, configure the Passkey Login authentication in your application config:
+
+To customize the Passkey Login authentication experience in your pre-built components, configure the UI as follows:
+
+```tsx twoslash
+import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+const uiConfig: AlchemyAccountsUIConfig = {
+ auth: {
+ sections: [
+ [
+ // Include passkey login in a section
+ { type: "passkey" },
+
+ // You can combine with other authentication methods
+ { type: "email" },
+ ],
+ ],
+ },
+};
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ },
+ uiConfig,
+);
+```
+
+Passkey login configuration accepts the following options:
+
+```ts twoslash
+type PasskeyAuthType = {
+ type: "passkey";
+};
+```
+
+You can find the full type definition in the [Account Kit source code](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/react/src/components/auth/types.ts).
+
+For more details on UI component customization, see the [UI Components](/wallets/react/ui-components) documentation.
+
+## Custom UI
+
+If you need complete control over the user experience, you can implement your own custom UI for Passkey Login authentication using Smart Wallets hooks.
+
+### Option 1: Passkey Login with Email
+
+If the user's passkey is associated with an email, you can use the email to help identify the correct passkey:
+
+```tsx twoslash
+import { useAuthenticate } from "@account-kit/react";
+
+// Inside your component
+const { authenticate } = useAuthenticate();
+
+// When the user wants to log in with their passkey and email
+const handlePasskeyLogin = (email: string) => {
+ authenticate(
+ {
+ type: "passkey",
+ email,
+ },
+ {
+ onSuccess: () => {
+ // Success - user authenticated with passkey
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+};
+```
+
+### Option 2: Passkey Login without Email
+
+If you want to authenticate a user with just their passkey (without requiring an email), you can use this approach:
+
+```tsx twoslash
+import { useAuthenticate } from "@account-kit/react";
+
+// Inside your component
+const { authenticate } = useAuthenticate();
+
+// When the user wants to log in with just their passkey
+const handlePasskeyOnlyLogin = () => {
+ authenticate(
+ {
+ type: "passkey",
+ createNew: false, // Important: set to false to prevent creating a new passkey
+ },
+ {
+ onSuccess: () => {
+ // Success - user authenticated with passkey
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+};
+```
+
+### Step 3: Track Authentication Status
+
+Use the `useSignerStatus` hook to determine if the user is authenticated:
+
+```tsx twoslash
+import { useSignerStatus } from "@account-kit/react";
+
+// Inside your component
+const { isConnected } = useSignerStatus();
+
+// You can use isConnected to conditionally render UI
+```
diff --git a/fern/wallets/pages/react/login-methods/social-providers.mdx b/fern/wallets/pages/react/login-methods/social-providers.mdx
new file mode 100644
index 000000000..71d2651a0
--- /dev/null
+++ b/fern/wallets/pages/react/login-methods/social-providers.mdx
@@ -0,0 +1,219 @@
+---
+title: Custom Social Providers with Auth0
+description: How to implement custom social providers using Auth0 in your React app
+slug: wallets/react/login-methods/social-providers
+---
+
+In addition to the standard social login providers (Google, Facebook, Apple), Smart Wallets allows you to integrate custom OAuth providers through Auth0. This gives you flexibility to add authentication methods like GitHub, Twitter, LinkedIn, and more.
+
+You can implement custom social providers in two ways:
+
+* [Pre-built UI Components](#pre-built-ui-components) - Quick implementation with minimal code
+* [Custom UI](#custom-ui) - Complete control over the user experience
+
+## Pre-built UI Components
+
+Smart Wallets provides pre-built UI components that handle the entire custom social provider authentication flow with minimal code.
+
+### Step 1: Add Authentication Components to Your Page
+
+Before configuring your authentication, first add one of the pre-built components to your application:
+
+
+
+Or:
+
+
+
+### Step 2: Setting Up Auth0
+
+Before configuring the UI components, you need to set up Auth0:
+
+1. Create or log in to an account on [auth0.com](https://auth0.com)
+
+2. In the Auth0 dashboard, go to "Authentication → Social" in the sidebar
+
+3. Click "Create Social Connection" and choose your desired provider (e.g., GitHub)
+
+
+
+4. You can either use Auth0's dev keys for testing or add your own credentials. If you want to add your own, click the link that says "How to obtain a Client ID" and follow the directions.
+
+5. Select the attributes and permissions you'll be requesting. It's recommended to at least request the user's email address as it can be useful for merging accounts from different providers later. Note that your users will be prompted for consent to share whatever information you request.
+
+
+
+6. Note the "Name" field (e.g., "github") - you'll need this later for the `auth0Connection` parameter
+
+7. Enable the connection for your Auth0 application
+
+
+
+8. From your Auth0 dashboard, go to "Applications → Applications" in the sidebar
+
+9. Select your application and note the "Domain", "Client ID", and "Client Secret"
+
+
+
+10. Add these to your Smart Wallets dashboard in the embedded accounts auth config
+
+
+
+ In addition to the "Client ID" and "Client Secret" fields, you must also fill in the "Auth0 Domain" field from the Auth0 dashboard.
+
+### Step 3: Configure Custom Social Providers in UI Components
+
+After adding the components and setting up Auth0, configure the custom social providers in your application config:
+
+```tsx twoslash
+import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+const uiConfig: AlchemyAccountsUIConfig = {
+ auth: {
+ sections: [
+ [
+ // Standard social providers
+ { type: "social", authProviderId: "google", mode: "popup" },
+
+ // Custom social providers via Auth0
+ {
+ type: "social",
+ authProviderId: "auth0",
+ // Specify the Auth0 connection to use directly
+ auth0Connection: "github",
+ displayName: "GitHub",
+ // Custom logo URL for the provider
+ logoUrl:
+ "https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png",
+ // Optional dark mode logo
+ logoUrlDark:
+ "https://github.githubassets.com/assets/GitHub-Mark-Light-ea2971cee799.png",
+ mode: "popup",
+ },
+ {
+ type: "social",
+ authProviderId: "auth0",
+ auth0Connection: "twitter",
+ displayName: "Twitter",
+ logoUrl: "https://path-to-twitter-logo.png",
+ mode: "popup",
+ },
+ ],
+ ],
+ },
+};
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ // Required for popup flow
+ enablePopupOauth: true,
+ },
+ uiConfig,
+);
+```
+
+Auth0 custom providers accept the following configuration:
+
+```ts twoslash
+type SocialAuthType = {
+ type: "social";
+ // For Auth0 custom providers
+ authProviderId: "auth0";
+ // Auth0-specific connection string (e.g., "github", "twitter")
+ auth0Connection?: string;
+ // Display name for the provider button
+ displayName?: string;
+ // URL for the provider's logo
+ logoUrl: string;
+ // URL for the provider's logo in dark mode (optional, `logoUrl` is used for both light & dark mode if not provided)
+ logoUrlDark?: string;
+ // Authentication mode (popup or redirect)
+ mode: "popup" | "redirect";
+ // Optional: Specifies the requested OAuth scope
+ scope?: string;
+ // Optional: Specifies additional claims to be included in the authentication token
+ claims?: string;
+};
+```
+
+You can find the full type definition in the [Account Kit source code](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/react/src/components/auth/types.ts).
+
+For more details on UI component customization, see the [UI Components](/wallets/react/ui-components) documentation.
+
+## Custom UI
+
+If you need complete control over the user experience, you can implement your own custom UI for custom social providers using Smart Wallets hooks.
+
+### Step 1: Set Up Auth0
+
+Before implementing in your React app, you need to configure Auth0 as described in the [Setting Up Auth0](#setting-up-auth0) section above.
+
+### Step 2: Implement Authentication in Your React App
+
+Use the `useAuthenticate` hook to implement Auth0 authentication:
+
+```tsx twoslash
+import { useAuthenticate } from "@account-kit/react";
+
+// Inside your component
+const { authenticate } = useAuthenticate();
+
+// Option 1: Generic Auth0 login (shows Auth0 provider selection screen)
+const handleAuth0Login = () => {
+ authenticate(
+ {
+ type: "oauth",
+ authProviderId: "auth0",
+ mode: "popup", // or "redirect"
+ },
+ {
+ onSuccess: () => {
+ // Authentication successful!
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+};
+
+// Option 2: Direct provider login (bypasses Auth0 selection screen)
+const handleGitHubLogin = () => {
+ authenticate(
+ {
+ type: "oauth",
+ authProviderId: "auth0",
+ auth0Connection: "github", // Use the connection name from Auth0
+ mode: "popup", // or "redirect"
+ },
+ {
+ onSuccess: () => {
+ // Authentication successful!
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+};
+```
+
+Option 1 will take users to an Auth0 login page where they can choose the authentication method they want. Option 2 sends users directly to the specific provider's login (like GitHub) without showing the Auth0 selection screen, which usually provides a better user experience.
+
+The value passed to `auth0Connection` should match the string that appeared in the "Name" field of your auth provider connection in the Auth0 dashboard.
+
+### Step 3: Track Authentication Status
+
+Use the `useSignerStatus` hook to determine if the user is authenticated:
+
+```tsx twoslash
+import { useSignerStatus } from "@account-kit/react";
+
+// Inside your component
+const { isConnected } = useSignerStatus();
+
+// You can use isConnected to conditionally render UI
+```
diff --git a/fern/wallets/pages/react/mfa/email-magic-link.mdx b/fern/wallets/pages/react/mfa/email-magic-link.mdx
new file mode 100644
index 000000000..9ae4d71ac
--- /dev/null
+++ b/fern/wallets/pages/react/mfa/email-magic-link.mdx
@@ -0,0 +1,167 @@
+---
+title: Email Magic Link with Multi-Factor Authentication
+description: How to authenticate users with Email Magic Link and MFA in your React app
+slug: wallets/react/mfa/email-magic-link
+---
+
+This guide shows you how to implement authentication with Email Magic Link and TOTP-based multi-factor authentication in your React application.
+
+## Overview
+
+When a user has MFA enabled with an authenticator app (TOTP), the login flow requires the following steps:
+
+1. The user enters their email address to request a magic link
+2. If MFA is enabled for their account, they're prompted to enter the 6-digit TOTP code from their authenticator app (e.g., Google Authenticator)
+3. After entering a valid TOTP code, a magic link is sent to their email
+4. The user clicks the magic link from email to complete authentication
+5. Upon successful verification, the user is authenticated and redirected to the appropriate page
+
+This two-factor approach provides an additional layer of security beyond a standard magic link.
+
+## Implementation
+
+### Step 1: Initialize Authentication and Handle MFA Required Error
+
+First, attempt to authenticate with email. If MFA is required, an error will be thrown.
+You can handle this error by prompting the user to enter their TOTP code.
+
+```tsx twoslash
+import React from "react";
+import { useAuthenticate } from "@account-kit/react";
+import { MfaRequiredError } from "@account-kit/signer";
+import { useState } from "react";
+
+function MagicLinkWithMFA() {
+ const { authenticate } = useAuthenticate();
+
+ // Step 1: Handle initial email submission and check for MFA requirement
+ const handleInitialAuthentication = (email: string) => {
+ authenticate(
+ {
+ type: "email",
+ emailMode: "magicLink",
+ email,
+ },
+ {
+ onSuccess: () => {
+ // This callback only fires when the entire auth flow is complete
+ // (user clicked magic link and completed MFA if required)
+ console.log("Authentication successful!");
+ },
+ onError: (error) => {
+ // If MFA is required the attempt will result in an MfaRequiredError
+ if (error instanceof MfaRequiredError) {
+ const { multiFactorId } = error.multiFactors[0];
+
+ // Store the multiFactorId to use when the user enters their TOTP code
+
+ // In step 2, we will prompt the user to enter their TOTP code (from their authenticator app)
+ // and we'll use this multiFactorId to verify the TOTP code
+ }
+ // Handle other errors
+ },
+ },
+ );
+ };
+
+ return
{/* Your UI components here */}
;
+}
+```
+
+### Step 2: Submit TOTP Code and Complete Magic Link Authentication
+
+Once we have the MFA data from the first step, we can complete the authentication by submitting the TOTP code with the multiFactorId.
+You must prompt the user to enter their TOTP code (from their authenticator app) and then submit it with the multiFactorId.
+
+```tsx twoslash
+import React from "react";
+import { useAuthenticate } from "@account-kit/react";
+
+// Continuing from the previous component...
+
+function MagicLinkWithMFA() {
+ const { authenticate } = useAuthenticate();
+
+ // Prompt the user to enter their TOTP code (from their authenticator app)
+ // Hardcoded for now, but in a real app you'd get this from the user
+ const totpCode = "123456";
+ const multiFactorId = "123456"; // This is the multiFactorId from the first step
+
+ // Step 2: Submit the TOTP code with multiFactorId to complete the flow
+ const handleMfaSubmission = (email: string) => {
+ authenticate(
+ {
+ type: "email",
+ emailMode: "magicLink",
+ email,
+ // The multiFactors array tells the authentication system which
+ // factor to verify and what code to use
+ multiFactors: [
+ {
+ multiFactorId,
+ multiFactorCode: totpCode,
+ },
+ ],
+ },
+ {
+ onSuccess: () => {
+ // This callback will only fire after the user has clicked the magic link and the email has been verified
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+
+ return
{/* Your UI components here */}
;
+}
+```
+
+### Step 3: Handle the Magic Link Redirect
+
+When the user clicks the magic link in their email, your application needs to handle the redirect and complete the authentication.
+The magic link will redirect to your application with a bundle parameter. You must submit this bundle to the `authenticate` function to complete the authentication.
+
+```tsx twoslash
+import React, { useEffect } from "react";
+import { useAuthenticate } from "@account-kit/react";
+
+function MagicLinkRedirect() {
+ const { authenticate } = useAuthenticate();
+
+ const handleMagicLinkRedirect = () => {
+ const url = new URL(window.location.href);
+ const bundle = url.searchParams.get("bundle");
+
+ // If there's no bundle parameter, this isn't a magic link redirect
+ if (!bundle) return;
+
+ authenticate(
+ {
+ type: "email",
+ bundle,
+ },
+ {
+ onSuccess: () => {
+ // onSuccess only fires once the entire flow is done (email magic link + optional MFA).
+ // It still runs even if the final step completes in another tab/window.
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+ };
+
+ // Call this function when the component mounts
+ useEffect(() => {
+ handleMagicLinkRedirect();
+ }, []);
+}
+```
+
+## Next Steps
+
+* [Set up MFA for your users](/wallets/react/mfa/setup-mfa)
+* [Implement Email OTP with MFA](/wallets/react/mfa/email-otp)
diff --git a/fern/wallets/pages/react/mfa/email-otp.mdx b/fern/wallets/pages/react/mfa/email-otp.mdx
new file mode 100644
index 000000000..bdee669d7
--- /dev/null
+++ b/fern/wallets/pages/react/mfa/email-otp.mdx
@@ -0,0 +1,142 @@
+---
+title: Email OTP with Multi-Factor Authentication
+description: How to authenticate using Email OTP when MFA is enabled
+slug: wallets/react/mfa/email-otp
+---
+
+This guide shows you how to implement Email OTP authentication when a user has multi-factor authentication (MFA) enabled.
+
+## Overview
+
+When MFA is enabled, the authentication process requires two steps:
+
+1. Verify the user's email with a one-time password
+2. Verify the 6-digit code (TOTP) from their authenticator app
+
+## Implementation
+
+### Step 1: Start Email OTP Authentication
+
+First, initiate the email OTP authentication process:
+
+```tsx twoslash
+import React from "react";
+import { useAuthenticate } from "@account-kit/react";
+
+// Inside your component
+const { authenticate } = useAuthenticate();
+
+const handleSendCode = (email: string) => {
+ authenticate(
+ {
+ type: "email",
+ emailMode: "otp",
+ email,
+ },
+ {
+ onSuccess: () => {
+ // This callback will only fire after both email OTP and MFA (if required) are completed
+ },
+ onError: (error) => {
+ // Handle error
+ console.error(error);
+ },
+ },
+ );
+};
+```
+
+### Step 2: Submit the OTP Code
+
+After the user receives the email OTP, they must submit the code to continue.
+
+The signer status will change to `AWAITING_EMAIL_AUTH` when an OTP code needs to be submitted:
+
+```tsx twoslash
+import { useSignerStatus, useAuthenticate } from "@account-kit/react";
+import { AlchemySignerStatus } from "@account-kit/signer";
+import React, { useEffect } from "react";
+
+function EmailOtpVerification() {
+ const { status } = useSignerStatus();
+ const { authenticate, isPending } = useAuthenticate({
+ onError: (error) => {
+ // Handle OTP verification errors
+ console.error("OTP verification failed:", error);
+ },
+ });
+
+ // Called when user enters their OTP code from email
+ const handleVerify = (emailOtp: string) => {
+ authenticate({
+ type: "otp",
+ otpCode: emailOtp,
+ });
+ };
+
+ // Example of prompting user when OTP verification is needed
+ useEffect(() => {
+ if (status === AlchemySignerStatus.AWAITING_EMAIL_AUTH) {
+ // Show OTP input UI to the user
+ }
+ }, [status]);
+
+ return (
+ // Your OTP input UI
+
{/* OTP input component */}
+ );
+}
+```
+
+### Step 3: Complete Authentication
+
+If MFA is required, the signer status will change to `AWAITING_MFA_AUTH`. You'll need to collect and submit the TOTP code from the user's authenticator app:
+
+```tsx twoslash
+import {
+ useSignerStatus,
+ useSigner,
+ useAuthenticate,
+} from "@account-kit/react";
+import { AlchemySignerStatus } from "@account-kit/signer";
+import React, { useEffect, useState } from "react";
+
+function MfaVerification() {
+ const signer = useSigner();
+ const { status } = useSignerStatus();
+ const [isVerifying, setIsVerifying] = useState(false);
+
+ // Called when user enters their TOTP code from authenticator app
+ const handleVerify = async (totpCode: string) => {
+ try {
+ setIsVerifying(true);
+ await signer?.validateMultiFactors({
+ multiFactorCode: totpCode,
+ });
+ // After successful MFA validation, the user will be authenticated
+ // and the onSuccess callback from the initial authenticate call will fire
+ } catch (error) {
+ console.error("MFA verification failed:", error);
+ } finally {
+ setIsVerifying(false);
+ }
+ };
+
+ // Example of prompting user when MFA verification is needed
+ useEffect(() => {
+ if (status === AlchemySignerStatus.AWAITING_MFA_AUTH) {
+ // Show TOTP input UI to the user
+ }
+ }, [status]);
+
+ return (
+ // Your TOTP input UI
+
{/* TOTP input component */}
+ );
+}
+```
+
+## Next Steps
+
+* [Set up MFA for your users](/wallets/react/mfa/setup-mfa)
+* [Email Magic Link with MFA](/wallets/react/mfa/email-magic-link)
diff --git a/fern/wallets/pages/react/mfa/setup-mfa.mdx b/fern/wallets/pages/react/mfa/setup-mfa.mdx
new file mode 100644
index 000000000..0e3869c76
--- /dev/null
+++ b/fern/wallets/pages/react/mfa/setup-mfa.mdx
@@ -0,0 +1,493 @@
+---
+title: Setting Up Multi-Factor Authentication
+description: How to set up additional security with authenticator apps in your React application
+slug: wallets/react/mfa/setup-mfa
+---
+
+
+
+ With Smart Wallets, multi-factor authentication (MFA) uses authenticator apps—like Google Authenticator, Authy, or Microsoft Authenticator—to generate a Time-based One-Time Password (TOTP).
+
+ By requiring both a user's primary login (e.g., email OTP, magic link, or social login) and a TOTP from their authenticator app, your application gains an extra layer of security.
+
+
+ Multi-factor authentication requires a primary authentication method (Email
+ OTP, Email Magic Link, or Social Login) to be already set up. See the [React
+ Quickstart](/wallets/react/quickstart) guide to set up your primary
+ authentication method first.
+
+
+ ## Prerequisites
+
+ Before implementing MFA, you need to have:
+
+ 1. **Set up primary authentication** - MFA requires a primary authentication method to be already set up. Follow the [React Quickstart](/wallets/react/quickstart) guide to configure email (OTP or magic link) or social login.
+ 2. **Working authentication flow** - Ensure users can successfully sign in with your primary authentication method.
+
+ ## Implementation
+
+ To implement authenticator app verification in your React application, you'll use the `useMFA` hook from Smart Wallets.
+
+ ### Step 1: Checking if Multi-Factor Authentication is Available
+
+ First, check if the user is logged in and able to edit their MFA settings:
+
+ ```tsx twoslash
+ import React from "react";
+ import { useMFA } from "@account-kit/react";
+
+ // Inside your component
+ const { isReady } = useMFA();
+
+ // Only show MFA setup options if available
+ if (isReady) {
+ // Render MFA setup UI
+ } else {
+ // User needs to authenticate first
+ }
+ ```
+
+ ### Step 2: Setting Up an Authenticator App
+
+ In this step, we receive a QR code URL from the backend and display it to the user. This URL contains a TOTP seed and necessary metadata. When displayed as a QR code, it can be scanned by most authenticator apps (Google Authenticator, Authy, 1Password, etc.) to set up 6-digit time-based codes. The backend also provides a unique multiFactorId which we'll need to store for the verification step.
+
+ ```tsx twoslash
+ import React, { useState } from "react";
+ import { QRCodeSVG } from "qrcode.react";
+ import { useMFA } from "@account-kit/react";
+
+ function AuthenticatorSetupComponent() {
+ const { addMFA } = useMFA();
+ const [qrCodeUrl, setQrCodeUrl] = useState("");
+ const [factorId, setFactorId] = useState("");
+
+ const handleSetupAuthenticator = () => {
+ // Use the mutate method from the mutation result
+ addMFA.mutate(
+ {
+ multiFactorType: "totp", // Technical name for authenticator apps
+ },
+ {
+ onSuccess: (result) => {
+ // Store the QR code URL and factor ID
+ setQrCodeUrl(result.multiFactorTotpUrl);
+ // Store the factor ID which will be needed for verification in the next step
+ // This ID uniquely identifies the MFA factor being configured
+ setFactorId(result.multiFactorId);
+ },
+ onError: (error) => {
+ console.error("Failed to set up authenticator app:", error);
+ },
+ },
+ );
+ };
+
+ // You can also check loading state directly
+ const isLoading = addMFA.isPending;
+
+ return (
+
+ );
+ }
+ ```
+
+ ### Step 4: Managing Authenticator Apps
+
+ You can retrieve and remove authenticator app–based MFA from a user's account by using the `useMFA` hook. Each verification method (also called a "factor") is identified by a unique `multiFactorId`. For example, a TOTP-based authenticator app is one factor.
+
+ ```tsx twoslash
+ import React, { useEffect, useState } from "react";
+ import { useMFA } from "@account-kit/react";
+ import type { MfaFactor } from "@account-kit/signer";
+
+ function ManageMfaComponent() {
+ const { getMFAFactors, removeMFA } = useMFA();
+ const [factors, setFactors] = useState([]);
+
+ // Fetch all MFA verification methods (factors) for the current user
+ useEffect(() => {
+ // Only fetch when component mounts and we're ready
+ getMFAFactors.mutate(undefined, {
+ onSuccess: (result) => {
+ // factors.multiFactors is an array of verification methods
+ setFactors(result.multiFactors);
+ },
+ });
+ }, [getMFAFactors]);
+
+ // Remove a TOTP authenticator app by its multiFactorId
+ const handleRemoveAuthenticator = (multiFactorId: string) => {
+ removeMFA.mutate(
+ { multiFactorIds: [multiFactorId] },
+ {
+ onSuccess: () => {
+ console.log("Authenticator removed successfully!");
+ // Update local state to reflect the removal
+ setFactors(factors.filter((f) => f.multiFactorId !== multiFactorId));
+ },
+ },
+ );
+ };
+
+ // Loading states are available directly from the mutation objects
+ if (getMFAFactors.isPending) return
+ );
+ }
+ ```
+
+ ## Next Steps
+
+ After setting up an authenticator app, users will need to provide both their primary authentication method and a 6-digit code when signing in:
+
+ ### Using Pre-built UI Components
+
+ If you're using the [pre-built UI components](/wallets/react/ui-components), the MFA verification process is handled automatically:
+
+ * The authentication flow will detect when a user has MFA enabled
+ * Users will be prompted for their authenticator app code after providing their primary credentials
+ * No additional code is required from you
+
+ ### Using Custom UI with Hooks
+
+ If you're implementing [custom UI with hooks](/wallets/react/react-hooks), you'll need to update your authentication code to handle the MFA verification step:
+
+ * [Email OTP with Multi-Factor Authentication](/wallets/react/mfa/email-otp) - See how to implement the two-step verification
+ * [Email Magic Link with Multi-Factor Authentication](/wallets/react/mfa/email-magic-link) - Learn how to handle magic links with MFA
+ * [Social Login with Multi-Factor Authentication](/wallets/react/mfa/social-login) - Social login with MFA is handled in the OAuth callback
+
+ For custom UI implementations, make sure your authentication logic checks for the MFA requirement and provides UI for users to enter their authenticator app code.
+
+
+
+ Alchemy Signer supports Time-based One-Time Passwords (TOTP) multi-factor authentication (MFA).
+ This lets you prompt users to set up a TOTP authenticator (e.g. Google Authenticator) as an additional security factor.
+
+
+ Multi-factor authentication is currently supported when authenticating with
+ Email OTP or Email Magic-link
+
+
+ ## Setting up Multi-Factor Authentication
+
+ * Prerequesit: Your user is already logged in with at least one authentication factor (e.g. [email OTP](/wallets/authentication/login-methods/email-otp), [email magic-link](/wallets/authentication/email-magic-link)).
+
+ ### 1. Add a new TOTP factor
+
+ Once the user is authenticated, you can call `addMfa` to enable TOTP. This returns factor details including an ID and setup information that your app can display to the user (e.g. a QR code or `otpauth` link that the user can scan in Google Authenticator).
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ const { multiFactors } = await signer.addMFA({
+ multiFactorType: "totp",
+ });
+
+ // Display the QR code or secret to the user
+ const totpUrl = result?.multiFactors[0].multiFactorTotpUrl;
+ const multiFactorId = result?.multiFactors[0].multiFactorId;
+ ```
+
+
+
+
+ You can show the `multiFactorTotpUrl` in your UI as a QR code or link for the user to add it to their authenticator app.
+
+ ### 2. Verify the TOTP setup
+
+ Once the user has scanned the TOTP secret, have them enter the 6-digit code from their authenticator app. Then call `verifyMfa`:
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ await signer.verifyMfa({
+ multiFactorId, // from addMfa
+ multiFactorCode: "123456",
+ });
+ ```
+
+
+
+
+ ### 3. Remove a TOTP factor
+
+ If a user wants to disable TOTP, call `removeMfa` with the `multiFactorId` you want to remove:
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ await signer.removeMfa({
+ multiFactorIds: [multiFactorId],
+ });
+ ```
+
+
+
+
+ ### 4. Get a list of existing MFA factors
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ const { multiFactors } = await signer.getMfaFactors();
+ ```
+
+
+
+
+ ## Authenticating Email OTP with multi-factor TOTP
+
+ ### Step 1: Send an OTP to user's email
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ signer.authenticate({
+ type: "email",
+ emailMode: "otp",
+ email: "user@mail.com",
+ });
+ ```
+
+
+
+
+ ### Step 2: Submit the email OTP code
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ signer.authenticate({
+ type: "otp",
+ otpCode: "EMAIL_OTP_CODE",
+ });
+ ```
+
+
+
+
+ ### Step 3: Submit the TOTP code (authenticator app code)
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ const user = await signer?.validateMultiFactors({
+ multiFactorCode: totpCode,
+ });
+ ```
+
+
+
+
+ ## Authenticating Email magic-link with multi-factor TOTP
+
+ When calling `authenticate` with `emailMode="magicLink"`, you can catch a `MfaRequiredError`. Then you can collect the TOTP code and resubmit.
+
+
+ ```ts example.ts
+ import { MfaRequiredError } from "@account-kit/signer";
+ import { signer } from "./signer";
+
+ const promptUserForCode = async () => {
+ // Prompt user for TOTP code
+ // const totpCode = await promptUserForCode();
+
+ return "123456";
+ };
+
+ try {
+ // Promise resolves when the user is fully authenticated (email magic link + optional MFA),
+ // even if completion happens in another tab/window
+ await signer.authenticate({
+ type: "email",
+ email: "user@mail.com",
+ emailMode: "magicLink",
+ });
+ } catch (err) {
+ if (err instanceof MfaRequiredError) {
+ // Prompt user for TOTP code
+ const totpCode = await promptUserForCode();
+
+ const { multiFactorId } = err.multiFactors[0];
+
+ // Promise resolves when the user is fully authenticated (email magic link + optional MFA),
+ // even if completion happens in another tab/window
+ await signer.authenticate({
+ type: "email",
+ emailMode: "magicLink",
+ email: "user@mail.com",
+ multiFactors: [
+ {
+ multiFactorId,
+ multiFactorCode: totpCode,
+ },
+ ],
+ });
+ } else {
+ // handle other errors
+ }
+ }
+ ```
+
+
+
+
+ ## Authenticating Social Login with multi-factor TOTP
+
+ When a user has MFA enabled using an authenticator app, the authentication process for social login is seamless. Unlike email authentication flows, you don't need to handle the MFA challenge manually in your code.
+
+ The TOTP verification happens automatically during the OAuth callback flow:
+
+ 1. The user authenticates with the social provider (Google, Facebook, etc.)
+ 2. After successful provider authentication, they're prompted for their TOTP code on the OAuth callback page
+ 3. Once verified, authentication completes normally
+
+ Simply use the standard social login authentication as shown in the [Social Login Authentication](/wallets/signer/authentication/social-login) guide:
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ await signer.authenticate({
+ type: "oauth",
+ authProviderId: "google", // Choose between the auth providers you selected to support from your auth policy
+ mode: "redirect", // Alternatively, you can choose "popup" mode
+ redirectUrl: "/", // After logging in, redirect to the index page
+ });
+ ```
+
+
+
+
+
diff --git a/fern/wallets/pages/react/mfa/social-login.mdx b/fern/wallets/pages/react/mfa/social-login.mdx
new file mode 100644
index 000000000..d8586d33e
--- /dev/null
+++ b/fern/wallets/pages/react/mfa/social-login.mdx
@@ -0,0 +1,82 @@
+---
+title: Social Login with Multi-Factor Authentication
+description: How to authenticate users with Social Login when MFA is enabled
+slug: wallets/react/mfa/social-login
+---
+
+This guide shows you how to implement social login authentication when a user has multi-factor authentication (MFA) enabled.
+
+## Overview
+
+When a user has MFA enabled using an authenticator app (TOTP), the authentication process for social login works as follows:
+
+1. The user initiates authentication with a social provider (Google, Facebook, etc.)
+2. After successful authentication with the provider
+3. The user is prompted to enter the **6-digit TOTP code** from their authenticator app (Google Authenticator, Authy, etc.) on the OAuth callback page
+
+The key advantage of social login with MFA is that the authenticator app verification happens automatically in the OAuth callback flow—your application code doesn't require special handling beyond enabling MFA.
+
+## Implementation
+
+### Step 1: Set Up Social Login
+
+First, implement social login as described in the [Social Login Authentication](/wallets/authentication/login-methods/social-login) guide. You can use either the pre-built UI components or custom UI approach.
+
+### Step 2: Implement MFA Setup for Users
+
+Before users can authenticate with social login + MFA, they need to set up an authenticator app for their account. Implement the MFA setup flow as described in the [Setting Up Multi-Factor Authentication](/wallets/react/mfa/setup-mfa) guide.
+
+Key implementation points:
+
+* Users need to be authenticated before they can set up MFA
+* Provide a UI for users to add an authenticator app to their account
+* Guide users through the QR code scanning process
+* Verify the setup with a 6-digit code from their authenticator app
+
+Once users have set up their authenticator app, they'll be prompted for the 6-digit code during subsequent social login attempts.
+
+### Step 3: No Additional Code Required
+
+When a user with MFA enabled attempts to authenticate using social login:
+
+1. They'll be redirected to the social provider's login page
+2. After successfully authenticating with the provider
+3. The OAuth callback page will automatically prompt them for their authenticator app code
+4. Once verified, they'll be redirected back to your application as a fully authenticated user
+
+```tsx twoslash
+import { useAuthenticate } from "@account-kit/react";
+
+// Your social login implementation doesn't change
+const { authenticate } = useAuthenticate();
+
+// For popup flow
+const handleGoogleLogin = () => {
+ authenticate(
+ {
+ type: "oauth",
+ authProviderId: "google",
+ mode: "popup",
+ },
+ {
+ onSuccess: () => {
+ // Authentication successful!
+ // If MFA was required, it was handled automatically in the oauth flow
+ },
+ onError: (error) => {
+ // Handle error
+ },
+ },
+ );
+};
+```
+
+### Step 4: Testing the Flow
+
+To test the complete authentication flow:
+
+1. Set up multi-factor authentication for a user using an authenticator app (see [Setting Up Multi-Factor Authentication](/wallets/react/mfa/setup-mfa))
+2. Log out the user
+3. Attempt to log in using social login
+4. After authenticating with the social provider, the user should be prompted for their authenticator app code
+5. After entering the correct code, the user should be fully authenticated
diff --git a/fern/wallets/pages/react/pay-gas-with-any-token.mdx b/fern/wallets/pages/react/pay-gas-with-any-token.mdx
new file mode 100644
index 000000000..b3e8f7910
--- /dev/null
+++ b/fern/wallets/pages/react/pay-gas-with-any-token.mdx
@@ -0,0 +1,264 @@
+---
+title: Pay gas with any token
+description: Enable your users to pay gas with ERC-20 tokens like USDC via Gas Manager
+slug: wallets/react/pay-gas-with-any-token
+---
+
+Gas fees paid in the native gas token can feel foreign to users that primarily hold stablecoins or your app's own token. With our smart wallet, you can enable your users to pay for gas with ERC-20 tokens beyond the native gas token, like USDC or your own custom tokens, streamlining the user experience.
+
+
+
+## Prerequisites
+
+
+
+## 1. Create a Gas Manager policy
+
+
+
+
+
+ ## 2. Add the policy to your global config
+
+ ```ts twoslash config.ts
+ import { createConfig } from "@account-kit/react";
+ import { sepolia } from "@account-kit/infra";
+
+ export const config = createConfig({
+ apiKey: "ALCHEMY_API_KEY",
+ chain: sepolia,
+ policyId: "GAS_MANAGER_POLICY_ID", // your policy
+ policyToken: {
+ address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", // USDC_ADDRESS on sepolia (can be any ERC-20 token address enabled in your policy)
+ maxTokenAmount: 10_000_000n, // Safety limit. If using USDC, this is 10 USDC (10 * 10^6).
+ },
+ });
+ ```
+
+ All `UserOperations` sent with the hooks will now be paid with the token configured above.
+
+
+ Be sure to use version **4.54.1** or later of the Account Kit SDK.
+
+
+ ## 3. Send a sponsored UserOperation
+
+```tsx twoslash
+import React from "react";
+import {
+ useSmartAccountClient,
+ useSendUserOperation,
+} from "@account-kit/react";
+import { sepolia, getAlchemyPaymasterAddress } from "@account-kit/infra";
+import { encodeFunctionData, parseAbi } from "viem";
+
+export default function PayWithUsdcButton() {
+ const { client, isLoadingClient } = useSmartAccountClient({});
+ const erc20Abi = parseAbi([
+ "function approve(address spender, uint256 amount) public returns (bool)",
+ ]);
+ const paymasterAddress = getAlchemyPaymasterAddress(sepolia, "0.7.0");
+
+ return (
+
+ client!.sendUserOperation({
+ uo: [
+ {
+ // approve call
+ target: tokenAddress,
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "approve",
+ args: [paymasterAddress, maxTokenAmount],
+ }) as `0x${string}`,
+ },
+ {
+ target: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // TARGET_ADDRESS
+ data: "0x",
+ value: 0n,
+ },
+ ],
+ })
+ }
+ disabled={isLoadingClient}
+ >
+ Send USDC-sponsored operation
+
+ );
+}
+```
+
+
+ When performing ERC-20 sponsorship, be sure to use the `sendUserOperation`
+ method on a client. Support for the `useSendUserOperation` hook is coming
+ soon.
+
+
+### 4 (Optional) Override on a single client
+
+If you only want specific operations to use the policy, you can specify it on a dedicated client instead of setting it globally.
+
+ ```tsx twoslash
+ const { client } = useSmartAccountClient({
+ policyId: "GAS_MANAGER_POLICY_ID",
+ policyToken: {
+ address: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", // USDC_ADDRESS on sepolia (can be any ERC-20 token address enabled in your policy)
+ maxTokenAmount: 10_000_000n, // Safety limit. If using USDC, this is 10 USDC (10 * 10^6).
+ },
+ });
+ ```
+
+Then use `client.sendUserOperation()` to send UserOperations.
+
+
+
+ ## 2. Set the Policy ID globally
+
+ When creating your Account Kit config, you can optionally pass in a Gas Policy ID. This will enable all UOs sent by the `useSendUserOperation` hook to be sponsored by the policy you created.
+
+ Copy it and then replace the `GAS_MANAGER_POLICY_ID` in the snippet below.
+
+
+ Remember to replace `ALCHEMY_API_KEY` with your Alchemy API key. If you don't
+ have one yet, you can create an API key on the [Alchemy
+ dashboard](https://dashboard.alchemy.com/signup/?a=aa-docs).
+
+
+ ```ts twoslash config.ts
+ // @noErrors
+ import { createConfig } from "@account-kit/react-native";
+ import { sepolia } from "@account-kit/infra";
+
+ export const config = createConfig({
+ apiKey: "ALCHEMY_API_KEY",
+ chain: sepolia,
+ policyId: "GAS_MANAGER_POLICY_ID", // [!code ++]
+ });
+ ```
+
+ Now you can follow the guide for [Sending user operations](/wallets/react-native/using-smart-accounts/send-user-operations) to send sponsored UOs from your smart account!
+
+ ## 3. Set the gas policy ID per UserOperation
+
+ If you want more control over which UOs are sponsored, then you can set the policy ID on a specific instance of the `Smart Account Client` returned by the `useSmartAccountClient()` hook.
+
+ ```tsx twoslash
+ import React from "react";
+ import { View, Pressable, Text } from "react-native";
+ import {
+ useSmartAccountClient,
+ useSendUserOperation,
+ } from "@account-kit/react-native";
+
+ export default function MyComponent() {
+ const { client } = useSmartAccountClient({
+ policyId: "GAS_MANAGER_POLICY_ID",
+ });
+ const { sendUserOperation } = useSendUserOperation({ client });
+
+ return (
+
+
+ sendUserOperation({
+ uo: {
+ target: "0xTARGET_ADDRESS",
+ data: "0x",
+ value: 0n,
+ },
+ })
+ }
+ >
+
+ Send Sponsored User Operation
+
+
+
+ );
+ }
+ ```
+
+
+
+ ## 2. Create a smart Account Client
+
+ Now you can create a Smart Account Client which is configured to sponsor gas.
+
+ ```tsx
+ import {
+ alchemy,
+ createAlchemySmartAccountClient,
+ sepolia,
+ } from "@account-kit/infra";
+ import { createLightAccount } from "@account-kit/smart-contracts";
+ // You can replace this with any signer you'd like
+ // We're using a LocalAccountSigner to generate a local key to sign with
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { generatePrivateKey } from "viem/accounts";
+
+ const alchemyTransport = alchemy({
+ apiKey: "YOUR_API_KEY",
+ });
+
+ export const client = createAlchemySmartAccountClient({
+ transport: alchemyTransport,
+ policyId: "YOUR_POLICY_ID",
+ policyToken: {
+ address: tokenAddress,
+ maxTokenAmount: 10_000_000n,
+ },
+ chain: sepolia,
+ account: await createLightAccount({
+ chain: sepolia,
+ transport: alchemyTransport,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ }),
+ });
+ ```
+
+ ## 3. Send a sponsored UserOperation
+
+ ```tsx
+ import { client } from "./client";
+ import { encodeFunctionData, parseAbi } from "viem";
+
+ const erc20Abi = parseAbi([
+ "function approve(address spender, uint256 amount) public returns (bool)",
+ ]);
+
+ const { hash } = await client.sendUserOperation({
+ uo: [
+ {
+ // approve call
+ target: tokenAddress,
+ data: encodeFunctionData({
+ abi: erc20Abi,
+ functionName: "approve",
+ args: [paymasterAddress, maxTokenAmount],
+ }) as `0x${string}`,
+ },
+ {
+ // actual call
+ target: "0xTARGET_ADDRESS",
+ data: "0x",
+ value: 0n,
+ },
+ ],
+ });
+ ```
+
+ ## 4. Estimate ERC20 token amount of a UO
+
+
+
+ ## 5. Simulate a ERC20 UO
+
+
+
+
+
+
+## Done
+
+Your dApp now sponsors gas with the chosen ERC-20 token!
diff --git a/fern/wallets/pages/react/pregenerate-wallets.mdx b/fern/wallets/pages/react/pregenerate-wallets.mdx
new file mode 100644
index 000000000..6fa328c49
--- /dev/null
+++ b/fern/wallets/pages/react/pregenerate-wallets.mdx
@@ -0,0 +1,140 @@
+---
+title: Pregenerate Wallets
+description: Learn how to pre-generate Smart Wallet addresses for your users with nothing more than an email address.
+slug: wallets/react/pregenerate-wallets
+---
+
+> **Objective** Generate a wallet owner (signer) and the smart-account address *before* a user ever logs in, using only their email.
+
+## Prerequisites
+
+* Alchemy **API Key** with Smart Wallets enabled
+ * If starting from scratch, follow the [quickstart](/docs/wallets/react/quickstart/existing-project#2-get-your-api-key) to get your API key
+ * Existing api keys can be found in the Alchemy Dashboard by viewing your config in the [Smart wallets configuration page](https://dashboard.alchemy.com/services/smart-wallets/configuration)
+* Node ≥ 18 (or cURL / any HTTP client)
+* The `@account-kit/smart-contracts` package (for step 4)
+
+You can generate a wallet address on both EVM and Solana before a user ever authenticates or logs in using only their email. This could enable use cases such as:
+
+* airdropping assets or seed balances to users who signed up for a waitlist with their email before they ever open your app
+* allowing users to send assets to another user with just their email even if the recipient has not logged in before. When that recipient logs in, they'll be able to claim their balance.
+* migrating assets to smart wallets to enable gas sponsorship, batching, etc.
+* and more!
+
+With Alchemy you can do this entirely from your server using the **Signer API** – no UI, hooks, or user interaction required.
+
+**Note:** Smart contract addresses are deterministic. The deployment address is a function of the address of owner/signer address, the account implementation (e.g. latest version of Modular Account), and the salt (optionally specified). If all three of those remain the same, then the smart account will be consistent across chains and for users. We will handle generating a consistent signer address for email.
+
+## 1. Create the owner signer
+
+First, you need to generate the signer address. This will be generated using the [`Create Wallet` API endpoint](https://www.alchemy.com/docs/wallets/api/smart-wallets/signer-api-endpoints/signer-api-endpoints/create-account). The signer address will be used as the owner of the new smart account and enable the user to sign transactions after authenticating with their email.
+
+
+
+ ```ts twoslash
+ const res = await fetch("https://api.g.alchemy.com/signer/v1/signup", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ // The API key is sent in the Authorization header
+ Authorization: `Bearer ${process.env.ALCHEMY_API_KEY}`,
+ },
+ body: JSON.stringify({
+ email: "[email protected]",
+ }),
+ });
+
+ if (!res.ok) throw new Error(await res.text());
+
+ const { address, solanaAddress, userId } = await res.json();
+ console.log({ address, solanaAddress, userId });
+ ```
+
+
+
+ ```ts
+ import request from "request";
+
+ request.post(
+ {
+ url: "https://api.g.alchemy.com/signer/v1/signup",
+ json: { email: "[email protected]" },
+ headers: {
+ Authorization: `Bearer ${process.env.ALCHEMY_API_KEY}`,
+ "Content-Type": "application/json",
+ },
+ },
+ (err, _resp, body) => {
+ if (err) throw err;
+ console.log(body); // ➜ { orgId, userId, address, solanaAddress, otpId }
+ },
+ );
+ ```
+
+
+
+ ```bash
+ curl -X POST https://api.g.alchemy.com/signer/v1/signup \
+ -H "Authorization: Bearer $ALCHEMY_API_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{ "email": "[email protected]" }'
+ ```
+
+
+
+| Field | Description |
+| --------------- | ------------------------------------------------------------------------------------------------------------------ |
+| `address` | The **signer (EOA)** address that will **own** your user's smart account (this is *not* the smart account itself). |
+| `solanaAddress` | The **Solana** wallet address associated with the same user. |
+| `orgId` | Your organisation ID – useful if you operate multiple orgs. |
+
+
+ The address you receive here is the **owner key**. Your user's Smart Contract
+ Account (ModularAccountV2) will be deployed at a different address – but we
+ can deterministically calculate it ahead of time using
+ `predictModularAccountV2Address`.
+
+
+## 2. Predict the smart-account address
+
+Using the signer address you generated in step 2, you can now generate the counter-factual (future) address of the smart account before it is even deployed.
+
+The `predictModularAccountV2Address` method will generate the address for a new Modular Account v2 and mirrors the CREATE2 logic used by the MAv2 factory.
+
+```ts twoslash
+import { predictModularAccountV2Address } from "@account-kit/smart-contracts";
+
+// Factory & implementation addresses vary by chain – you can grab them from
+// the Deployed addresses page.
+// https://www.alchemy.com/docs/wallets/smart-contracts/deployed-addresses
+
+const mav2FactoryAddress = "0x00000000000017c61b5bEe81050EC8eFc9c6fecd";
+const implementationAddress = "0x000000000000c5A9089039570Dd36455b5C07383";
+
+const smartAccountAddress = predictModularAccountV2Address({
+ factoryAddress: mav2FactoryAddress,
+ implementationAddress,
+ salt: 0n, // use 0 unless you have a custom salt scheme
+ type: "SMA",
+ ownerAddress: address, // signer address from Step 2
+});
+
+console.log({ smartAccountAddress });
+```
+
+Now you have **both** the owner key (`address`) and the eventual smart account
+address (`smartAccountAddress`). When the user finally authenticates, their
+smart account will be deployed at exactly this counter-factual address.
+
+
+ If your application is still using the older **Light Account** contracts (v1 or v2) instead of Modular Account V2, use the corresponding helpers from `@account-kit/smart-contracts`:
+
+ * `predictLightAccountAddress` – single-owner Light Account
+ * `predictMultiOwnerLightAccountAddress` – multi-owner Light Account
+
+ These functions take the same factory/owner/salt pattern shown above and work for all historical Light-Account factory versions. Everything else in this guide (steps 1-3) remains the same.
+
+
+***
+
+That's it! You now hold the future Smart Contract Account address. You can fund it immediately or store it for later onboarding.
diff --git a/fern/wallets/pages/react/react-hooks.mdx b/fern/wallets/pages/react/react-hooks.mdx
new file mode 100644
index 000000000..d88c1a508
--- /dev/null
+++ b/fern/wallets/pages/react/react-hooks.mdx
@@ -0,0 +1,128 @@
+---
+title: Custom UI for Authentication
+description: Overview of implementing custom authentication UI in your React app
+slug: wallets/react/react-hooks
+---
+
+While Smart Wallets provides pre-built UI components for authentication, you may want to create your own custom UI to match your application's design system. This section covers how to implement custom authentication flows using Smart Wallets hooks.
+
+
+ Tailwind CSS is a required dependency for using Alchemy Account Kit UI
+ components. However, Alchemy Smart Wallets hooks function independently and do
+ not require Tailwind.
+
+
+## Available Authentication Methods
+
+Smart Wallets supports several authentication methods that you can implement with custom UI. Each method has its own dedicated page with detailed implementation instructions, code examples, and specific parameters for the authentication hooks:
+
+
+
+
+ **Visit each method's dedicated page** for specific implementation details,
+ including the exact parameters to use with the `useAuthenticate` hook for that
+ authentication method.
+
+
+## Core Hooks for Custom UI
+
+The following section provides an overview of the main hooks you'll use when implementing custom authentication UI. These hooks are the foundation for all authentication methods, but their specific usage and parameters vary depending on the authentication method you choose.
+
+### useAuthenticate
+
+The `useAuthenticate` hook is the foundation for all authentication methods. It provides the `authenticate` function that handles the authentication process.
+
+
+ If MFA is required (e.g., the user has added an authenticator app), the
+ authenticate function will throw an MfaRequiredError or request a
+ multiFactorCode. See the MFA docs for a detailed example of handling TOTP
+ codes.
+
+
+```tsx twoslash
+import React from "react";
+import { useAuthenticate } from "@account-kit/react";
+
+function MyAuthComponent() {
+ const { authenticate, authenticateAsync, isPending } = useAuthenticate();
+
+ // Use authenticate with different parameters based on auth method
+ // The specific parameters depend on the authentication method
+ // See the individual authentication method pages for details
+}
+```
+
+### useUser
+
+The `useUser` hook returns the current user information from either an External Owned Account (EOA) or from a Smart Contract Account (SCA). This is the best way to check if a user is logged in regardless of account type.
+
+```tsx twoslash
+import React from "react";
+import { useUser } from "@account-kit/react";
+
+function MyComponent() {
+ const user = useUser();
+
+ if (!user) {
+ return
Please log in
;
+ }
+
+ return (
+
+
User address: {user.address}
+
Account type: {user.type}
{/* "eoa" or "sca" */}
+
+ );
+}
+```
+
+### useAccount
+
+The `useAccount` hook retrieves the smart contract account instance for the authenticated user. It's primarily used to get the smart contract account address and interact with the account.
+
+```tsx twoslash
+import React from "react";
+import { useAccount } from "@account-kit/react";
+
+function MyComponent() {
+ const { account, address, isLoadingAccount } = useAccount({
+ type: "ModularAccountV2", // Specify the account type you're using
+ });
+
+ if (isLoadingAccount) {
+ return
Loading account...
;
+ }
+
+ if (!account) {
+ return
Please log in to access your account
;
+ }
+
+ return (
+
+
Smart contract account address: {address}
+ {/* Now you can use the account instance for transactions */}
+
+ );
+}
+```
+
+This hook:
+
+* Returns a smart contract account instance (`account`) when the user is logged in
+* Provides the smart contract account address, not the signer address
+* Returns `undefined` for both `account` and `address` when the user is not logged in
+* Includes an `isLoadingAccount` flag to handle loading states
+
+Note: If you just need to check if a user is logged in (regardless of account type), consider using `useUser` instead.
+
+## Getting Started
+
+To implement custom authentication UI:
+
+1. **Choose an authentication method** from the list above and visit its dedicated page
+2. Follow the method-specific implementation guidelines on that page
+3. Use the core hooks described above following the method-specific parameters
+4. Implement the UI components for your chosen authentication flow
+5. Handle success and error states appropriately
+
+Each authentication method page provides detailed code examples tailored to that specific method, showing exactly how to configure the hooks and implement the entire authentication flow.
diff --git a/fern/wallets/pages/react/shared/authentication-methods.mdx b/fern/wallets/pages/react/shared/authentication-methods.mdx
new file mode 100644
index 000000000..8d554ba37
--- /dev/null
+++ b/fern/wallets/pages/react/shared/authentication-methods.mdx
@@ -0,0 +1,8 @@
+* [Email OTP](/wallets/authentication/login-methods/email-otp) - One-time password sent via email
+* [Email Magic Link](/wallets/authentication/login-methods/email-magic-link) - Authentication links sent via email
+* [Social Login](/wallets/authentication/login-methods/social-login) - Authentication with providers like Google and Facebook
+* [Custom Social Providers](/wallets/react/login-methods/social-providers) - Add custom OAuth providers via Auth0
+* [Passkey Signup](/wallets/react/login-methods/passkey-login) - Create accounts with passkeys
+* [Passkey Login](/wallets/react/login-methods/passkey-login) - Authenticate with existing passkeys
+* [Multi-Factor Authentication](/wallets/react/mfa/setup-mfa) - Add an additional verification layer after initial login using an authenticator app
+* [EOA Login](/wallets/react/login-methods/eoa-login) - Bring in your own EOAs
diff --git a/fern/wallets/pages/react/shared/embedded-auth-example.mdx b/fern/wallets/pages/react/shared/embedded-auth-example.mdx
new file mode 100644
index 000000000..528bc50b5
--- /dev/null
+++ b/fern/wallets/pages/react/shared/embedded-auth-example.mdx
@@ -0,0 +1,18 @@
+## Using Embedded Authentication
+
+To embed authentication directly in your page:
+
+```tsx twoslash
+import React from "react";
+import { AuthCard } from "@account-kit/react";
+
+export default function MyLoginPage() {
+ return (
+
+
+
+ );
+}
+```
+
+For more details on embedded authentication, see the [Embedded Authentication](/wallets/react/ui-components#embedded-auth) documentation.
diff --git a/fern/wallets/pages/react/shared/modal-auth-example.mdx b/fern/wallets/pages/react/shared/modal-auth-example.mdx
new file mode 100644
index 000000000..b9be898d7
--- /dev/null
+++ b/fern/wallets/pages/react/shared/modal-auth-example.mdx
@@ -0,0 +1,16 @@
+## Using Modal Authentication
+
+To add authentication in a modal popup:
+
+```tsx twoslash
+import React from "react";
+import { useAuthModal } from "@account-kit/react";
+
+export default function MyPage() {
+ const { openAuthModal } = useAuthModal();
+
+ return Sign in;
+}
+```
+
+For more details on modal configuration, see the [Modal Authentication](/wallets/react/ui-components#modal-auth) documentation.
diff --git a/fern/wallets/pages/react/solana-wallets/get-started.mdx b/fern/wallets/pages/react/solana-wallets/get-started.mdx
new file mode 100644
index 000000000..cc0127764
--- /dev/null
+++ b/fern/wallets/pages/react/solana-wallets/get-started.mdx
@@ -0,0 +1,131 @@
+---
+title: Getting started with Solana Smart Wallets
+description: Learn how to use Smart Wallets on Solana
+text: Getting started with Solana Smart Wallets
+slug: wallets/react/solana-wallets/get-started
+---
+
+
+ Solana Wallet support is in beta. Please [reach
+ out](mailto:support@alchemy.com) if you run into any issues or need support
+ integrating.
+
+
+
+
+
+
+We support Smart Wallets on Solana, enabling developers to create embedded wallets, sign transactions, sponsor gas, and batch transactions on Solana. Create both EVM and Solana wallets with social login and use those wallets alongside existing Solana libraries like `@solana/web3.js` to build seamless, gas-less user experiences!
+
+Check out the [demo](https://demo.alchemy.com/) to see Smart Wallets with gasless transactions on Solana in action.
+
+**Key features:**
+
+* Create Solana wallets with [email, social login, passkeys, etc](/docs/wallets/react/getting-started).
+* Sign Solana messages and transactions
+* Send Solana transactions
+* Sponsor gas and rent for Solana transactions
+* Batch multiple Solana instructions in a single transaction
+
+This guide will walk you through setting up and using Smart Wallets on Solana.
+
+### Prerequisites
+
+* Set up social login methods and user [authentication](/docs/wallets/react/getting-started)
+* Install a Solana library, such as `@solana/web3.js` to handle transaction construction and submission
+ ```bash
+ npm install @solana/web3.js
+ ```
+* Get an API key from Alchemy. Create an app on your [dashboard](https://dashboard.alchemy.com/apps) and enable Solana **and** EVM networks (you'll need both).
+* Install the latest `@account-kit` packages:
+
+
+ ```bash yarn
+ yarn add @account-kit/infra @account-kit/react
+ ```
+
+ ```bash npm
+ npm i -s @account-kit/infra @account-kit/react
+ ```
+
+
+# Creating a Solana Wallet
+
+First, set up user login and [authentication](/docs/wallets/react/getting-started) if you haven't already.
+
+Next, add Solana to your configuration by using the `solana` parameter when calling `createConfig` in the config.ts file and replace the API key with your key.
+
+*Note: It is required to set the chain parameter in the config. You can choose any EVM chain that your app has enabled like `sepolia`.*
+
+```ts config.ts
+import { cookieStorage, createConfig } from "@account-kit/react";
+import { Connection } from "@solana/web3.js";
+import { sepolia } from "@account-kit/infra";
+...
+createConfig({
+ ...otherConfig
+ chain: sepolia,
+ solana: {
+ connection: new Connection(
+ "https://solana-devnet.g.alchemy.com/v2/",
+ {
+ wsEndpoint: "wss://api.devnet.solana.com",
+ commitment: "confirmed",
+ }
+ ),
+ policyId: "" // Optional - gas/rent sponsorship policy ID: https://dashboard.alchemy.com/gas-manager
+ }
+}
+```
+
+# Getting a Solana Wallet address
+
+Once a user has been authenticated, you can retrieve their Solana wallet address in 2 ways:
+
+## Usin the `useSolanaSigner` hook
+
+If you just want the Solana wallet address and signer, we recommending using this hook.
+
+```jsx
+import { useSolanaSigner } from "@account-kit/react";
+
+function MyComponent() {
+ const signer = useSolanaSigner({});
+
+ if (!signer) {
+ return
Loading signer...
;
+ }
+
+ return
Solana Address: {signer.address}
;
+}
+```
+
+## Using the `useSolanaTransaction` hook
+
+If you want to connect to a user’s Solana Wallet and send transactions, you should use the [`useSolanaTransaction`](/wallets/reference/account-kit/react/hooks/useSolanaTransaction) hook. This hook also exposes the Solana wallet address through the `signer.address` parameter.
+
+## Not using React hooks?
+
+If you're not using React, use lower level libraries to convert your Alchemy signer instance into a Solana-compatible signer using the `SolanaSigner` class. [See Signer Solana docs](/wallets/react/solana-wallets/get-started).
diff --git a/fern/wallets/pages/react/ui-components.mdx b/fern/wallets/pages/react/ui-components.mdx
new file mode 100644
index 000000000..e1e954595
--- /dev/null
+++ b/fern/wallets/pages/react/ui-components.mdx
@@ -0,0 +1,171 @@
+---
+title: Authentication with UI components
+description: How to use our pre-built authentication component in your React app
+slug: wallets/react/ui-components
+---
+
+Smart Wallets allows you to use pre-built, [highly customizable](https://demo.alchemy.com/) UI components to handle authenticating your users. These components provide flexibility to:
+
+* Use the pre-built [modal](#modal-auth) or [embed](#embedded-auth) the auth card directly in your app
+* Choose from [multiple authentication methods](#available-authentication-methods)
+* Customize [authentication method UI](#customize-authentication-ui)
+* Customize [theme](/wallets/react/customization/theme)
+
+
+ Tailwind CSS is a required dependency for using Alchemy Account Kit UI
+ components. However, Alchemy Smart Wallets hooks function independently and do
+ not require Tailwind.
+
+
+## Available Authentication Methods
+
+Smart Wallets supports several authentication methods that you can implement with pre-built UI components:
+
+
+
+Each authentication method page includes specific configuration options and examples for both pre-built UI components and custom UI implementations.
+
+**Multi-Factor Authentication (MFA)**\
+If your users have an authenticator app (TOTP) set up, the UI components will automatically prompt them for their 6-digit code after they complete any primary login method (e.g., Email OTP, Email Magic Link, or Social Login). No extra configuration is required in the UI component – just [enable MFA](/wallets/react/mfa/setup-mfa) for your users.
+
+## Modal auth
+
+Assuming your application has been [set up](/wallets/react/quickstart), using UI components is the easiest way to authenticate users. All you have to do is leverage the [`useAuthModal`](/wallets/reference/account-kit/react/hooks/useAuthModal) hook and provide users a CTA to open the modal.
+
+```tsx twoslash
+import React from "react";
+import { useAuthModal } from "@account-kit/react";
+
+export default function MyPage() {
+ const { openAuthModal } = useAuthModal();
+
+ return Authenticate;
+}
+```
+
+That's it! When the user clicks that button, the modal will open and they can complete authentication. Once they are authenticated, you can use the [`useAccount`](/wallets/reference/account-kit/react/hooks/useAccount) hook to get the logged in user's SCA address.
+
+**Want to display a loading state during authentication?**
+For authentication with redirects in a modal, you may want to add a loading state when users are waiting for authentication to complete.
+To do this, manage the loading state directly using our hooks, as shown below. In embedded authentication and non-redirect flows, such as passkey authentication, we handle the loading state for you.
+
+```tsx twoslash
+import { useAuthModal, useSignerStatus } from "@account-kit/react";
+import { useEffect } from "react";
+
+const { openAuthModal } = useAuthModal();
+const { isAuthenticating } = useSignerStatus();
+
+useEffect(() => {
+ if (isAuthenticating) {
+ openAuthModal();
+ }
+}, [openAuthModal, isAuthenticating]);
+```
+
+## Embedded auth
+
+The body of the Auth Modal is also exported for you to use directly in your application. This is useful if you don't want a modal flow for login and want a standalone page using the card.
+
+```tsx twoslash
+import React from "react";
+import { AuthCard } from "@account-kit/react";
+
+export default function MyLoginPage() {
+ return (
+
+
+
+ );
+}
+```
+
+That's it! The user can now input their credentials and complete login. Once they are authenticated, you can use the [`useAccount`](/wallets/reference/account-kit/react/hooks/useAccount) hook to get the logged in user's SCA address.
+
+## Customize authentication UI
+
+When rendering the authentication component to your users, it's possible to customize the various sections a user will see as well as the content of the modal.
+
+
+ The easiest way to configure your UI components is using the [Demo
+ App](https://demo.alchemy.com/).
+
+
+**How does the UI config work?**
+
+* `auth` property accepts a `sections` object
+* `sections` object is an array of arrays representing the various sections in descending order, each section (element in the outer array) separated by a divider in the UI
+* each item within a section is an `AuthTypes` objects containing configuration parameters for that authentication method.
+
+**The following [`AuthTypes`](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/react/src/components/auth/types.ts)
+can be passed into sections:**
+
+### Email
+
+This adds an email login to a section. For detailed information on email authentication, see [Email OTP](/wallets/authentication/login-methods/email-otp) and [Email Magic Link](/wallets/authentication/login-methods/email-magic-link) pages.
+
+### Passkey
+
+This adds a passkey login method to a section. For detailed information on passkey authentication, see [Passkey Login](/wallets/react/login-methods/passkey-login) and [Passkey Signup](/wallets/react/login-methods/passkey-login) pages.
+
+### External wallets
+
+This adds an external wallet login method to a section. This allows users to connect to your app with existing EOAs via browser extension or WalletConnect.
+
+```ts
+type ExternalWalletsAuthType = {
+ type: "external_wallets";
+ // if this is undefined, wallet connect will not be displayed
+ walletConnect?: WalletConnectParameters;
+};
+```
+
+### Social
+
+This adds social authentication methods to a section. For detailed information on social authentication, see [Social Login](/wallets/authentication/login-methods/social-login) and [Custom Social Providers](/wallets/react/login-methods/social-providers) pages.
+
+### Example
+
+Here's an example UI configuration which adds 3 sections to the auth modal. The first section contains an email auth, the second section is for passkey login and 2 social auth logins (google and facebook), and the third section is external wallets.
+
+```ts twoslash
+import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+const uiConfig: AlchemyAccountsUIConfig = {
+ illustrationStyle: "outline",
+ auth: {
+ sections: [
+ [{ type: "email" }],
+ [
+ { type: "passkey" },
+ { type: "social", authProviderId: "google", mode: "popup" },
+ { type: "social", authProviderId: "facebook", mode: "popup" },
+ ],
+ [
+ {
+ type: "external_wallets",
+ walletConnect: { projectId: "your-project-id" },
+ },
+ ],
+ ],
+ addPasskeyOnSignup: false,
+ },
+};
+
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ },
+ uiConfig,
+);
+```
+
+This example would output a modal similar to this image:
+
+
+
+## Need More Control?
+
+If you need complete control over the user experience, you can implement your own custom UI using Smart Wallets hooks. See the [Authentication with React Hooks](/wallets/react/react-hooks) page and the individual authentication method pages for detailed implementations.
diff --git a/fern/wallets/pages/recipes/hyperliquid-wallets.mdx b/fern/wallets/pages/recipes/hyperliquid-wallets.mdx
new file mode 100644
index 000000000..e054d4e58
--- /dev/null
+++ b/fern/wallets/pages/recipes/hyperliquid-wallets.mdx
@@ -0,0 +1,311 @@
+---
+title: Hyperliquid Transactions Quickstart
+description: Step-by-step guide to let users send transactions on hyperliquid.
+slug: wallets/recipes/hyperliquid-wallets
+---
+
+By the end of this tutorial, you’ll have an application integrated with Alchemy Wallets, enabling email and social login for user authentication, and the ability to sign and send transactions seamlessly.
+
+***
+
+## Getting Started Instructions
+
+## 1. Create a Custom Chain Config
+
+Extend [viem’s custom chains](https://viem.sh/docs/chains/introduction#custom-chains) to create a chain definition for HyperEVM, as it's not a defined chain on viem yet. This is the chain config you’ll be passing into your viem client in step #3.
+
+```tsx
+import { defineChain } from "viem";
+
+export const hype = defineChain({
+ id: 999,
+ name: "Hype",
+ nativeCurrency: {
+ decimals: 18,
+ name: "Hype",
+ symbol: "HYPE",
+ },
+ rpcUrls: {
+ default: {
+ http: ["https://hyperliquid-mainnet.g.alchemy.com/v2/{API_KEY}"],
+ webSocket: ["wss://hyperliquid-mainnet.g.alchemy.com/v2/{API_KEY}"],
+ },
+ },
+ blockExplorers: {
+ default: { name: "Explorer", url: "https://hyperevmscan.io/" },
+ },
+});
+```
+
+Wrap it in `defineAlchemyChain` to create an alchemy chain to pass into your `config.tsx`
+
+```tsx
+import { defineAlchemyChain } from "@account-kit/infra";
+
+const chain = defineAlchemyChain({
+ chain: hype,
+ rpcBaseUrl: `https://hyperliquid-mainnet.g.alchemy.com/v2/API_KEY`,
+});
+
+...
+//config.tsx
+export const config = createConfig(
+ {
+ transport: alchemy({ apiKey: "API_KEY" }),
+ chain: chain,
+ ssr: true, // more about ssr: https://www.alchemy.com/docs/wallets/troubleshooting/ssr
+ storage: cookieStorage, // more about persisting state with cookies: https://www.alchemy.com/docs/wallets/troubleshooting/ssr#persisting-the-account-state
+ enablePopupOauth: true, // must be set to "true" if you plan on using popup rather than redirect in the social login flow
+ policyId: "policy_id",
+ },
+ uiConfig
+);
+```
+
+## 2. Set Up Web2 Login with an Alchemy Signer
+
+#### Working with React?
+
+Follow this [Quickstart](https://www.alchemy.com/docs/wallets/react/quickstart) guide to set up a new project.
+
+The most important step is getting your API Key (`NEXT_PUBLIC_ALCHEMY_API_KEY`) and ensuring your project is configured correctly on your dashboard. Make sure you have Hyperliquid enabled as a network on your app.
+
+
+
+
+
+Next, navigate to your `page.tsx`, and get the embedded EOA address using [useSigner()](https://www.alchemy.com/docs/wallets/reference/account-kit/react/hooks/useSigner). This new embedded EOA, also known as a signer, will be where users assets live and will sign transactions.
+
+```tsx
+const signer = useSigner();
+```
+
+Note: in order access your embedded EOA created by Alchemy Signer, you need to have finished authentication. To check your authentication status, you can check [useSignerStatus()](https://www.alchemy.com/docs/wallets/reference/account-kit/react/hooks/useSignerStatus). For example:
+
+```tsx
+...
+ if (signerStatus.isConnected && signer) {
+ const address = signer.getAddress();
+ console.log("Connected signer address:", address);
+ }
+ ...
+```
+
+#### Not Working With React?
+
+That’s okay! We have lower level methods we can leverage here to access your signer!
+
+You can follow this [Quickstart](https://www.alchemy.com/docs/wallets/signer/quickstart), to use our '@account-kit/signer' package directly to create and use our wallets.
+
+#### Create a Signer Instance
+
+```tsx
+import { AlchemyWebSigner } from "@account-kit/signer";
+
+export const signer = new AlchemyWebSigner({
+ client: {
+ connection: {
+ apiKey: "API_KEY",
+ },
+ iframeConfig: {
+ iframeContainerId: "alchemy-signer-iframe-container",
+ },
+ },
+});
+```
+
+#### Authenticate a User
+
+Next, you’ll need to authenticate your user before you can use the signer. In this example, we use email auth, but we support a number of other auth methods. Check out our [guides](https://www.alchemy.com/docs/wallets/react/react-hooks) to complete authentication.
+
+```tsx
+import { signer } from "./signer";
+
+const result = await signer.authenticate({
+ type: "email",
+ email: "example@mail.com",
+});
+...
+```
+
+Once you finish authenticating, you can access your signer!
+
+## 3. Send Transactions
+
+Got your signer/embedded EOA address? Now you are ready to send transactions! Alchemy signer supports signing messages as raw hashes. You can use methods including `signMessage`, `signTypedData`, and `signTransaction`.
+
+```tsx
+const signedTx = await signer.signTransaction(txRequest);
+```
+
+Then you can use a generic wallet client to send transactions! For example, if you are using viem, then you can use the `toViemAccount` method which will allow you to use the signer with a [WalletClient](https://viem.sh/docs/clients/wallet#local-accounts-private-key-mnemonic-etc).
+
+```tsx
+import { createWalletClient, http, custom, parseEther } from "viem";
+
+export const walletClient = createWalletClient({
+ transport: http("https://hyperliquid-mainnet.g.alchemy.com/v2/{API_KEY}"),
+ chain: Hype,
+ account: signer.toViemAccount(),
+});
+
+const txRequest = await walletClient.prepareTransactionRequest({
+ account: acct,
+ to: "0x0000000000000000000000000000000000000000",
+ value: parseEther("0.0001"),
+});
+
+// Sign transaction
+
+const txHash = await walletClient.sendRawTransaction({
+ serializedTransaction: signedTx,
+});
+```
+
+## 4. Sponsoring User Operations
+
+Alchemy gas sponsorship lets your users send transactions on HyperEVM without holding the native token for gas. To start, make sure you have a gas policy ID configured in your app dashboard and pass it to your client initialization. Learn more in [Sponsor Gas](https://www.alchemy.com/docs/wallets/transactions/sponsor-gas).
+
+As an example, we will be sponsoring a HyperEVM user operation opening a limit order, using the HyperCore Writer contract. Start by creating some helpers to encode the call.
+
+```ts
+// hyperliquidOrder.ts
+import {
+ encodeAbiParameters,
+ encodeFunctionData,
+ hexToBytes,
+ toHex,
+} from "viem";
+
+export const CORE_WRITER_ADDRESS =
+ "0x3333333333333333333333333333333333333333" as const;
+
+export const HYPERLIQUID_CALLDATA = (() => {
+ const asset = 0; // BTC
+ const isBuy = true;
+ const limitPx = 100000000000n; // $100,000
+ const sz = 100000n; // 0.001 BTC
+ const reduceOnly = false;
+ const encodedTif = 2; // GTC
+ const cloid = 0n;
+
+ const payloadHex = encodeAbiParameters(
+ [
+ { type: "uint32" },
+ { type: "bool" },
+ { type: "uint64" },
+ { type: "uint64" },
+ { type: "bool" },
+ { type: "uint8" },
+ { type: "uint128" },
+ ],
+ [asset, isBuy, limitPx, sz, reduceOnly, encodedTif, cloid],
+ );
+
+ // encoding version (1 byte) + action ID (3 bytes)
+ const prefix = new Uint8Array([0x01, 0x00, 0x00, 0x01]);
+
+ const payload = hexToBytes(payloadHex);
+ const actionBytes = new Uint8Array(prefix.length + payload.length);
+ actionBytes.set(prefix, 0);
+ actionBytes.set(payload, prefix.length);
+
+ const coreWriterAbi = [
+ {
+ type: "function",
+ name: "sendRawAction",
+ stateMutability: "nonpayable",
+ inputs: [{ name: "data", type: "bytes", internalType: "bytes" }],
+ outputs: [],
+ },
+ ] as const;
+
+ return encodeFunctionData({
+ abi: coreWriterAbi,
+ functionName: "sendRawAction",
+ args: [toHex(actionBytes)],
+ });
+})();
+```
+
+### Using React
+
+If you are working in React, you can use the [useSendUserOperation](https://www.alchemy.com/docs/wallets/reference/account-kit/react/hooks/useSendUserOperation) hook. Make sure you have set up your config from step 1.
+
+```tsx
+import React from "react";
+import {
+ useSendUserOperation,
+ useSmartAccountClient,
+} from "@account-kit/react";
+import { CORE_WRITER_ADDRESS, HYPERLIQUID_CALLDATA } from "./hyperliquidOrder";
+
+function ComponentWithSendUserOperation() {
+ const { client } = useSmartAccountClient({});
+
+ const { sendUserOperation, isSendingUserOperation } = useSendUserOperation({
+ client,
+ waitForTxn: true,
+ onSuccess: ({ hash, request }) => {
+ // [optional] handle success
+ },
+ onError: (error) => {
+ // [optional] handle error
+ },
+ });
+
+ return (
+
+ );
+}
+
+export default ComponentWithSendUserOperation;
+```
+
+### Without React
+
+If you are not using React, you can send the same user operation directly with the [Modular Account V2 client](https://www.alchemy.com/docs/wallets/reference/account-kit/smart-contracts/functions/createModularAccountV2Client) in account-kit, passing in the chain config from step 1 and signer from step 2.
+
+```ts
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { CORE_WRITER_ADDRESS, HYPERLIQUID_CALLDATA } from "./hyperliquidOrder";
+
+const modularAccountV2Client = await createModularAccountV2Client({
+ chain, // chain config from step 1
+ signer, // signer from step 2
+ transport,
+ policyId: "policy_id",
+});
+
+await modularAccountV2Client.sendUserOperation({
+ uo: {
+ target: CORE_WRITER_ADDRESS,
+ data: HYPERLIQUID_CALLDATA,
+ value: 0n,
+ },
+});
+```
+
+### Resources
+
+* [Alchemy Signer](https://www.alchemy.com/docs/wallets/signer/what-is-a-signer)
+* [Connect your EOAs](https://www.alchemy.com/docs/wallets/react/login-methods/eoa-login)
+* [Alchemy Gas Sponsorship](https://www.alchemy.com/docs/wallets/transactions/sponsor-gas)
+* [Viem Custom Chain Support](https://viem.sh/docs/chains/introduction#custom-chains)
+* [Full Example](https://github.com/aashkrishnan/hyperevmguide/blob/ash/hyperevm/app/page.tsx)
+* [Bridge Funds to Hype](https://debridge.finance/)
diff --git a/fern/wallets/pages/recipes/multi-chain-setup.mdx b/fern/wallets/pages/recipes/multi-chain-setup.mdx
new file mode 100644
index 000000000..ff6ed73af
--- /dev/null
+++ b/fern/wallets/pages/recipes/multi-chain-setup.mdx
@@ -0,0 +1,143 @@
+---
+title: Multi-chain Apps
+description: Learn how to build multi-chain apps with Smart Wallets.
+slug: wallets/recipes/multi-chain-setup
+---
+
+Smart Wallets supports multi-chain apps, allowing you to build applications that interact with multiple blockchains. This guide will show you how to set up your app to work with multiple chains.
+
+## Update your config
+
+In order to support multiple chains in your app, the first thing you need to do is update your `createConfig` call to include the chains you want to support.
+
+
+
+
+```ts twoslash config.ts
+// @noErrors
+import { createConfig } from "@account-kit/react";
+import { sepolia, mainnet } from "@account-kit/infra";
+
+export const config = createConfig({
+ apiKey: "ALCHEMY_API_KEY",
+ // this is the default chain
+ chain: sepolia,
+ chains: [
+ {
+ chain: mainnet,
+ // optional: you can specify a policy ID for this chain, if you want to sponsor gas
+ policyId: "MAINNET_GAS_MANAGER_POLICY_ID",
+ },
+ {
+ chain: sepolia,
+ // optional: you can specify a policy ID for this chain, if you want to sponsor gas
+ policyId: "SEPOLIA_GAS_MANAGER_POLICY_ID",
+ },
+ ],
+});
+```
+
+## Change chains
+
+Once your app is configured to use multiple chains, you can switch between them at any time using the [`useChain`](/wallets/reference/account-kit/react/hooks/useChain) hook.
+
+```tsx twoslash
+import React from "react";
+import { useChain } from "@account-kit/react";
+import { mainnet, sepolia } from "@account-kit/infra";
+
+export default function MyComponent() {
+ const { chain, setChain } = useChain();
+
+ return (
+
+ );
+}
+```
+
+***
+
+## Best Practices
+
+* Let users change networks/assets in the Coinbase modal (default here is card ➜ ETH).
+* For user-level analytics you can pass `partnerUserId` when generating the onramp URL.
+
+## Success!
+
+Users can now click "Buy Crypto", complete their purchase in a popup, and immediately spend the ETH that lands in their smart wallet.
+
+## Next Steps
+
+* **Sponsor their first transaction** – set up [Gas Manager](/wallets/transactions/sponsor-gas) so new users don't pay gas.
+
+## Resources
+
+* [Coinbase Onramp API](https://docs.cdp.coinbase.com/onramp/docs/api-onramp-initializing)
+* [Alchemy Smart Wallets Quickstart](https://www.alchemy.com/docs/wallets/react/quickstart)
+* [Coinbase Onchainkit](https://www.npmjs.com/package/@coinbase/onchainkit)
+* [Coinbase getOnrampBuyUrl](https://docs.base.org/onchainkit/fund/get-onramp-buy-url)
+* [JWT and Session Token Example](https://github.com/coinbase/onramp-demo-application/blob/51733031e49ed4b505291ee7acbdbee429dceb3c/app/utils/sessionTokenApi.ts)
diff --git a/fern/wallets/pages/recipes/overview.mdx b/fern/wallets/pages/recipes/overview.mdx
new file mode 100644
index 000000000..0812e98ef
--- /dev/null
+++ b/fern/wallets/pages/recipes/overview.mdx
@@ -0,0 +1,61 @@
+---
+title: Recipes
+description: Step-by-step guides for common Smart Wallet features and integrations.
+slug: wallets/recipes/overview
+---
+
+Learn how to implement common Smart Wallet features and integrations in your app.
+
+## Popular
+
+
+
+ Learn how to format your transaction payload to send tokens.
+
+
+ Learn how to onramp funds to embedded smart wallets with Coinbase.
+
+
+ Learn how to send transactions on Hyperliquid using Alchemy signer and
+ login.
+
+
+
+## Coming Soon
+
+
+
+ Learn how to create and manage wallets with multiple owners.
+
+
+ Set up spending limits and permissions for AI agents to transact on behalf
+ of users.
+
+
+ Integrate Smart Wallets smart wallets into existing Wagmi-based
+ applications.
+
+
+ Enable users to move assets between different blockchains seamlessly.
+
+
+ Upgrade existing EOA wallets to smart contract functionality with EIP-7702.
+
+
+
+> Don't see what you're looking for? [Open an issue](https://github.com/alchemyplatform/aa-sdk/issues) or [email us](mailto:wallets@alchemy.com)!
diff --git a/fern/wallets/pages/recipes/programmatic-wallet-creation.mdx b/fern/wallets/pages/recipes/programmatic-wallet-creation.mdx
new file mode 100644
index 000000000..5220c6516
--- /dev/null
+++ b/fern/wallets/pages/recipes/programmatic-wallet-creation.mdx
@@ -0,0 +1,281 @@
+---
+title: How to programmatically create a wallet
+description: Generate a signer, initialize a Smart Wallet Client, sponsor gas with a policy, derive the counterfactual address and deploy by sending the first UserOperation.
+slug: wallets/recipes/programmatic-wallet-creation
+---
+
+This recipe shows how to programmatically create a smart wallet: you’ll generate a signer, spin up a Smart Wallet Client, read the counterfactual address before deployment and deploy the wallet by sending your first gas sponsored `UserOperation` (prepared → signed → sent).
+
+
+
+ You'll need the following packages for this recipe:
+
+ * **@account-kit/signer**: Server wallet signer and access key generation
+ * **@account-kit/infra**: Alchemy transport & chain constants (e.g., `arbitrumSepolia`)
+ * **@account-kit/wallet-client**: Smart Wallet Client (prepare/sign/send flows)
+ * **dotenv**: Read .env for your API key & policy id
+ * **typescript + tsx + @types/node**: Run TypeScript files directly
+
+ ```bash
+ npm i @account-kit/signer @account-kit/infra @account-kit/wallet-client dotenv
+ npm i -D typescript tsx @types/node
+ ```
+
+
+
+ Create a `.env` in your project root:
+
+ ```bash
+ # Get your app API key: https://dashboard.alchemy.com/apps
+ ALCHEMY_API_KEY=YOUR_API_KEY
+ # Get your gas sponsorship policy ID: https://dashboard.alchemy.com/services/gas-manager/configuration
+ ALCHEMY_POLICY_ID=YOUR_POLICY_ID
+ # Generate a secure access key for server wallet authentication - see step 3 below
+ ACCESS_KEY=your-secure-access-key-here
+ ```
+
+
+
+ Server wallets enable backend applications to programmatically control wallets using access keys, without requiring interactive authentication. This is perfect for automated systems, batch operations, or when you need to sign transactions from your backend.
+
+ **How server wallets work:**
+
+ * You generate a secure access key that never leaves your server
+ * Alchemy derives a public key from your access key for authentication
+ * The access key is used to sign transactions and messages on behalf of users
+ * No private keys are stored or transmitted to Alchemy's servers
+
+ ```ts
+ import { generateAccessKey } from "@account-kit/signer";
+
+ // Generate a secure access key (do this once and store securely)
+ const accessKey = generateAccessKey();
+ console.log("Access key:", accessKey);
+
+ // Add it to your .env file - you'll need it to control the server signer
+ ```
+
+
+ **Critical: Save your access key securely!**
+
+ This access key is required to control your server wallet and cannot be recovered if lost. Make sure to store it in a secure location.
+
+
+
+
+ ```ts
+ import { createServerSigner } from "@account-kit/signer";
+
+ const signer = await createServerSigner({
+ auth: {
+ accessKey: process.env.ACCESS_KEY!,
+ },
+ connection: {
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ },
+ });
+
+ console.log("Signer address:", await signer.getAddress());
+ ```
+
+
+
+ ```ts
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+ import { alchemy, arbitrumSepolia } from "@account-kit/infra";
+
+ const transport = alchemy({ apiKey: process.env.ALCHEMY_API_KEY! });
+
+ const client = createSmartWalletClient({
+ transport,
+ chain: arbitrumSepolia,
+ signer,
+ });
+ ```
+
+
+
+ The counterfactual address is the account address associated with the given signer but the account contract hasn't been deployed yet.
+
+ ```ts
+ const account = await client.requestAccount();
+ const address = account.address;
+ console.log("Counterfactual address:", address);
+ ```
+
+
+
+ Use the **capabilities** pipeline with `paymasterService` to sponsor gas via your policy and deploy the account contract by sending a gas sponsored UserOperation.
+
+ ```ts
+ const prepared = await client.prepareCalls({
+ from: address,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ data: "0x",
+ value: "0x0",
+ },
+ ], // minimal call to deploy the account contract
+ capabilities: {
+ paymasterService: { policyId: process.env.ALCHEMY_POLICY_ID! },
+ },
+ });
+
+ const signed = await client.signPreparedCalls(prepared);
+ const sent = await client.sendPreparedCalls(signed);
+
+ const txHash = await client.waitForCallsStatus({ id: sent.preparedCallIds[0] });
+ console.log("Prepared call IDs:", sent.preparedCallIds);
+ console.log("Tx hash:", txHash);
+ ```
+
+
+ For non-sponsored path, remove the `paymasterService` capability in
+ `prepareCalls` and **fund the account** to pay gas. The rest of the flow is
+ unchanged.
+
+
+ ### Full script (copy-paste)
+
+ ```ts provision.ts
+ import "dotenv/config";
+ import { createServerSigner } from "@account-kit/signer";
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+ import { alchemy, arbitrumSepolia } from "@account-kit/infra";
+
+ const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY!;
+ const ALCHEMY_POLICY_ID = process.env.ALCHEMY_POLICY_ID!;
+ const ACCESS_KEY = process.env.ACCESS_KEY!;
+
+ if (!ALCHEMY_API_KEY || !ALCHEMY_POLICY_ID || !ACCESS_KEY) {
+ throw new Error("Missing ALCHEMY_API_KEY, ALCHEMY_POLICY_ID, or ACCESS_KEY in env");
+ }
+
+ async function main() {
+ // 1) Create server signer using access key
+ const signer = await createServerSigner({
+ auth: {
+ accessKey: ACCESS_KEY,
+ },
+ connection: {
+ apiKey: ALCHEMY_API_KEY,
+ },
+ });
+
+ // 2) Transport + Smart Wallet Client
+ const transport = alchemy({ apiKey: ALCHEMY_API_KEY });
+ const client = createSmartWalletClient({
+ transport,
+ chain: arbitrumSepolia,
+ signer,
+ });
+
+ // 3) Account & counterfactual address
+ const account = await client.requestAccount();
+ const address = account.address;
+ console.log("Counterfactual address:", address);
+
+ // 4) Prepare → sign → send (sponsored) to trigger deployment
+ const prepared = await client.prepareCalls({
+ from: address,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ data: "0x",
+ value: "0x0",
+ },
+ ], // minimal call to deploy the account contract
+ capabilities: {
+ paymasterService: { policyId: ALCHEMY_POLICY_ID },
+ },
+ });
+
+ const signed = await client.signPreparedCalls(prepared);
+ const sent = await client.sendPreparedCalls(signed);
+
+ // 5) Wait for inclusion → tx hash
+ const txHash = await client.waitForCallsStatus({
+ id: sent.preparedCallIds[0],
+ });
+
+ console.log("Prepared call IDs:", sent.preparedCallIds);
+ console.log("Tx hash:", txHash);
+
+ return { address, preparedCallIds: sent.preparedCallIds, txHash };
+ }
+
+ main().then(
+ (res) => {
+ console.log("✅ Wallet provisioned:", res);
+ process.exit(0);
+ },
+ (err) => {
+ console.error("❌ Failed:", err);
+ process.exit(1);
+ },
+ );
+ ```
+
+
+
+ ```bash
+ # one-time per project
+ npm i @account-kit/signer @account-kit/infra @account-kit/wallet-client dotenv
+ npm i -D typescript tsx @types/node
+
+ # then run
+ npx tsx provision.ts
+ ```
+
+ You should see:
+
+ * `Counterfactual address: 0x…`
+ * `Prepared call IDs: [...]`
+ * `Tx hash: 0x…` (open on an **Arbitrum Sepolia** explorer to see the deployment)
+
+ Example:
+
+ ```shell
+ Counterfactual address: 0x022fA5358476f0EB881138BD822E5150AFb36c5B
+ Prepared call IDs: [
+ '0x0000000000000000000000000000000000000000000000000000000000066eee7d4670e76c7186cf2fb9d448e3b8348e316bdb5b142c3ff3149aa703805c81cb'
+ ]
+ Tx hash: {
+ id: '0x0000000000000000000000000000000000000000000000000000000000066eee7d4670e76c7186cf2fb9d448e3b8348e316bdb5b142c3ff3149aa703805c81cb',
+ status: 'success',
+ atomic: true,
+ chainId: 421614,
+ receipts: [
+ {
+ status: 'success',
+ blockHash: '0x180071dc55dbcf7d31dd2fbe1c1a58e3cb5564d0b59c465c4f558236340cecd3',
+ blockNumber: 196845735n,
+ gasUsed: 208770n,
+ transactionHash: '0x2e625854abcb557bc2bc02e97d2f3deaae544a2aface000fbcf1c3573fee1526',
+ logs: [Array]
+ }
+ ],
+ statusCode: 200,
+ version: '2.0.0'
+ }
+ ✅ Wallet provisioned: {
+ address: '0x022fA5358476f0EB881138BD822E5150AFb36c5B',
+ preparedCallIds: [
+ '0x0000000000000000000000000000000000000000000000000000000000066eee7d4670e76c7186cf2fb9d448e3b8348e316bdb5b142c3ff3149aa703805c81cb'
+ ],
+ txHash: {
+ id: '0x0000000000000000000000000000000000000000000000000000000000066eee7d4670e76c7186cf2fb9d448e3b8348e316bdb5b142c3ff3149aa703805c81cb',
+ status: 'success',
+ atomic: true,
+ chainId: 421614,
+ receipts: [ [Object] ],
+ statusCode: 200,
+ version: '2.0.0'
+ }
+ }
+ ```
+
+
+
+And when opened in an Arbitrum Sepolia explorer, you should see the [deployment](https://sepolia.arbiscan.io/address/0x022fA5358476f0EB881138BD822E5150AFb36c5B#internaltx), congrats you have just learned how to programmatically create a smart wallet 🎉
diff --git a/fern/wallets/pages/recipes/send-usdc.mdx b/fern/wallets/pages/recipes/send-usdc.mdx
new file mode 100644
index 000000000..210b33997
--- /dev/null
+++ b/fern/wallets/pages/recipes/send-usdc.mdx
@@ -0,0 +1,126 @@
+---
+title: Send USDC (or other ERC-20s)
+description: Learn how to build and send a transaction that transfers USDC from a smart account using Smart Wallets.
+slug: wallets/recipes/send-usdc
+---
+
+In this recipe you'll construct an ERC-20 `transfer` call and submit it through a Smart Account Client. The same pattern works for **any** ERC-20 token; just swap the token address and number of decimals.
+
+> Prefer code? Jump straight to the **React** or **Core** tabs below.
+
+## Prerequisites
+
+1. Smart wallets integrated in your app (see the [React quickstart](/wallets/react/quickstart) or [Core quickstart](/wallets/core/quickstart)).
+
+* The quickstart uses Arbitrum Sepolia, so make sure your gas policy is configured for Arbitrum Sepolia if you're using the default settings.
+
+2. The USDC contract address for your target chain. For Arbitrum Sepolia this is `0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d`.
+
+* Visit [Circle's documentation](https://developers.circle.com/stablecoins/usdc-contract-addresses) for USDC addresses on other chains.
+
+## 1. Encode the `transfer` calldata
+
+```ts
+import { encodeFunctionData, parseAbi } from "viem";
+
+const usdcAddress = "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d";
+const recipient = "0xRecipientAddress";
+const amount = BigInt(10) * BigInt(1_000_000); // 10 USDC (6 decimals)
+
+const transferCalldata = encodeFunctionData({
+ abi: parseAbi(["function transfer(address,uint256) returns (bool)"]),
+ functionName: "transfer",
+ args: [recipient, amount],
+});
+```
+
+## 2. Send the transaction
+
+
+
+ ```tsx
+ import {
+ useSmartAccountClient,
+ useSendUserOperation,
+ } from "@account-kit/react";
+
+ export function SendUsdcButton() {
+ const { client } = useSmartAccountClient({});
+
+ const { sendUserOperation, sendUserOperationResult } = useSendUserOperation({
+ client,
+ waitForTxn: true,
+ onSuccess: () => {
+ console.log("USDC transfer successful!");
+ },
+ onError: (error) => {
+ console.error("USDC transfer failed:", error);
+ },
+ });
+
+ const handleClick = async () => {
+ if (!client) return;
+
+ sendUserOperation({
+ uo: {
+ target: usdcAddress,
+ data: transferCalldata,
+ value: BigInt(0), // no native value for ERC-20 transfers
+ },
+ });
+ };
+
+ return Send USDC;
+ }
+ ```
+
+
+
+ ```ts
+ import { createSmartAccountClient } from "@alchemy/aa-core";
+ // Set up your transport, signer and entryPoint as usual
+ const client = await createSmartAccountClient({
+ /* config */
+ });
+
+ const userOpHash = await client.sendUserOperation({
+ uo: {
+ target: usdcAddress,
+ data: transferCalldata,
+ value: BigInt(0),
+ },
+ });
+
+ console.log("USDC transfer userOp hash:", userOpHash);
+ ```
+
+
+
+## 3. Wait for the transaction to be mined
+
+If you're using the React hooks with `waitForTxn: true`, the transaction will automatically be waited for. You can access the transaction hash from `sendUserOperationResult`:
+
+```tsx
+const { sendUserOperation, sendUserOperationResult } = useSendUserOperation({
+ client,
+ waitForTxn: true,
+ onSuccess: () => {
+ console.log("Transaction hash:", sendUserOperationResult?.hash);
+ },
+});
+```
+
+Alternatively, you can manually wait for the transaction:
+
+```ts
+import { useWaitForUserOperationTransaction } from "@account-kit/react";
+
+const { waitForUserOp } = useWaitForUserOperationTransaction();
+const receipt = await waitForUserOp(userOpHash);
+```
+
+## Next steps
+
+* Parameterize the token address/decimals to support any ERC-20.
+* Batch multiple user operations in **one** request (e.g. aprrove, transfer, etc).
+* Combine with [sponsored gas](/wallets/transactions/sponsor-gas) for a completely gas-less UX.
diff --git a/fern/wallets/pages/recipes/smart-wallets-aave.mdx b/fern/wallets/pages/recipes/smart-wallets-aave.mdx
new file mode 100644
index 000000000..c2f048750
--- /dev/null
+++ b/fern/wallets/pages/recipes/smart-wallets-aave.mdx
@@ -0,0 +1,350 @@
+---
+title: Smart Wallets with Aave
+description: >-
+ Learn how to build DeFi applications that interact with Aave using Alchemy
+ Smart Wallets. This recipe covers supplying and withdrawing assets with both
+ Core and Wallets API.
+slug: wallets/recipes/smart-wallets-aave
+---
+
+Learn how to build DeFi applications that interact with Aave using Alchemy Smart Wallets. This recipe covers supplying and withdrawing assets with both [our Core library](/wallets/reference/aa-sdk/core) and our [Wallet APIs](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) for seamless user experiences.
+
+## Prerequisites
+
+1. Have DAI available on your chosen chain (default: Arbitrum).
+2. Alchemy API key, Account config and Gas Manager policy ID for sponsored transactions (you can follow the [setup here](https://www.alchemy.com/docs/wallets/react/setup))
+
+## Walkthrough
+
+Check out this video walkthrough, and follow along with the quick start below:
+
+
+
+
+
+## Quick Start
+
+1. **Clone and setup:**
+
+
+ ```bash HTTPS
+ git clone https://github.com/alchemyplatform/smart-wallets-aave.git
+ cd smart-wallets-aave
+ npm install
+ ```
+
+ ```bash SSH
+ git clone git@github.com:alchemyplatform/smart-wallets-aave.git
+ cd smart-wallets-aave
+ npm install
+ ```
+
+ ```bash "GitHub CLI"
+ gh repo clone alchemyplatform/smart-wallets-aave
+ cd smart-wallets-aave
+ npm install
+ ```
+
+
+2. **Configure environment:**
+
+ ```bash
+ cp .env.example .env
+ # Fill in your ALCHEMY_API_KEY, PRIVATE_KEY, and PAYMASTER_POLICY_ID
+ ```
+
+3. **Initialize smart wallet and get address:**
+
+ ```bash
+ npm run account
+ ```
+
+4. **Fund your smart wallet:**
+ Send 0.01+ DAI to the smart wallet address from step 3
+
+5. **Interact with Aave:**
+ ```bash
+ npm run supply # Supply assets to Aave
+ npm run withdraw # Withdraw assets from Aave
+ ```
+
+
+
+ The configuration is centralized in `src/config.ts` for easy customization:
+
+ ```typescript
+ import { arbitrum, sepolia } from "@account-kit/infra";
+
+ export const config = {
+ // Network configuration - change this to switch chains
+ chain: arbitrum, // Can be changed to sepolia, polygon, etc.
+
+ // Token and protocol addresses
+ addresses: {
+ dai: {
+ [arbitrum.id]: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1",
+ [sepolia.id]: "0x68194a729C2450ad26072b3D33ADaCbcef39D574",
+ },
+ aaveV3Pool: {
+ [arbitrum.id]: "0x794a61358D6845594F94dc1DB02A252b5b4814aD",
+ [sepolia.id]: "0x6Ae43d3271ff6888e7Fc43Fd7321a503ff738951",
+ },
+ },
+
+ // Transaction amounts
+ amounts: {
+ deposit: "0.01", // Amount to deposit
+ withdraw: "0.01", // Amount to withdraw
+ },
+ };
+ ```
+
+
+
+
+
+ This approach uses the direct `@aa-sdk/core` implementation for simpler, more streamlined Aave interactions.
+
+ ```typescript
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts";
+ import { alchemy } from "@account-kit/infra";
+ import { reserve, supply, withdraw } from "@aave/client/actions";
+ import {
+ currentChain,
+ getCurrentTokenAddress,
+ getCurrentAavePoolAddress,
+ config,
+ } from "./config";
+
+ // Supply assets to Aave
+ async function executeSupply() {
+ const alchemyApiKey = process.env.ALCHEMY_API_KEY!;
+ const privateKey = process.env.PRIVATE_KEY! as `0x${string}`;
+ const policyId = process.env.PAYMASTER_POLICY_ID! as `0x${string}`;
+
+ // Setup signer
+ const signer = LocalAccountSigner.privateKeyToAccountSigner(privateKey);
+
+ // Create smart account client with gas sponsorship
+ const smartAccountClient = await createModularAccountAlchemyClient({
+ transport: alchemy({ apiKey: alchemyApiKey }),
+ policyId, // Enables gas sponsorship
+ chain: currentChain,
+ signer,
+ });
+
+ const accountAddress = smartAccountClient.account.address;
+
+ // Get reserve data from Aave
+ const reserveDataResult = await reserve(client, {
+ market: evmAddress(getCurrentAavePoolAddress()),
+ underlyingToken: evmAddress(getCurrentTokenAddress()),
+ chainId: chainId(currentChain.id),
+ user: accountAddress,
+ });
+
+ const reserveData = reserveDataResult.value;
+
+ // Get supply transaction data
+ const supplyResult = await supply(client, {
+ market: reserveData.market.address,
+ amount: {
+ erc20: {
+ currency: reserveData.underlyingToken.address,
+ value: config.amounts.deposit,
+ },
+ },
+ sender: evmAddress(accountAddress),
+ chainId: reserveData.market.chain.chainId,
+ });
+
+ let supplyData = supplyResult.value;
+
+ // Handle transactions (approval + supply or just supply)
+ if (supplyData.__typename === "ApprovalRequired") {
+ // Send approval transaction
+ const approvalTx = await smartAccountClient.sendUserOperation({
+ account: smartAccountClient.account,
+ uo: {
+ target: supplyData.approval.to,
+ data: supplyData.approval.data,
+ },
+ });
+
+ await smartAccountClient.getUserOperationReceipt(approvalTx.hash);
+
+ // Send supply transaction
+ const supplyTx = await smartAccountClient.sendUserOperation({
+ account: smartAccountClient.account,
+ uo: {
+ target: supplyData.originalTransaction.to,
+ data: supplyData.originalTransaction.data,
+ value: BigInt(supplyData.originalTransaction.value),
+ },
+ });
+
+ console.log("Supply completed! Hash:", supplyTx.hash);
+ }
+ }
+
+ // Withdraw assets from Aave
+ async function executeWithdraw() {
+ // Setup same as supply...
+ const smartAccountClient = await createModularAccountAlchemyClient({
+ transport: alchemy({ apiKey: alchemyApiKey }),
+ policyId,
+ chain: currentChain,
+ signer,
+ });
+
+ // Get withdraw transaction data
+ const withdrawResult = await withdraw(client, {
+ market: reserveData.market.address,
+ amount: {
+ erc20: {
+ currency: reserveData.underlyingToken.address,
+ value: { exact: config.amounts.withdraw }, // or { max: true } for all
+ },
+ },
+ sender: evmAddress(accountAddress),
+ chainId: reserveData.market.chain.chainId,
+ });
+
+ let withdrawData = withdrawResult.value;
+
+ if (withdrawData.__typename === "TransactionRequest") {
+ const withdrawTx = await smartAccountClient.sendUserOperation({
+ account: smartAccountClient.account,
+ uo: {
+ target: withdrawData.to,
+ data: withdrawData.data,
+ value: BigInt(withdrawData.value),
+ },
+ });
+
+ console.log("Withdrawal completed! Hash:", withdrawTx.hash);
+ }
+ }
+ ```
+
+
+
+ This approach uses the higher-level wallet-client API with explicit prepare/sign/send workflow for Aave interactions.
+
+ ```typescript
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+ import { alchemy, arbitrum } from "@account-kit/infra";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+
+ // Setup smart wallet client
+ const smartWalletClient = createSmartWalletClient({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(privateKey),
+ chain: arbitrum,
+ transport: alchemy({ apiKey: alchemyApiKey }),
+ });
+
+ const account = await smartWalletClient.requestAccount();
+
+ // Get Aave operation data (supply/withdraw)
+ const aaveData = await getAaveOperationData(); // implementation same as Core approach
+
+ // Prepare transaction calls based on operation type
+ const calls = [];
+ if (aaveData.__typename === "ApprovalRequired") {
+ // For supply operations requiring approval
+ calls.push({
+ to: aaveData.approval.to,
+ data: aaveData.approval.data,
+ });
+ calls.push({
+ to: aaveData.originalTransaction.to,
+ data: aaveData.originalTransaction.data,
+ value: toHex(BigInt(aaveData.originalTransaction.value)),
+ });
+ } else if (aaveData.__typename === "TransactionRequest") {
+ // For direct operations (withdrawals, approved supplies)
+ calls.push({
+ to: aaveData.to,
+ data: aaveData.data,
+ value: toHex(BigInt(aaveData.value)),
+ });
+ }
+
+ // Prepare, sign, and send with gas sponsorship
+ const preparedCalls = await smartWalletClient.prepareCalls({
+ calls,
+ from: account.address,
+ capabilities: {
+ paymasterService: {
+ policyId: policyId, // Gas sponsorship
+ },
+ },
+ });
+
+ const signedCalls = await smartWalletClient.signPreparedCalls(preparedCalls);
+ const result = await smartWalletClient.sendPreparedCalls(signedCalls);
+
+ console.log(
+ "Aave operation completed! Hash:",
+ result.receipts![0]?.transactionHash,
+ );
+ ```
+
+
+
+
+
+ The repository includes convenient scripts for different approaches:
+
+ ```bash
+ # Core implementation (recommended)
+ npm run core:account # Initialize account
+ npm run core:supply # Supply to Aave
+ npm run core:withdraw # Withdraw from Aave
+
+ # Wallet APIs implementation
+ npm run wallet:account # Initialize account
+ npm run wallet:supply # Supply to Aave
+ npm run wallet:withdraw # Withdraw from Aave
+
+ # Default scripts (use Core)
+ npm run account # Defaults to core:account
+ npm run supply # Defaults to core:supply
+ npm run withdraw # Defaults to core:withdraw
+ ```
+
+
+
+## Success!
+
+You can now interact with Aave protocol through smart wallets with sponsored gas fees, enabling seamless DeFi experiences for supplying, withdrawing, and managing liquidity without gas complexity.
+
+## Resources
+
+* [Aave V3 Documentation](https://docs.aave.com/developers/)
+* [Alchemy Smart Wallets Quickstart](https://www.alchemy.com/docs/wallets/react/quickstart)
+* [Gas Manager Setup](https://www.alchemy.com/docs/setup-a-gas-manager-policy)
+* [Aave Contract Addresses](https://aave.com/docs/resources/addresses)
diff --git a/fern/wallets/pages/recipes/social-payments-and-defi.mdx b/fern/wallets/pages/recipes/social-payments-and-defi.mdx
new file mode 100644
index 000000000..b8497deba
--- /dev/null
+++ b/fern/wallets/pages/recipes/social-payments-and-defi.mdx
@@ -0,0 +1,32 @@
+---
+title: Social Payments and Defi
+description:
+slug: wallets/recipes/social-payments-and-defi
+---
+
+Below you will find two versions of a demo social payments and defi application called **ChainMoney**.
+
+
+
+Both versions of this application allow you to signup with email, send money to people by username, and earn yield on your balance.
+The first version below uses smart wallet react hooks and built-in auth components while the second uses Wallet APIs to do everything on the backend.
+
+## ChainMoney using AccountKit
+
+This version of the ChainMoney demo app uses our [react hooks](/docs/wallets/reference/account-kit/react) and built-in auth components.
+
+You can try our hosted demo below or head over to the github repository to see how it works in greater detail.
+
+* **Hosted Demo**: https://chain-money-account-kit.vercel.app/
+* **Github**: https://github.com/alchemyplatform/chain-money-account-kit
+
+## ChainMoney using Wallet APIs
+
+This version of the ChainMoney demo app uses our [wallet APIs](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account) on the backend.
+
+As opposed to the above example, this application uses [supabase auth](https://supabase.com/docs/guides/auth) though you can choose to replace this with any auth system of your choice!
+
+In addition, this implementation requires the developer to maintain their own security model through the management of a private key (or alternatively, several private keys) that [controls access to user accounts](https://github.com/alchemyplatform/chain-money-wallet-apis/blob/4b563fbbaebc6558aa65afbd588fb3e6699e6c2b/lib/wallet-service.ts#L123-L134).
+
+* **Hosted Demo**: https://chain-money-demo-theta.vercel.app/
+* **Github**: https://github.com/alchemyplatform/chain-money-wallet-apis
diff --git a/fern/wallets/pages/recipes/upgrade-to-smart-accounts.mdx b/fern/wallets/pages/recipes/upgrade-to-smart-accounts.mdx
new file mode 100644
index 000000000..9cb630da9
--- /dev/null
+++ b/fern/wallets/pages/recipes/upgrade-to-smart-accounts.mdx
@@ -0,0 +1,60 @@
+---
+title: Upgrade to Smart Accounts
+description: Learn how to upgrade existing wallets to smart accounts with two different approaches.
+slug: wallets/recipes/upgrade-to-smart-accounts
+---
+
+Smart accounts unlock seamless transaction flows and advanced features like social login, gas sponsorship, batching, multi-owner accounts, and more. You can easily upgrade your existing wallet connection and user authentication to smart wallets.
+
+## Upgrading External Wallets
+
+If you have an app that currently uses an EOA connector, such as Rainbow Kit, for user's wallets, you can add on Smart Wallets by swapping out the EOA connector for social login and our pre-built UI for EOA connection. This will enable you to use React hooks for wallet connection, social login, and sending of transactions.
+
+You can also use our social login with your existing wallet connection but you will need to build a custom UI to route to users between the two.
+
+Either way you can follow our getting started guides loosely for the steps necessary to integrate smart wallets into the tech stack most closely matching yours:
+
+
+
+ Add smart wallets to a React app from scratch
+
+
+ Add smart wallets to Other JavaScript Frameworks from scratch
+
+
+ Add smart wallets to React Native using Expo from scratch
+
+
+ Add smart wallets to Bare React Native from scratch
+
+
+
+## Upgrading Embedded EOAs
+
+If you have an existing embedded EOA wallet, you can enable gas sponsorship and batching, by upgrading wallets using EIP-7702.
+
+
+
+ Upgrade existing embedded EOA wallets to smart wallets using EIP-7702.
+
+
diff --git a/fern/wallets/pages/recipes/wallet-session-keys-app.mdx b/fern/wallets/pages/recipes/wallet-session-keys-app.mdx
new file mode 100644
index 000000000..5a29c5905
--- /dev/null
+++ b/fern/wallets/pages/recipes/wallet-session-keys-app.mdx
@@ -0,0 +1,37 @@
+---
+title: Session Keys App
+description:
+slug: wallets/recipes/wallet-session-keys-app
+---
+
+Below is a demo application that showcases Session Keys on Alchemy Smart Wallets.
+
+
+
+This demo walks you through the flow of creating and using session keys with smart wallets. You'll learn how to request a smart account, create session keys with different permissions, authorize sessions, and execute transactions without repeated wallet signatures.
+
+* [Launch the demo →](https://wallet-session-key-app.vercel.app/)
+
+## Key Features
+
+The demo includes step-by-step guidance through:
+
+* **Session Key Creation** — Generate temporary keys with scoped permissions
+* **Permission Types** — Choose from root permissions, native token transfers, or ERC20 token transfers
+* **Session Duration** — Set expiration (5 minutes, 1 hour, or 1 day)
+* **Authorization Flow** — See how session keys are authorized by the wallet owner
+* **Transaction Execution** — Execute multiple transactions using session keys without additional wallet approvals
+* **Real-time Feedback** — Watch transaction status updates on Sepolia
+
+This application uses our react hooks and built-in auth components to demonstrate the complete session key lifecycle.
+
+* **Hosted Demo**: https://wallet-session-key-app.vercel.app/
+* **Github**: https://github.com/alchemyplatform/wallet-session-key-app
+
+## Resources
+
+* [Launch the demo →](https://wallet-session-key-app.vercel.app/)
+* [wallet\_requestAccount](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account)
+* [wallet\_createSession](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-create-session)
+* [wallet\_prepareCalls](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+* [wallet\_sendPreparedCalls](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls)
diff --git a/fern/wallets/pages/resources/bbp.mdx b/fern/wallets/pages/resources/bbp.mdx
new file mode 100644
index 000000000..79585d72d
--- /dev/null
+++ b/fern/wallets/pages/resources/bbp.mdx
@@ -0,0 +1,11 @@
+---
+slug: wallets/resources/bbp
+---
+
+
+ We believe that strong security requires strong collaboration with skilled
+ security researchers to improve the resilience of our code and all of the
+ things it can do. If you believe you have found a security issue in our source
+ code or smart contracts, we encourage you to participate in the Alchemy [Bug
+ Bounty](https://hackerone.com/alchemyplatform) program and notify us.
+
diff --git a/fern/wallets/pages/resources/contact-us.mdx b/fern/wallets/pages/resources/contact-us.mdx
new file mode 100644
index 000000000..00270e86a
--- /dev/null
+++ b/fern/wallets/pages/resources/contact-us.mdx
@@ -0,0 +1,19 @@
+---
+title: Contact Alchemy
+description: Contact Alchemy about Smart Wallets
+slug: wallets/resources/contact-us
+---
+
+If there's anything we can do to improve your experience with Smart Wallets, please tell us!
+
+## Feedback
+
+1. If you have any questions on how to get started, start a [discussion](https://github.com/alchemyplatform/aa-sdk/discussions) in our github.
+
+2. If you are interested in an enterprise plan with Alchemy to scale your application, connect with us at [https://www.alchemy.com](https://www.alchemy.com/contact-sales/?a=ak-docs).
+
+## Contributing
+
+1. If you see a issue in our code or documentation, feel free to address it yourself! See this [README](https://github.com/alchemyplatform/aa-sdk/blob/main/CONTRIBUTING.md) on contributing to Smart Wallets's codebase.
+
+2. If you are interested in [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900) or modular account development, join the [outreach waitlist](https://docs.google.com/forms/d/1Z3wFRiMoEKoo8FJFrymVEOzrbKQXjSnYhm_hKKDnooE/edit) or our [Telegram group](https://t.me/+KfB9WuhKDgk5YzIx).
diff --git a/fern/wallets/pages/resources/faqs.mdx b/fern/wallets/pages/resources/faqs.mdx
new file mode 100644
index 000000000..697d5186c
--- /dev/null
+++ b/fern/wallets/pages/resources/faqs.mdx
@@ -0,0 +1,378 @@
+---
+title: Frequently Asked Questions
+description: Learn how to get started with Alchemy's Smart Wallets, a vertically
+slug: wallets/resources/faqs
+---
+
+
+ We recommend you check out our [github
+ discussions](https://github.com/alchemyplatform/aa-sdk/discussions) for more
+ commonly asked questions and support.
+
+
+## Smart Accounts - Light Account
+
+### Do accounts have the same address across all chains?
+
+
+ In almost all cases, yes, you will get the same address on all chains as long as the connecting signer address is the same! The deployment address is a function of the address of owner/signer address, the account implementation (e.g. latest version of Light Account), and the salt (you can optionally specify this). If all three of those remain the same, then you deploy the smart account at the same contract address.
+
+ There are two scenarios where you would get a different contract address:
+
+ 1. If you deploy one smart account, then change the signer, then deploy the second account.
+ 2. If you upgrade the smart account (e.g. to a new version of Light Account). It is unlikely that we will make many updates to this contract so the address will not change frequently.
+
+
+### How does a smart account get deployed?
+
+
+ Your smart account will be deployed when the first `UserOperation` (UO) is
+ sent from the account. The first UO must be sent with a non-zero `initCode`.
+ aa-sdk will handle generation of this `initCode` for you using
+ [`getAccountInitCode`](/wallets/reference/aa-sdk/core/functions/toSmartContractAccount).
+
+
+### How can I upgrade a Light Account?
+
+
+ It is unlikely you will need to frequently update the Light Account contract
+ itself, however it is possible if needed. Light Account has
+ [`UUPSUpgradeable`](https://github.com/alchemyplatform/light-account/blob/main/src/LightAccount.sol#L50)
+ which adds upgrade methods on the account itself. To upgrade an account you
+ will need to send a `UserOperation` using the method `upgradeTo` or
+ `upgradeToAndCall`, depending on whether or not you need to initialize the new
+ implementation.
+
+
+### Can I have multiple accounts for the same signer address? / How do I set the value of the salt for Light Account?
+
+
+ Yes! The optional salt value on Light Account enables the ability to have
+ multiple accounts under a single signer. This value defaults to 0. You can set
+ it when you create [light account](#TODO).
+
+
+### How can I upgrade from Simple Account to Light Account?
+
+
+ [Simple Account's](https://github.com/eth-infinitism/account-abstraction/blob/cc3893bcaf2272c163ce89d5eb9eadb8e6b52db7/contracts/accounts/SimpleAccount.sol#L22) support [`upgradeToAndCall`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/4e7e6e54daedf091d91f2f2df024cbb8f253e2ef/contracts/proxy/utils/UUPSUpgradeable.sol#L86) implemented by openzeppelin [UUPSUpgradeable](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) contract. This allows you to upgrade from Simple Account to Light Account without changing the smart contract account address. Using `upgradeToAndCall` will update the underlying implementation contract on the account while the account address and assets will stay the same.
+
+ You can call `upgradeToAndCall` on the Simple Account with these params:
+
+ * `newImplementation`: Latest LightAccount implementation address (found [here](https://github.com/alchemyplatform/light-account/tree/develop/deployments) - make sure to use the implementation address, not the factory address)
+ * For example LightAccount v1.1.0 - 0xae8c656ad28F2B59a196AB61815C16A0AE1c3cba
+ * `data`: encoded version of the `initialize` function with `anOwner` parameter set to the owner on the account, usually the same owner as what the account used as Simple Account.
+ * In solidity (foundry) you can use abi.encodeCall and in viem you can use [encodeFunctionData](https://github.com/alchemyplatform/light-account/tree/develop/deployments)
+
+ It is very important that the `initialize` step is encoded correctly to ensure the account does not get into a risky state where someone else could call initialize on it with one's signer and take control of your account. You can call `owner()` on the account after the upgrade to ensure it is assigned correctly.
+
+ This can be called on the existing smart contract account by sending a user operation that calls `execute` (or `executeBatch`) and has that call `upgradeToAndCall` (the same way you would make the account send calls to other addresses).
+
+
+## Submitting `UserOperation`s
+
+### How does the speed of `UserOperation`s compare to normal transactions?
+
+
+ If the `UserOperation` (meta-transaction for 4337 accounts) is correctly
+ priced and submitted a few hundred milliseconds prior to a new block getting
+ created, it will typically get placed in the next block. This is because the
+ Bundler needs time to create/propagate its transaction. You can think of it as
+ 1 extra block time worth of latency, but we are working towards improving this
+ latency.
+
+
+### Why am I seeing a delay in landing `UserOperation`s on-chain?
+
+
+ This can happen when `UserOperation`s (UOs) become underpriced, frequently due to fee market movement between when gas and fees are estimations and when the UO is actually submitted.
+
+ You may experience this when calling the [`waitForUserOperationTransaction`](/wallets/reference/aa-sdk/core/variables/waitForUserOperationTransaction) method. It may throw an error if it does not find the UO in a mined Transaction within its retry limits.
+
+ You can mitigate this by defining a more flexible retry period when constructing a [`Client`](/wallets/reference/aa-sdk/core/functions/createSmartAccountClient) (i.e. `txMaxRetries`, `txRetryIntervalMs`, `txRetryMultiplier` in `opts`). If your UO continues to be delayed beyond a limit you are willing to wait, you can resubmit it using [`dropAndReplaceUserOperation`](/wallets/reference/aa-sdk/core/functions/dropAndReplaceUserOperation).
+
+
+### Are `UserOperation`s protected from MEV bots?
+
+
+ Right now, `UserOperation`s are sent to a private mempool for all networks
+ other than Polygon, where there is no way to do this. We are actively involved
+ in proposals for a peer-to-peer mempool standard.
+
+
+### Can I simulate `UserOperation`s the same way I simulate transactions?
+
+
+ Yes! Check out [this guide](#TODO/react/simulate-user-operations).
+
+
+## Gas Estimation
+
+### How does gas estimation for 4337 smart contract accounts work?
+
+
+ Our bundler estimates gas and submits `UserOperation`s (UOs) under the hood of the aa-sdk. Our gas estimations are just that, estimations that optimize for UOs landing on chain, and you may need to adjust gas limits based on your needs using [overrides](https://github.com/alchemyplatform/aa-sdk/blob/v4.x.x/aa-sdk/core/src/types.ts#L97).
+
+ Learn more about gas estimation and how it is implemented in our [Bundler](https://www.alchemy.com/blog/erc-4337-gas-estimation).
+
+ There are many nuances and edge cases that our bundler considers especially for L2’s. Learn more [here](https://www.alchemy.com/blog/l2-gas-and-signature-aggregators).
+
+ We recommend adding error handling when sending a UO to handle potential gas and fee changes due to market movement. Learn more about [frequent errors](#common-errors).
+
+
+## Gas Manager
+
+### What tiers support gas sponsorship?
+
+
+ Gas sponsorship is available on testnet for all tiers. For support on mainnet,
+ you must be on a paying tier (i.e. Growth tier and above). Learn more about
+ our different pricing tiers
+ [here](https://alchemy.com/docs/reference/gas-manager-coverage-api-pricing#fee-logic).
+
+
+### How is gas sponsored? Do I need to fund the Gas Manager?
+
+
+ We front the gas for your application and put the USD equivalent on your bill
+ at the end of the month. No need to worry about pre-funding the Gas Manager or
+ conversions, we’ve got you covered! You can follow [this guide](#TODO) for
+ more details on how to sponsor `UserOperation`s.
+
+
+### What are my gas sponsorship limits?
+
+
+ You can find details of Gas Manager limits depending on your tier
+ [here](https://alchemy.com/docs/reference/gas-manager-coverage-api-pricing#fee-logic).
+
+
+### Do you support ERC-20 or stablecoin paymasters?
+
+
+ Yes. Gas Manager includes an ERC-20 paymaster so your users can pay fees with
+ tokens like USDC. See [How to pay gas with any
+ token](/reference/how-to-pay-gas-with-any-token) for details.
+
+
+### How is the Gas Manager protected from DDOS attacks?
+
+
+ In your Gas Manager policy, you can configure spending rules per address, per
+ app, and/or policy wide limits. See how to set up these policies
+ [here](#TODO).
+
+
+### How to Authenticate Users and Verify User Sessions on the Backend?
+
+
+ After a user logs in with Smart Wallets on the frontend, you might want to verify their identity on your backend to authorize actions or access.
+
+ You can do this using one of two approaches:
+
+ ***
+
+ ### Option 1: SIWE [Sign-In With Ethereum](https://eips.ethereum.org/EIPS/eip-4361)
+
+ Use this flow when you just need to verify user sessions created by EOAs and Smart Contract Accounts (SCAs) via Smart Wallets.
+
+ For EOAs, Smart Wallets doesn’t create a session on the client side and `stampWhoAmI` isn’t available, so we can use SIWE. This can be used for verifying EOA and SCA flows.
+ You can create a [SIWE message](https://docs.login.xyz/sign-in-with-ethereum/quickstart-guide/creating-siwe-messages) with all the necessary info to get server side (address, chainId, etc).
+
+ 1. The backend provides a nonce to the frontend.
+ 2. The user signs a SIWE message with their wallet (EOA or SCA).
+ 3. The frontend sends the signed message back to the backend.
+ 4. The backend verifies the signature:
+
+ * EOAs: Standard signature recovery
+ * SCAs: Use EIP-1271 or EIP-6492 to verify the contract signature
+
+ 5. The backend issues a session token.
+
+ **Note:**
+
+ * SIWE requires an explicit signature from the user, which [costs](https://www.alchemy.com/docs/reference/compute-unit-costs) more than calling `whoami`
+ * For verifying both EOA and SCA user sessions
+
+ ***
+
+ ### Option 2: `stampWhoAmI` + `whoami` (Only when using Alchemy signer)
+
+ Use this flow when you just need to verify user sessions created by SCAs via Smart Wallets.
+
+ 1. The frontend generates a stamped request using [signer.inner.stampWhoAmI](https://www.alchemy.com/docs/wallets/reference/account-kit/signer/classes/BaseSignerClient#stampwhoami).
+ 2. It sends the stamp to your backend.
+ 3. The backend calls Alchemy's [/signer/v1/whoami](https://www.alchemy.com/docs/node/smart-wallets/signer-api-endpoints/signer-api-endpoints/auth-user) endpoint to verify the identity.
+ 4. If you need to make subsequent requests, you can also avoid calling the whoami endpoint on every request. To do so, after verifying the `whoami`, the backend can issue its own session token (e.g. an HTTP-only cookie or access token). If the token is present, you can safely skip the `whoami` check.
+
+ **Why this approach?**
+
+ * No user signature required
+ * **Cheaper** than flows requiring a signed message
+ * Easily retrieve user's login info, such email and address if available
+
+
+## Common Errors
+
+### Replacement underpriced: `"code":-32602,"message":"replacement underpriced","data"...`
+
+
+ Replacement underpriced errors occur when you attempt to send another user
+ operation (UO) from the same sender before the pending user operation has been
+ confirmed on-chain. Please follow recommendations
+ [here](https://www.alchemy.com/support/how-to-fix-replacement-underpriced-errors).
+
+
+### `FailedToFindTransactionError: Failed to find transaction for user operation...`
+
+
+ Please follow [this](https://www.alchemy.com/support/best-practice-guide-on-how-to-implement-user-operation-retries) guide for best practices on implementing waiting and retries.
+
+ This error indicates that your User Operation has not yet landed on chain (assuming it was accepted by the bundler). We recommend you 1) continue waiting or 2) drop and replace and 3) add multiplier to gas esitmates to increase the priority of your transaction.
+
+
+### Invalid policy ID: `{ code: -32602, message: 'Invalid Policy ID' }`
+
+
+ Gas Manager policies can only be tied to one app. Make sure you are using the
+ API Key that is associated with the app the Gas Manager policy is configured
+ for, or create a new policy for the app you are using.
+
+
+### Precheck failed: `{ code: -3200, message: 'precheck failed: ...' }`
+
+
+ Precheck failed errors are often related to gas and/or fees. Our Bundler follows standard [ERC 4337](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4337.md#client-behavior-upon-receiving-a-useroperation) implementation for gas and fee checks in order to 1) ensure your `UserOperation`s (UOs) land on chain and to 2) protect the Bundler from potential attacks in order to support scalability.
+
+ These errors are often related to market movement between the time when gas and fees are estimated and the time when UOs are submitted to the bundler. This fluctuation in the market is especially variant on testnet. To ensure your UO is included in a block, we currently reject sending any UOs that are underpriced compared to the network rate .
+
+ To handle these errors, we recommend you use our [override fields](https://github.com/alchemyplatform/aa-sdk/blob/v4.x.x/aa-sdk/core/src/types.ts#L97) to increase buffers on top of our estimates and implement retry mechanisms as needed.
+
+ Our gas and fee estimations are just that, estimations, but we are always working to improve these estimates!
+
+
+### Total execution gas limit exceeded: `{ code: -3202, message: 'precheck failed: total execution gas is X but must be at most 10000000}`
+
+
+ Currently our Bundler allows max 10M gas in aggregate between
+ `preVerificationGas`, `verificationGasLimit`, and `callGasLimit`. To reduce
+ the gas needed, try reducing the size of your call data and/or sending your
+ call data in multiple `UserOperation`s rather than one.
+
+
+### `waitForUserOperationTransaction` timeout
+
+
+ [`waitForUserOperationTransaction`](/wallets/reference/aa-sdk/core/variables/waitForUserOperationTransaction) may throw this error if it does not find the mined User Operation within its retry limits.
+
+ You can mitigate this by defining a more flexible retry period when constructing a [`Client`](/wallets/reference/aa-sdk/core/functions/createSmartAccountClient) (i.e. `txMaxRetries`, `txRetryIntervalMs`, `txRetryMultiplier` in `opts`).
+
+ If your `UserOperation` continues to be delayed beyond a limit you are willing to wait, you can resubmit the user operation using [`dropAndReplaceUserOperation`](/wallets/reference/aa-sdk/core/functions/dropAndReplaceUserOperation#usage).
+
+
+### `Although one or more Error Occurred [execution reverted] Contract Execution Completed` on etherscan
+
+
+ This revert warning message is expected when using Modular Account v1. In
+ MAv1, we have a checker that enforces that calls made using the account (from
+ `execute` or `executeBatch`) don't target plugin contracts. When the contract
+ being called to isn't a plugin (which is usually the case), the checker
+ reverts, and it shows this message in explorers.
+
+
+### `ERR_BLOCKED_BY_CLIENT` when loading the page
+
+
+ This message is usually triggered by browser ad‑blocking tools that stop our
+ analytics scripts from running. Analytics collection will be blocked, but the
+ Smart Wallet functionality will continue to work normally.
+
+
+## Other Support
+
+### Does the aa-sdk repo support React Native?
+
+No, the `aa-sdk` repo does not offically support React Native. **It is on our radar!**
+
+Currently we have a strong dependency on Viem, which requires several global features, such as `TextEncoder` and `crypto`, that are absent in React Native's environment. See more about [Viem's capability here](https://viem.sh/docs/compatibility).
+
+However, we have created a small PoC using Expo that you can find [here](https://github.com/alchemyplatform/aa-sdk-rn-expo/tree/main). For more information on how to use Smart Wallets within a React Native application see [the guide](/wallets/react-native/overview).
+
+### How should I protect my api key and policy id in the frontend?
+
+**Why is it important to protect my API key and Policy ID?**
+
+To prevent unauthorized use or abuse of your API key and Policy ID, it’s critical to avoid exposing them on the frontend. If these credentials are exposed, they could be misused, leading to unintended usage or sponsorship.
+
+Gas sponsorship requests will only succeed if both the Policy ID and the corresponding API key are used together. While protecting your API key alone renders the Policy ID useless to malicious actors, the best security practice is to protect both.
+
+**How can I protect my API Key?**
+We recommend moving your API key to the backend by routing to a proxy. You can see an [example](https://github.com/alchemyplatform/aa-sdk/blob/ef7303333830df53a9106ba37ce015675a276cd9/examples/ui-demo/src/app/config.tsx#L74) of setting up a transport to a backend rpcUrl in our demo app.
+
+For more information on how to secure your API key, refer to [this guide](https://alchemy.com/docs/best-practices-for-key-security-and-management).
+
+
+ If you have logic in your proxy that restricts certain paths, ensure that the
+ relative paths for network-agnostic RPC requests and signer API requests ([see
+ example
+ here](https://github.com/alchemyplatform/aa-sdk/blob/14187f8b87224d8730da2919575ac753626461eb/examples/ui-demo/src/app/api/rpc/%5B...routes%5D/route.ts#L21)),
+ and network-specific RPC requests ([see example
+ here](https://github.com/alchemyplatform/aa-sdk/blob/14187f8b87224d8730da2919575ac753626461eb/examples/ui-demo/src/app/api/rpc/route.ts))
+ are preserved when forwarding.
+
+
+**How can I protect my Policy ID?**
+
+Protecting your Policy ID requires some custom work, but it’s similar to safeguarding any key on the backend. One solution is to use a proxy server that holds both the API key and Policy ID. In the frontend, when creating an Alchemy client, pass the proxy server URL as the RPC URL instead of a public Alchemy URL.
+
+Additionally, you'll need to implement custom code on your proxy server to limit gas sponsorship requests. This could include rules that make sense for your app, such as limiting gas fees, restricting certain contract or method calls, or implementing limits based on IP addresses or CAPTCHA verification.
+
+### Why are my users getting logged out unexpectedly?
+
+**Issue:**\
+You or your users might notice that the session ends sooner than expected, requiring re-authentication.
+
+**Default behavior:**\
+By default, authenticated sessions using `AlchemyWebSigner` are stored in `localStorage` and expire after **15 minutes** of inactivity.
+
+***
+
+#### ✅ Solution: Extend or configure session duration
+
+You can customize the session timeout by passing a `sessionConfig` object to the `createConfig` method:
+
+```ts
+createConfig({
+ ...,
+ sessionConfig: {
+ expirationTimeMs: 1000 * 60 * 60, // 1 hour session duration (defaults to 15 min)
+ },
+})
+```
+
+👉 [Reference: createConfig](https://www.alchemy.com/docs/wallets/reference/account-kit/react/functions/createConfig)
+
+***
+
+#### 🧪 Other common causes of session ending:
+
+* **Server-Side Rendering (SSR) misconfigured**
+ If you're using SSR and haven't configured the provider correctly, session state can reset on the client.
+ 👉 [Enable SSR and pass initial params →](https://www.alchemy.com/docs/wallets/troubleshooting/ssr)
+
+* **Ad blockers**
+ Some ad blockers (especially those with aggressive cookie or storage policies) can interfere with localStorage or SDK requests, causing unexpected logouts.
+
+* **Private/incognito mode**
+ Browsers often restrict or wipe localStorage between tabs or after a short time in incognito mode. Consider warning users or designing for short session lengths in those environments.
+
+***
+
+#### 🔁 Debugging Checklist
+
+If you're seeing repeated or sporadic logouts in production, test the behavior across:
+
+* Browsers (Chrome, Firefox, Safari)
+* Devices (desktop vs mobile)
+* Browsing modes (incognito vs standard)
+* With and without ad blockers
diff --git a/fern/wallets/pages/resources/terms.mdx b/fern/wallets/pages/resources/terms.mdx
new file mode 100644
index 000000000..690582569
--- /dev/null
+++ b/fern/wallets/pages/resources/terms.mdx
@@ -0,0 +1,96 @@
+---
+title: Terms
+description: Glossary of terms related to Smart Wallets
+slug: wallets/resources/terms
+---
+
+## Smart Wallets
+
+The Smart Wallets framework is designed to embed smart accounts in web3 applications. It includes a set of tools such as [Signer integrations](/wallets/signer/what-is-a-signer), [Gas Manager](https://alchemy.com/docs/gas-manager-services) and [Bundler](https://alchemy.com/docs/bundler-services) utilities that unlock features such as [gas sponsorship](/wallets/transactions/sponsor-gas), [batched transactions](/wallets/transactions/send-batch-transactions) and email/social login. With its user-friendly suite of SDKs, known as [aa-sdk](https://github.com/alchemyplatform/aa-sdk), Smart Wallets makes it easy to deploy smart accounts, manage `UserOperation`s, and handle gas sponsorship, streamlining the entire process with minimal coding effort.
+
+## Bundler
+
+A network participant in the [ERC-4337](#erc-4337) standard that collects and submits `UserOperations` (UOs) to the blockchain, handling the associated gas fees, in exchange for payment during UO processing either directly from the user or from a [Paymaster](https://www.alchemy.com/overviews/what-is-a-paymaster). Alchemy’s implementation of a bundler is called [Rundler](https://github.com/alchemyplatform/rundler). It is written in Rust and designed to achieve high performance and reliability.
+
+## Client
+
+Built on top of `viem`, we have built our own [`Client`](https://viem.sh/docs/clients/custom) extended with custom functionality as a [`BundlerClient`](/wallets/resources/types#BundlerClient) and [`SmartAccountClient`](/wallets/resources/types#SmartAccountClient) compliant to EIP-4337 and EIP-6900 standards. `Client`, in general, is an intermediary or connector that enables interactions between client applications and your `SmartAccount` (either `LightAccount` or `ModularAccount`) or the `Bundler`.
+
+## EntryPoint
+
+A standardized smart contract that acts as the primary gateway for processing `UserOperations` (UOs) on the blockchain. It receives bundled UOs from [`Bundlers`](#bundler) and verifies and executes these operations according to predefined rules, ensuring security and adherence to user-specified conditions. `EntryPoint` contract is a singleton contract to execute bundles of `UserOperation`s. `Bundler`s whitelist the supported `EntryPoint`.
+
+## ERC-4337
+
+A standard authored by the [Ethereum Foundation](https://ethereum.foundation/) for account abstraction, establishing a uniform interface for all smart accounts. This standard also outlines the roles and functionalities of [Bundlers](https://alchemy.com/docs/bundler-services), [Paymasters](https://www.alchemy.com/overviews/what-is-a-paymaster), and [`EntryPoint`](#entrypoint). Reference: https://eips.ethereum.org/EIPS/eip-4337
+
+## ERC-6492
+
+A standard designed for verifying signatures from smart accounts that haven't been deployed yet. It is important for account abstraction, allowing decentralized applications (dApps) to authenticate user signatures even before the user's smart account is deployed. The deployment of these accounts typically occurs during the user's first transaction, making [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492) essential for early interaction verification between users and dApps.
+
+## ERC-6900
+
+A [standard for modular accounts](https://eips.ethereum.org/EIPS/eip-6900) authored by Alchemy and [Yoav](https://github.com/yoavw) (one of the authors of ERC-4337) from the Ethereum Foundation. It defines standard interfaces for Modular Accounts ([`Modular Accounts`](#modular-account)) capable of supporting all standard-conformant [`Plugins`](#plugin).
+
+## Gas Manager
+
+[`Gas Manager`](https://alchemy.com/docs/gas-manager-services) is the Alchemy’s [Paymaster](#paymaster) service. With robust security and customizability, Alchemy Gas Manager allows you to easily and securely sponsor the gas fees for users of your applications. Gas Managers authenticate transactions and ensure payments are carried out only when specific conditions are fulfilled, significantly reducing the chances of fraud and errors. Additionally, Gas Managers allow you to set granular rules for sponsoring your user's gas fees, so you can determine precisely when and how the gas fees will be sponsored. Head over to [Gas Manager](https://alchemy.com/docs/gas-manager-services) documentation to learn more.
+
+## Light Account
+
+[Light Account](/wallets/smart-contracts/other-accounts/light-account/) is a collection of lightweight, production-ready [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) smart accounts developed by Alchemy. It builds on top of Ethereum Foundation's canonical [SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/cc3893bcaf2272c163ce89d5eb9eadb8e6b52db7/contracts/accounts/SimpleAccount.sol#L22) to add key improvements such as ownership transfers, multiple owners, ERC-1271 signature support, and gas optimizations. It has been audited [multiple](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-01-09_quantstamp_aa8196b.pdf) [times](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-04-26_quantstamp_93f46a2.pdf) by Quantstamp.
+
+## Magic Link Authentication
+
+A magic link is a one-time use link sent to a user during the authentication process. After entering their username, the user is sent a URL, either to the user's email address or their mobile phone via text. The user clicks to authenticate themselves without entering a password, and for some, this might seem like "magic," thus the name. Magic links are attractive because it is a simple way to remove the need for a customer to generate and remember a password from the process.
+
+## Modular Account
+
+A type of smart account enabled by the [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900) standard and characterized by its [modular structure](/wallets/smart-contracts/modular-account-v2/overview). This structure segments different functionalities of the account into distinct, independently upgradeable modules or plugins. Each plugin can have specific functions such as validation, execution, or hooks, enabling the smart account to extend its capabilities or modify its behavior without altering the core account logic. Modular Accounts enhance the flexibility, upgradeability, and interoperability of [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) smart accounts. Modular Account contracts have been audited by both [Spearbit](https://github.com/alchemyplatform/modular-account/blob/develop/audits/2024-01-31_spearbit_0e3fd1e.pdf) and [Quantstamp](https://github.com/alchemyplatform/modular-account/blob/develop/audits/2024-02-19_quantstamp_0e3fd1e.pdf).
+
+
+
+## Passkey
+
+Passkeys are a safer and easier alternative to passwords. With passkeys, users can sign in to apps and websites with a biometric sensor (such as a fingerprint or facial recognition), PIN, or pattern, freeing them from having to remember and manage passwords.
+
+## Paymaster
+
+A [Paymaster](https://eips.ethereum.org/EIPS/eip-4337#paymasters) is an on-chain contract that allows an entity to sponsor the gas fees for another entity. It can be used by dapps or companies to abstract away the concept of gas from their users. This significantly enhances the UX of dApps and can help onboard the next wave of Web3 users.
+
+## Plugin
+
+A module for [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900#terms) smart contract accounts, plugins are deployed smart contracts that host any amount of the EIP-6900 modular functions: execution functions, validation functions, or hooks. These plugins ensure modularity, upgradeability, and adherence to standardized interfaces.
+
+## Signer
+
+A service or application that manages the private key and signs either arbitrary messages or structured data objects such as `UserOperations` or `Transactions` before sending to the network. Types of signers include:
+
+* **Custodial**: Managed by a third party, it holds and autonomously uses the private key for transactions, necessitating complete user trust.
+* **Non-custodial**: While a third party manages the private key, user involvement is required for signing transactions. Examples: Metamask.
+* **MPC (Multi-Party Computation)**: Partial or complete key shares are managed by third parties, but user participation is needed for transaction signatures. Examples: Privy, Portal, Fireblocks, Fordefi.
+* **Decentralized MPC**: Operated by a decentralized network, it manages key shares and requires node consensus for transaction signatures. Examples: Lit, Web3auth, 0xpass.
+
+## Smart Contract Account
+
+A [Smart Contract Account (SCA)](/wallets/resources/types#smartcontractaccount), or smart account in short, is an individual on-chain account located at a public address where an ERC-4337 compatible smart account [`contract`](https://ethereum.org/developers/docs/smart-contracts) is deployed to. This address is controlled by one or more owners of the smart contract account. The [aa-sdk](https://github.com/alchemyplatform/aa-sdk) supports different smart account implementations such as [Modular Account V2](/wallets/smart-contracts/modular-account-v2/overview), [Light Account](/wallets/smart-contracts/other-accounts/light-account/), or [Simple Account](https://github.com/eth-infinitism/account-abstraction/blob/cc3893bcaf2272c163ce89d5eb9eadb8e6b52db7/contracts/accounts/SimpleAccount.sol#L22). You can also [add your own account implementation in aa-sdk](#TODO/smart-contracts/custom/contributing).
+
+## Transaction
+
+`Transactions` in blockchain are cryptographically signed data messages that contain a set of instructions. These instructions can be interpreted to send native tokens from one account to another or interact with a smart contract deployed on the blockchain. A `Transaction` usually consists of the following parameters: `nonce`, `gasPrice`, `gasLimit`, `to`, `value`, `data`, `v`, `r`, `s`. Ethereum and other EVM blockchains have evolved to allow other transaction standards such as [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559), to allow more predictable gas fees and a more efficient transaction market.
+
+## Transaction Calldata
+
+Transaction `calldata` refers to the data passed along with a transaction that allows accounts to send messages to other entities or interact with smart contracts. When calling smart contracts on-chain (either from an `EOA` or another contract), the `calldata` is the encoded data within the `Transaction` containing the input parameters (or arguments) of the function being called on the contract.
+
+## User Operation
+
+User Operations are pseudo-transaction objects introduced by the ERC-4337 standard that are issued to execute actions via a smart account. It encapsulates the intended actions or transactions of the user, which are executed on-chain by a [Bundler](#bundler) through an [`EntryPoint`](https://eips.ethereum.org/EIPS/eip-4337#definitions) contract on different EVM chains.
+
+## Wallet
+
+[Wallets](https://ethereum.org/wallets) are applications that give you control over your account. Like your physical wallet, it contains everything you need to prove your identity and handle your assets. Your wallet allows you to sign in to applications, read your balance, send transactions, and verify your identity. Your wallet is a tool for interacting with your account, which can be either an [Externally-Owned Account (EOA)](https://ethereum.org/developers/docs/accounts) or [`SmartContractAccount`](/wallets/resources/types#smartcontractaccount). Conventionally, wallets used to interact with `SmartContractAccounts` are called `Smart Contract Wallets`, while wallets used to interact with Externally-owned accounts (EOA) are called `EOA Wallets`.
+
+## Wallet-as-a-Service (WaaS)
+
+Also called a key management service (KMS). WaaS is an infrastructure-as-a-service provider that stores private key material. A WaaS provider would be classified under one of the [signer custody types](#signer).
diff --git a/fern/wallets/pages/resources/types.mdx b/fern/wallets/pages/resources/types.mdx
new file mode 100644
index 000000000..b273ec438
--- /dev/null
+++ b/fern/wallets/pages/resources/types.mdx
@@ -0,0 +1,714 @@
+---
+title: Types
+description: Glossary of types in aa-sdk
+slug: wallets/resources/types
+---
+
+## `BatchUserOperationCallData`
+
+An array of `UserOperationCallData`, representing a sequence of `UserOperations` to be executed in batch by calling the `executeBatch` function on the `SmartContractAccount` contract. Check out our guide on [How to submit batch transactions](/wallets/transactions/send-batch-transactions) to learn more about batching multiple transactions into a single `UserOperation`.
+
+
+
+```ts
+export type BatchUserOperationCallData = Exclude[];
+```
+
+
+
+## `BigNumberish`
+
+A type that can be a hexadecimal string prefixed with [`Hex`](https://viem.sh/docs/glossary/types#hex), a `bigint`, or a `number`. It is used to represent values that can be converted to or operate as big integers.
+
+
+
+```ts
+export const BigNumberishSchema = z.union([HexSchema, z.number(), z.bigint()]);
+```
+
+
+
+## `BigNumberishRange`
+
+An object type that may contain optional `min` and `max` fields, each of which accepts a `BigNumberish` value. This type is used to specify a numerical range, including both the minimum and maximum bounds.
+
+
+
+```ts
+export const BigNumberishRangeSchema = z
+ .object({
+ min: BigNumberishSchema.optional(),
+ max: BigNumberishSchema.optional(),
+ })
+ .strict();
+```
+
+
+
+## `BundlerAction`
+
+Bundler Actions are `viem` [`Actions`](https://viem.sh/docs/actions/public/introduction) that map one-to-one with "public" [`Bundler`](/wallets/resources/terms#bundler) RPC methods (`eth_sendUserOperation`, `eth_getUserOperationByHash`, etc.) under the [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337) and [EIP-6900](https://eips.ethereum.org/EIPS/eip-6900) standards. They are used with a [`BundlerClient`](#bundlerclient). `BundlerActions` do not require any special permissions, nor do they provide "signing" capabilities to the user. Examples of `BundlerActions` include retrieving the details of a specific user operation, estimating user operation gas, etc.
+
+
+
+```ts
+export type BundlerActions = {
+ /**
+ * calls `eth_estimateUserOperationGas` and returns the result
+ *
+ * @param request - the UserOperationRequest to estimate gas for
+ * @param entryPoint - the entry point address the op will be sent to
+ * @param stateOverride - the state override to use for the estimation
+ * @returns the gas estimates for the given response
+ */
+ estimateUserOperationGas<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ >(
+ request: UserOperationRequest,
+ entryPoint: Address,
+ stateOverride?: StateOverride,
+ ): Promise>;
+
+ /**
+ * calls `eth_sendUserOperation` and returns the hash of the sent UserOperation
+ *
+ * @param request - the UserOperationRequest to send
+ * @param entryPoint - the entry point address the op will be sent to
+ * @returns the hash of the sent UserOperation
+ */
+ sendRawUserOperation<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ >(
+ request: UserOperationRequest,
+ entryPoint: Address,
+ ): Promise;
+
+ /**
+ * calls `eth_getUserOperationByHash` and returns the UserOperationResponse
+ *
+ * @param hash - the hash of the UserOperation to fetch
+ * @returns - the user operation if found or null
+ */
+ getUserOperationByHash(hash: Hash): Promise;
+
+ /**
+ * calls `eth_getUserOperationReceipt` and returns the UserOperationReceipt
+ *
+ * @param hash - the hash of the UserOperation to get the receipt for
+ * @param tag - if client want to get receipt for different block tag.
+ * @returns - a user operation receipt or null if not found
+ */
+ getUserOperationReceipt(
+ hash: Hash,
+ tag?: "pending" | "latest",
+ ): Promise;
+
+ /**
+ * calls `eth_supportedEntryPoints` and returns the entry points the RPC supports
+ *
+ * @returns - an array of the entrypoint addresses supported
+ */
+ getSupportedEntryPoints(): Promise;
+};
+```
+
+
+
+## `BundlerClient`
+
+`BundlerClient` is a custom `viem` [`Client`](https://viem.sh/docs/clients/custom) that extends viem's [`PublicClient`](https://viem.sh/docs/clients/public) with bundler-specific actions for [EIP-4337](https://eips.ethereum.org/EIPS/eip-4337) operations and [EIP-6900](https://eips.ethereum.org/EIPS/eip-6900) standards. The actions are defined in [`@aa-sdk/core`](/wallets/reference/aa-sdk/core). It's account agnostic and only exposes methods for interacting directly with Bundler RPC and ETH RPC methods.
+`BundlerClient` also supports [`Public Actions`](https://viem.sh/docs/actions/public/introduction) for client applications to connect, query, and interact with the blockchain (i.e., sending transactions, smart contract executions, data retrieval, etc.). Additionally, it is EIP-1193 compliant, so can easily be swapped out in place of other web3 providers (eg. `window.ethereum`).
+In the vast majority of cases, you will not use this client directly. Use the [`SmartAccountClient`](/wallets/concepts/smart-account-client) instead, which wraps the Bundler Client and provides the same actions plus account-specific functionality.
+
+
+
+````ts
+export type BundlerClient = Client<
+ T,
+ Chain,
+ undefined,
+ [...PublicRpcSchema, ...BundlerRpcSchema],
+ PublicActions & BundlerActions
+>;
+
+/**
+ * Creates a bundler client from an existing public client with the provided transport and chain.
+ *
+ * @example
+ * ```ts
+ * import { createPublicClient } from "viem";
+ * import { createBundlerClientFromExisting } from "@aa-sdk/core";
+ *
+ * const publicClient = createPublicClient(...);
+ * const bundlerClient = createBundlerClientFromExisting(publicClient);
+ * ```
+ *
+ * @param {PublicClient} client The existing public client to be extended with bundler actions
+ * @returns {BundlerClient} A bundler client that extends the functionality of the provided public client
+ */
+export const createBundlerClientFromExisting: <
+ T extends Transport | FallbackTransport = Transport,
+>(
+ client: PublicClient,
+) => BundlerClient = (
+ client: PublicClient,
+): BundlerClient => {
+ return client.extend(bundlerActions);
+};
+````
+
+
+
+## `ClientMiddleware`
+
+Middleware represents different operations involved in the [`SmartAccountClient`](/wallets/concepts/smart-account-client) pipeline for constructing a user operation given the user inputs by populating the UO with other data, including gas fees, paymaster data, etc.
+
+
+
+```ts
+export type ClientMiddleware<
+ TContext extends UserOperationContext | undefined =
+ | UserOperationContext
+ | undefined,
+> = {
+ dummyPaymasterAndData: ClientMiddlewareFn;
+ feeEstimator: ClientMiddlewareFn;
+ gasEstimator: ClientMiddlewareFn;
+ customMiddleware: ClientMiddlewareFn;
+ paymasterAndData: ClientMiddlewareFn;
+ userOperationSimulator: ClientMiddlewareFn;
+ signUserOperation: ClientMiddlewareFn;
+};
+```
+
+
+
+## `ClientMiddlewareConfig`
+
+Configuration object to configure `ClientMiddleware` of the [`SmartAccountClient`](/wallets/concepts/smart-account-client) during the client instantiation. You can configure using this object to configure the middleware of your interest selectively.
+
+
+
+```ts
+export type ClientMiddlewareConfig<
+ TContext extends UserOperationContext | undefined =
+ | UserOperationContext
+ | undefined,
+> = Partial>;
+```
+
+
+
+## `ClientMiddlewareFn`
+
+Each middleware is a function that takes in a user operation object, `UserOperationStruct`, performs its job to retrieve or compute the data, and populate different fields of the user operation to pass onto the next middleware in the pipeline before being signed and sent to the network. `ClientMiddlewareFn` is the function type that represents each middleware. In optional [`UserOperationOverrides`](#useroperationoverrides), and [`UserOperationFeeOptions`](https://github.com/alchemyplatform/aa-sdk/blob/v4.x.x/aa-sdk/core/src/types.ts#L55), and returns a promise that resolves to a modified `UserOperationStruct`. This function is what you specify as your overridden middleware value for applying custom logic during the `UserOperationStruct` object to be sent to the bundler for on-chain execution.
+
+
+
+```ts
+export type ClientMiddlewareFn<
+ TContext extends UserOperationContext | undefined =
+ | UserOperationContext
+ | undefined,
+> = <
+ TAccount extends SmartContractAccount,
+ C extends MiddlewareClient,
+ TEntryPointVersion extends
+ GetEntryPointFromAccount = GetEntryPointFromAccount,
+>(
+ struct: Deferrable>,
+ args: ClientMiddlewareArgs,
+) => Promise>>;
+```
+
+
+
+## `EntryPointDef`
+
+An object type that defines the interface for `EntryPoint` functions for packing the user operation to the optimized data structure to enhance performance and reduce gas costs of transactions, and generating the hash of the user operation for the format compatible to the specified `Chain` and `EntryPointVersion`.
+
+
+
+```ts
+export type EntryPointDef<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ TChain extends Chain = Chain,
+ TAbi extends Abi | readonly unknown[] = Abi,
+> = {
+ version: TEntryPointVersion;
+ address: Address;
+ chain: TChain;
+ abi: GetContractParameters["abi"];
+ getUserOperationHash: (
+ request: UserOperationRequest,
+ ) => Hex;
+ packUserOperation: (
+ userOperation: UserOperationRequest,
+ ) => Hex;
+};
+```
+
+
+
+## `Multiplier`
+
+An object type with a required `multipler` field, which is a `number` value with max precision of 4 decimal places.
+
+
+
+```ts
+export const MultiplierSchema = z
+ .object({
+ /**
+ * Multiplier value with max precision of 4 decimal places
+ */
+ multiplier: z.number().refine(
+ (n) => {
+ return (n.toString().split(".")[1]?.length ?? 0) <= 4;
+ },
+ { message: "Max precision is 4 decimal places" },
+ ),
+ })
+ .strict();
+```
+
+
+
+## `SmartAccountAuthenticator`
+
+An extension of the [`SmartAccountSigner`](#smartaccountsigner) interface, this interface contains authentication-related functions in addition to the signing methods of the `SmartAccountSigner`. It provides methods to authenticate the signer (`authenticate`) as the "authorized" signer, often as the owner, of the `SmartContractAccount`. It also has methods to retrieve authentication details (`getAuthDetails`) about the signer instance that the user is using to authenticate to one's account.
+
+
+
+```ts
+/**
+ * Extends the @interface SmartAccountSigner interface with authentication.
+ *
+ * @template AuthParams - the generic type of the authentication parameters
+ * @template AuthDetails - the generic type of the authentication details
+ * @template Inner - the generic type of the inner client that the signer wraps to provide functionality such as signing, etc.
+ */
+export interface SmartAccountAuthenticator
+ extends SmartAccountSigner {
+ authenticate: (params: AuthParams) => Promise;
+
+ getAuthDetails: () => Promise;
+}
+```
+
+
+
+## `SmartAccountClient`
+
+`SmartAccountClient` is a custom `viem` `Client`, like the [`BundlerClient`](#bundlerclient), which is an intermediary or connector that enables your client application to interact with the `SmartContractAccount`. `SmartAccountClient` is analogous to the [`WalletClient`](https://viem.sh/docs/clients/wallet). The difference is that while `WalletClient` has [`WalletActions`](https://viem.sh/docs/actions/wallet/introduction) that lets your client application interact with an [Externally-owned account (EOA)](https://ethereum.org/developers/docs/accounts) with a [wallet](/wallets/resources/terms#wallet), `SmartAccountClient` provides [`SmartAccountClientActions`](#smartaccountclientaction) for client applications to interact with `SmartContractAccounts`.
+
+
+
+```ts
+export type SmartAccountClient<
+ transport extends Transport = Transport,
+ chain extends Chain | undefined = Chain | undefined,
+ account extends SmartContractAccount | undefined =
+ | SmartContractAccount
+ | undefined,
+ actions extends Record = Record,
+ rpcSchema extends RpcSchema = SmartAccountClientRpcSchema,
+ context extends UserOperationContext | undefined =
+ | UserOperationContext
+ | undefined,
+> = Prettify<
+ Client<
+ transport,
+ chain,
+ account,
+ rpcSchema,
+ actions & SmartAccountClientActions
+ >
+>;
+```
+
+
+
+## `SmartAccountClientAction`
+
+`SmartAccountClientActions` are `viem` [`Actions`](https://viem.sh/docs/actions/wallet/introduction) that map one-to-one with smart contract account-related or "signable" actions, such as constructing user operation requests to be sent to the [`Bundler`](/wallets/resources/terms/#bundler), signing messages or user operation requests, sending user operations to the `Bundler`, upgrading accounts to different implementation address, etc. They are used with a `SmartAccountClient`. `SmartAccountClientActions` require special permissions and provide signing capabilities for `SmartContractAccounts`.
+
+
+
+```ts
+export type SmartAccountClientActions<
+ chain extends Chain | undefined = Chain | undefined,
+ account extends SmartContractAccount | undefined =
+ | SmartContractAccount
+ | undefined,
+ context extends UserOperationContext | undefined =
+ | UserOperationContext
+ | undefined,
+> = BaseSmartAccountClientActions &
+ BundlerActions &
+ PublicActions;
+```
+
+
+
+## `SmartAccountSigner`
+
+An interface representing a signer capable of signing messages and typed data. It provides methods to retrieve the signer's address (`getAddress`), sign a message (`signMessage`), and sign typed data (`signTypedData`). `SmartAccountSigner` refers to the [`Signer`](/wallets/resources/terms#signer) instance responsible for the signing activities using its private key for smart account activities. Often, the `Signer` is referred to as the `Owner` of the account as it has the authority to use the smart account on-chain with its signatures.
+
+
+
+```ts
+// TODO: This is a temporary type to be removed when viem is updated
+export type AuthorizationRequest = OneOf<
+ | {
+ address: Address;
+ }
+ | {
+ contractAddress: Address;
+ }
+> & {
+ /** Chain ID. */
+ chainId: uint32;
+ /** Nonce of the EOA to delegate to. */
+ nonce: uint32;
+};
+
+/**
+ * A signer that can sign messages and typed data.
+ *
+ * @template Inner - the generic type of the inner client that the signer wraps to provide functionality such as signing, etc.
+ */
+export interface SmartAccountSigner {
+ signerType: string;
+ inner: Inner;
+
+ getAddress: () => Promise;
+
+ signMessage: (message: SignableMessage) => Promise;
+
+ signTypedData: <
+ const TTypedData extends TypedData | Record,
+ TPrimaryType extends keyof TTypedData | "EIP712Domain" = keyof TTypedData,
+ >(
+ params: TypedDataDefinition,
+ ) => Promise;
+
+ signAuthorization?: (
+ unsignedAuthorization: AuthorizationRequest,
+ ) => Promise>;
+}
+```
+
+
+
+## `SmartContractAccount`
+
+`SmartContractAccount` is the client-side interface for creating, managing, and interacting with smart contract accounts in a type-safe manner. It extends `viem`'s [`Account`](https://v1.viem.sh/docs/accounts/custom.html) interface with smart account functionality.
+Within Smart Wallets, implementations for Light Account and Modular Account are exported from `@account-kit/smart-contracts`. These implementations handle the logic for generating the deployment data, encoding
+single and batch UO execution, and signing of messages, typed data, and UOs.
+
+Smart Contract Accounts are rarely used on their own, and are typically passed in to [Smart Account Client](#TODO/concepts/smart-account-client) implementations.
+When using either the [React](/wallets/react/quickstart) or [Core](/wallets/core/overview) libraries, the connection of an account and a client are handled for you.
+
+It's also possible to use a custom account when using the [Infra](/wallets/reference/account-kit/infra) library.
+Within `@aa-sdk/core`, a method [`toSmartContractAccount`](/wallets/reference/aa-sdk/core/functions/toSmartContractAccount) is provided so you can create an instance of your Smart Contract Account.
+
+
+
+```ts
+export type SmartContractAccount<
+ Name extends string = string,
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> = LocalAccount & {
+ source: Name;
+ getDummySignature: () => Hex | Promise;
+ encodeExecute: (tx: AccountOp) => Promise;
+ encodeBatchExecute: (txs: AccountOp[]) => Promise;
+ signUserOperationHash: (uoHash: Hex) => Promise;
+ signMessageWith6492: (params: { message: SignableMessage }) => Promise;
+ signTypedDataWith6492: <
+ const typedData extends TypedData | Record,
+ primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
+ >(
+ typedDataDefinition: TypedDataDefinition,
+ ) => Promise;
+ encodeUpgradeToAndCall: (params: UpgradeToAndCallParams) => Promise;
+ getAccountNonce(nonceKey?: bigint): Promise;
+ getInitCode: () => Promise;
+ isAccountDeployed: () => Promise;
+ getFactoryAddress: () => Promise;
+ getFactoryData: () => Promise;
+ getEntryPoint: () => EntryPointDef;
+ getImplementationAddress: () => Promise;
+} & SigningMethods;
+```
+
+
+
+## `StateOverride`
+
+A type defining state overrides for [`eth_call`](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-eth#eth-call) method. An optional address-to-state mapping, where each entry specifies some state to be ephemerally overridden prior to executing the call.
+State overrides allow you to customize the network state for the purpose of the simulation, so this feature is useful when you need to test or simulate scenarios under conditions that aren’t currently present on the live network.
+
+## `ToSmartContractAccountParams`
+
+This type defines the parameters to the `SmartContractAccount` instantiation action, [`toSmartContractAccount`](/wallets/reference/aa-sdk/core/functions/toSmartContractAccount). You can configure this parameter to specify the [`Transport`](https://viem.sh/docs/clients/intro#transports), [`Chain`](https://viem.sh/docs/glossary/types#chain), [`EntryPointDef`](#entrypointdef), and other base functionalities of the smart account that you are creating.
+
+
+
+```ts
+export type ToSmartContractAccountParams<
+ Name extends string = string,
+ TTransport extends Transport = Transport,
+ TChain extends Chain = Chain,
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> = {
+ source: Name;
+ transport: TTransport;
+ chain: TChain;
+ entryPoint: EntryPointDef;
+ accountAddress?: Address;
+ getAccountInitCode: () => Promise;
+ getDummySignature: () => Hex | Promise;
+ encodeExecute: (tx: AccountOp) => Promise;
+ encodeBatchExecute?: (txs: AccountOp[]) => Promise;
+ getNonce?: (nonceKey?: bigint) => Promise;
+ // if not provided, will default to just using signMessage over the Hex
+ signUserOperationHash?: (uoHash: Hex) => Promise;
+ encodeUpgradeToAndCall?: (params: UpgradeToAndCallParams) => Promise;
+ getImplementationAddress?: () => Promise;
+} & Omit &
+ (SigningMethods | Never);
+```
+
+
+
+## `User`
+
+`User` is a type that defines the model for the details of a user's Embedded Account via an Alchemy Signer. It includes the user's `email`, `orgId`, `userId`, `userId`, `address` (the EOA signer address corresponding to the user credentials), and `credentialId`. You can use the [`useUser`](/wallets/reference/account-kit/react/hooks/useUser) react hook to look up a user.
+
+
+
+```ts
+export type User = {
+ email?: string;
+ phone?: string;
+ orgId: string;
+ userId: string;
+ address: Address;
+ solanaAddress?: string;
+ credentialId?: string;
+ idToken?: string;
+ accessToken?: string;
+ claims?: Record;
+};
+```
+
+
+
+## `UserOperationCallData`
+
+`UserOperationCallData` is a type that represents the user's "intent" or the desired outcome representing a specific objective a user aims to accomplish. It includes `target` (the destination address), `data` (the [`Transaction calldata`](/wallets/resources/terms#transaction-calldata)), and `value` (the amount value of ETH, or the native token to send). It acts as the input to the `sendUserOperation` method on [`SmartAccountClient`](#smartaccountclient).
+
+
+
+```ts
+export type UserOperationCallData =
+ | {
+ /* the target of the call */
+ target: Address;
+ /* the data passed to the target */
+ data: Hex;
+ /* the amount of native token to send to the target (default: 0) */
+ value?: bigint;
+ }
+ | Hex;
+```
+
+
+
+## `UserOperationEstimateGasResponse`
+
+An interface that defines the structure for the response received from the RPC method [`eth_estimateUserOperationGas`](https://alchemy.com/docs/reference/eth-estimateuseroperationgas). This response provides detailed information about the estimated gas usage for a `UserOperation`.
+
+
+
+```ts
+export interface UserOperationEstimateGasResponse<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> {
+ /* Gas overhead of this UserOperation */
+ preVerificationGas: BigNumberish;
+ /* Actual gas used by the validation of this UserOperation */
+ verificationGasLimit: BigNumberish;
+ /* Value used by inner account execution */
+ callGasLimit: BigNumberish;
+ /*
+ * EntryPoint v0.7.0 operations only.
+ * The amount of gas to allocate for the paymaster validation code.
+ * Note: `eth_estimateUserOperationGas` does not return paymasterPostOpGasLimit.
+ */
+ paymasterVerificationGasLimit: TEntryPointVersion extends "0.7.0"
+ ? BigNumberish | undefined
+ : never;
+}
+```
+
+
+
+## `UserOperationOverrides`
+
+Partial structure for overriding default values in a `UserOperationStruct`, such as gas limits and fees. Available fields include `maxFeePerGas`, `maxPriorityFeePerGas`, `callGasLimit`, `preVerificationGas`, `verificationGasLimit`, `paymasterAndData`, or `nonceKey`. You can also specify a `stateOverride` to be passed into `eth_estimateUserOperationGas` during gas estimation. These override values are available from each [`ClientMiddleware`](#clientmiddleware) of the `SmartAccountClient`. Check out [`UserOperationOverrides`](https://github.com/alchemyplatform/aa-sdk/blob/v4.x.x/aa-sdk/core/src/types.ts#L97) page to learn more.
+
+
+
+```ts
+export type UserOperationOverrides<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> = Partial<
+ {
+ callGasLimit:
+ | UserOperationStruct["callGasLimit"]
+ | Multiplier;
+ maxFeePerGas:
+ | UserOperationStruct["maxFeePerGas"]
+ | Multiplier;
+ maxPriorityFeePerGas:
+ | UserOperationStruct["maxPriorityFeePerGas"]
+ | Multiplier;
+ preVerificationGas:
+ | UserOperationStruct["preVerificationGas"]
+ | Multiplier;
+ verificationGasLimit:
+ | UserOperationStruct["verificationGasLimit"]
+ | Multiplier;
+
+ /**
+ * The same state overrides for
+ * [`eth_call`](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-eth#eth-call) method.
+ * An address-to-state mapping, where each entry specifies some state to be ephemerally overridden
+ * prior to executing the call. State overrides allow you to customize the network state for
+ * the purpose of the simulation, so this feature is useful when you need to estimate gas
+ * for user operation scenarios under conditions that aren’t currently present on the live network.
+ */
+ stateOverride: StateOverride;
+ } & UserOperationPaymasterOverrides
+> &
+ /**
+ * This can be used to override the nonce or nonce key used when calling `entryPoint.getNonce`
+ * It is useful when you want to use parallel nonces for user operations
+ *
+ * NOTE: not all bundlers fully support this feature and it could be that your bundler will still only include
+ * one user operation for your account in a bundle
+ */
+ Partial<
+ | {
+ nonceKey: bigint;
+ nonce: never;
+ }
+ | { nonceKey: never; nonce: bigint }
+ >;
+```
+
+
+
+## `UserOperationReceipt`
+
+An interface that defines the structure for the response received from the RPC method [`eth_getUserOperationReceipt`](https://alchemy.com/docs/reference/eth-getuseroperationreceipt). It includes details like sender, nonce, gas cost, and success status of the `UserOperation`.
+
+
+
+```ts
+export interface UserOperationReceipt {
+ /* The request hash of the UserOperation. */
+ userOpHash: Hash;
+ /* The entry point address used for the UserOperation. */
+ entryPoint: Address;
+ /* The account initiating the UserOperation. */
+ sender: Address;
+ /* The nonce used in the UserOperation. */
+ nonce: BigNumberish;
+ /* The paymaster used for this UserOperation (or empty). */
+ paymaster?: Address;
+ /* The actual amount paid (by account or paymaster) for this UserOperation. */
+ actualGasCost: BigNumberish;
+ /* The total gas used by this UserOperation (including preVerification, creation, validation, and execution). */
+ actualGasUsed: BigNumberish;
+ /* Indicates whether the execution completed without reverting. */
+ success: boolean;
+ /* In case of revert, this is the revert reason. */
+ reason?: string;
+ /* The logs generated by this UserOperation (not including logs of other UserOperations in the same bundle). */
+ logs: string[];
+ /* The TransactionReceipt object for the entire bundle, not only for this UserOperation. */
+ receipt: TransactionReceipt;
+ /* The status of the useroperation. Could be "Mined" or "Preconfirmed". */
+ status: string;
+}
+```
+
+
+
+## `UserOperationRequest`
+
+Interface for the request format required for a JSON-RPC request to `eth_sendUserOperation`. It includes sender, nonce, gas limits, fees, and more fields.
+
+
+
+```ts
+// Reference: https://eips.ethereum.org/EIPS/eip-4337#definitions
+export type UserOperationRequest<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> = (TEntryPointVersion extends "0.6.0"
+ ? UserOperationRequest_v6
+ : TEntryPointVersion extends "0.7.0"
+ ? UserOperationRequest_v7
+ : never) &
+ Eip7702ExtendedFields;
+```
+
+
+
+## `UserOperationResponse`
+
+An interface that defines the structure for the response received from the RPC method [`eth_getUserOperationByHash`](https://alchemy.com/docs/reference/eth-getuseroperationbyhash), detailing the result of executing a `UserOperation`. It includes the block number, block hash, transaction hash and more information associated with the UO.
+
+
+
+```ts
+export interface UserOperationResponse<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> {
+ /* the User Operation */
+ userOperation: UserOperationRequest;
+ /* the address of the entry point contract that executed the user operation */
+ entryPoint: Address;
+ /* the block number the user operation was included in */
+ blockNumber: BigNumberish;
+ /* the hash of the block the user operation was included in */
+ blockHash: Hash;
+ /* the hash of the transaction that included the user operation */
+ transactionHash: Hash;
+}
+```
+
+
+
+## `UserOperationStruct`
+
+Interface for structuring a `UserOperation`, with fields similar to `UserOperationRequest` but used for building requests.
+
+
+
+```ts
+export type UserOperationStruct<
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+> = (TEntryPointVersion extends "0.6.0"
+ ? UserOperationStruct_v6
+ : TEntryPointVersion extends "0.7.0"
+ ? UserOperationStruct_v7
+ : never) &
+ Eip7702ExtendedFields;
+```
+
+
diff --git a/fern/wallets/pages/shared/create-gas-policy.mdx b/fern/wallets/pages/shared/create-gas-policy.mdx
new file mode 100644
index 000000000..5b9dcbe20
--- /dev/null
+++ b/fern/wallets/pages/shared/create-gas-policy.mdx
@@ -0,0 +1,14 @@
+A gas manager policy is a set of rules that define which UOs are eligible for gas sponsorship. You can control which operations are eligible for sponsorship by defining rules:
+
+* **Spending rules**: limit the amount of money or the number of user ops that can be sponsored by this policy
+* **Allowlist**: restrict wallet addresses that are eligible for sponsorship. The policy will only sponsor gas for UOs that were sent by addresses on this list.
+* **Blocklist**: ban certain addresses from receiving sponsorship under this policy
+* **Policy duration**: define the duration of your policy and the sponsorship expiry period. This is the period for which the Gas Manager signature (paymaster data) will remain valid once it is generated.
+
+To learn more about policy configuration, refer to the guide on [setting up a gas manager policy](https://alchemy.com/docs/setup-a-gas-manager-policy/?a=ak-docs).
+
+Once you have decided on policy rules for your app, [create a policy](https://dashboard.alchemy.com/gas-manager/policy/create/?a=ak-docs) in the Gas Manager dashboard.
+
+Now you should have a Gas policy created with a policy id you can use to sponsor gas for your users.
+
+
diff --git a/fern/wallets/pages/signer/as-an-eoa.mdx b/fern/wallets/pages/signer/as-an-eoa.mdx
new file mode 100644
index 000000000..56c212992
--- /dev/null
+++ b/fern/wallets/pages/signer/as-an-eoa.mdx
@@ -0,0 +1,37 @@
+---
+title: Using the Signer as an EOA
+description: Learn how to use the Alchemy Signer as an EOA
+slug: wallets/signer/as-an-eoa
+---
+
+
+ Note that EOA wallets will not have access to smart account features like gas
+ sponsorship, batched transactions, multi-owner, or plugins. If you want to
+ switch from EOA to smart accounts later, then each user will need to transfer
+ their assets from the EOA account to a new smart account. It is not currently
+ possible to "upgrade" an EOA to a smart contract account, although the
+ community is discussing potential
+ [EIPs](https://eips.ethereum.org/EIPS/eip-7377) to do that in the future.
+
+
+Because the Alchemy Signer has its own `address` and supports signing messages as raw hashes, it is possible to use this signer as an EOA directly. To do so, you can adapt the AlchemySigner to your library of choice and leverage its `signMessage`, `signTypedData`, and `signTransaction` methods directly. The public address of the signer can be accessed via `getAddress`.
+
+If you are using viem, then you can use the `toViemAccount` method which will allow you to use the signer with a [`WalletClient`](https://viem.sh/docs/clients/wallet#local-accounts-private-key-mnemonic-etc).
+
+
+
+```ts createWalletClient.ts
+import { signer } from "./signer";
+import { createWalletClient, http } from "viem";
+import { sepolia } from "viem/chains";
+
+export const walletClient = createWalletClient({
+ transport: http("alchemy_rpc_url"),
+ chain: sepolia,
+ account: signer.toViemAccount(),
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/add-passkey.mdx b/fern/wallets/pages/signer/authentication/add-passkey.mdx
new file mode 100644
index 000000000..d14743c67
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/add-passkey.mdx
@@ -0,0 +1,25 @@
+---
+title: Add Passkey
+description: Learn how to add a passkey to a user account
+slug: wallets/signer/authentication/add-passkey
+---
+
+If your user has already authenticated with an email or passkey, you can also add a new passkey to their account. This is
+useful in the case that you want to give your users a more convenient method of logging in or if they want to add more devices
+to their account.
+
+## Add a passkey
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+// NOTE: this assumes you have already authenticated the user
+// you can further customize the Credential Creation Options here
+await signer.addPasskey({});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/auth0.mdx b/fern/wallets/pages/signer/authentication/auth0.mdx
new file mode 100644
index 000000000..2e64b7a9e
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/auth0.mdx
@@ -0,0 +1,69 @@
+---
+title: Custom Auth Providers with Auth0
+description: Authenticate a user using a custom OAuth provider with Auth0
+slug: wallets/signer/authentication/auth0
+---
+
+You can use Auth0 to integrate an auth provider other than the ones listed in the dashboard. Let’s try using Auth0 to add “Login with GitHub” to our app.
+
+First, create an account or log in to an existing account on [auth0.com](http://auth0.com). In the Auth0 dashboard, select “Authentication → Social” in the sidebar, then “Create Social Connection,” and choose the provider you want:
+
+
+
+You have the option of using Auth0’s dev keys for GitHub or adding your own. If you want to add your own credentials, click the link that says “How to obtain a Client ID” and follow the directions.
+
+You’ll also be able to select the attributes and permissions you will be requesting. It is recommended at the very least to request the user’s email address because it could later be useful for merging accounts from different providers, but you have the option of asking for whatever information will be useful for your app (but note that your users will be prompted for consent to share whatever information you request). After filling out this section, your GitHub configuration in Auth0 might look like this:
+
+
+
+Also on this page, note the “Name” field containing “github”. This will be used later as a variable in adding authentication to your app.
+
+Once you confirm on this page, you’ll be taken to a page where you can choose which of your Auth0 apps will use this login method. Enable it for the app of your choice, which may just be “Default App” if this is a newly created Auth0 account:
+
+
+
+Finally, we need to add your Auth0 credentials to the Alchemy dashboard. To find them, go to “Applications → Applications” in the sidebar and select an application for which you enabled your auth method in the previous step. Take note of the "Domain", "Client ID", and "Client Secret" fields:
+
+
+
+and add them to the embedded accounts auth config in your Alchemy dashboard:
+
+
+
+In addition to the "Client ID" and "Client Secret" fields needed by the other auth providers, you must also fill in the “Auth0 Domain,” field from the Auth0 dashboard.
+
+You can now authenticate with Auth0 in the same way as the other auth providers, passing in an `authProviderId` of `"auth0"`. If you do this, your users will be taken to an Auth0 login page, in which they can choose the authentication method they want such as GitHub.
+
+However, you may prefer to send your users directly to the GitHub login without passing through an Auth0 selection screen first. To do this, you can pass the additional parameter `auth0Connection` to your `authenticate` call. The value which should be passed to `auth0Connection` is the string that appeared in the “Name” field of your auth provider connection in the Auth0 dashboard, which in this case was “github”.
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+await authenticate({
+ type: "oauth",
+ authProviderId: "auth0",
+ auth0Connection: "github",
+ mode: "popup",
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/email-magic-link.mdx b/fern/wallets/pages/signer/authentication/email-magic-link.mdx
new file mode 100644
index 000000000..99bce4150
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/email-magic-link.mdx
@@ -0,0 +1,81 @@
+---
+title: Email Magic Link Authentication
+description: Authenticate a user using an email magic link
+slug: wallets/signer/authentication/email-magic-link
+---
+
+Email magic link authentication allows you to log in and sign up users using an email address. Your users will receive a link in their inbox which will redirect them to your site (configured in the dashboard) to complete login.
+
+
+We recommend using the OTP email flow instead, as it is more reliable across different browser environments. OTP flows have also been shown to have up to a 3x higher conversion rate and a 10-second faster flow compared to magic link.
+
+For setting up the OTP flow, see [Email OTP Authentication](/wallets/authentication/login-methods/email-otp).
+
+
+
+
+ For setting up an account config, see the [Signer
+ Quickstart](/wallets/signer/quickstart).
+
+
+## Authenticate a user
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+// send the email
+// resolves when the user is fully authenticated (magic link + optional MFA), even if completion happens in another tab/window
+await signer.authenticate({
+ type: "email",
+ emailMode: "magicLink",
+ email: "user@mail.com",
+});
+
+// later once the user has clicked the link
+const url = new URL(window.location.href);
+const bundle = url.searchParams.get("bundle");
+if (!bundle) {
+ throw new Error("No bundle found in URL");
+}
+
+// resolves when the user is fully authenticated (magic link + optional MFA), even if completion happens in another tab/window
+await signer.authenticate({
+ type: "email",
+ bundle,
+});
+```
+
+
+
+
+
+### Track Authentication Status
+
+Use `signer.on("statusChanged", callback)` and the `AlchemySignerStatus` enum to respond to OTP/MFA prompts and completion:
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+import { AlchemySignerStatus } from "@account-kit/signer";
+
+signer.on("statusChanged", (status) => {
+ switch (status) {
+ case AlchemySignerStatus.AWAITING_EMAIL_AUTH:
+ // show OTP input UI
+ break;
+ case AlchemySignerStatus.AWAITING_MFA_AUTH:
+ // show TOTP input UI
+ break;
+ case AlchemySignerStatus.CONNECTED:
+ // authentication complete
+ break;
+ }
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/email-otp.mdx b/fern/wallets/pages/signer/authentication/email-otp.mdx
new file mode 100644
index 000000000..9aac1796e
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/email-otp.mdx
@@ -0,0 +1,70 @@
+---
+title: Email OTP Authentication
+description: Authenticate a user using an email OTP code
+slug: wallets/authentication/login-methods/email-otp
+---
+
+Email OTP authentication allows you to log in and sign up users using an email address. Your users will receive six-digit code in their inbox which they can enter in your site to complete login.
+
+
+ For setting up an account config, see the [Signer
+ Quickstart](/wallets/signer/quickstart).
+
+
+## Authenticate a user
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+// send the email
+// Promise resolves when the user is fully authenticated (OTP + optional MFA),
+// even if final step completes in another tab/window
+await signer.authenticate({
+ type: "email",
+ emailMode: "otp",
+ email: "user@mail.com",
+});
+
+// later once the user has entered the code from their email
+// Promise resolves when the user is fully authenticated (OTP + optional MFA),
+// even if final step completes in another tab/window
+await signer.authenticate({
+ type: "otp",
+ otpCode: "123456",
+});
+```
+
+
+
+
+
+### Track Authentication Status
+
+Use `signer.on("statusChanged", callback)` and the `AlchemySignerStatus` enum to respond to OTP/MFA prompts and completion:
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+import { AlchemySignerStatus } from "@account-kit/signer";
+
+signer.on("statusChanged", (status) => {
+ switch (status) {
+ case AlchemySignerStatus.AWAITING_EMAIL_AUTH:
+ // show OTP input UI
+ break;
+ case AlchemySignerStatus.AWAITING_MFA_AUTH:
+ // show TOTP input UI
+ break;
+ case AlchemySignerStatus.CONNECTED:
+ // authentication complete
+ break;
+ }
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/passkey-login.mdx b/fern/wallets/pages/signer/authentication/passkey-login.mdx
new file mode 100644
index 000000000..8566c4908
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/passkey-login.mdx
@@ -0,0 +1,49 @@
+---
+title: Passkey Login
+description: Authenticate a user using a passkey
+slug: wallets/signer/authentication/passkey-login
+---
+
+If a user has added a passkey to their account, or they initially signed up with a passkey, you can easily log them in with that passkey.
+
+## Authenticate a user with email and passkey
+
+If you want to allow sign-up and login with a passkey, you can ask the user for an email to associate with their passkey. This way, they can log in with their email and passkey in the future. Under the hood, the email is also used to check if an account exists already so you can have a unified sign-up and login flow.
+
+
+ It's important that you validate this email before creating an account for the
+ user. This is to prevent users from losing access to their wallets if they
+ lose their device.
+
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+const result = await signer.authenticate({
+ type: "passkey",
+ email: "user@mail.com",
+});
+```
+
+
+
+
+
+## Authenticate an anonymous user
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+const result = await signer.authenticate({
+ type: "passkey",
+ createNew: false,
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/passkey-signup.mdx b/fern/wallets/pages/signer/authentication/passkey-signup.mdx
new file mode 100644
index 000000000..f492868dc
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/passkey-signup.mdx
@@ -0,0 +1,34 @@
+---
+title: Passkey Signup
+description: Authenticate a new user using a passkey
+slug: wallets/signer/authentication/passkey-signup
+---
+
+It is possible to create wallets for users using just a passkey. This is useful for creating wallets for users if you don't want to go through the email OTP or magic link flow.
+
+
+
+## Authenticate a user with email and passkey
+
+If you want to allow sign-up and login with a passkey, you can ask the user for an email to associate with their passkey. This way, they can log in with their email or passkey in the future. Under the hood, the email is also used to check if an account exists already so you can have a unified sign-up and login flow.
+
+
+ It's important that you validate this email before creating an account for the
+ user. This is to prevent users from losing access to their wallets if they
+ lose their device.
+
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+const result = await signer.authenticate({
+ type: "passkey",
+ email: "user@mail.com",
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/server-wallets.mdx b/fern/wallets/pages/signer/authentication/server-wallets.mdx
new file mode 100644
index 000000000..7cee52232
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/server-wallets.mdx
@@ -0,0 +1,197 @@
+---
+title: Server wallets
+description: Control wallets programmatically using access keys
+slug: wallets/authentication/login-methods/server-wallets
+---
+
+
+ Server wallets are in early access and we'd love to hear how you're using
+ them! Contact us at wallets@alchemy.com - we'd love to help you get up and
+ running.
+
+
+Server wallets can be programmatically controlled using access keys. This enables backend applications to sign transactions and messages on behalf of users without requiring interactive authentication.
+
+
+
+## Prerequisites
+
+Get your **API key** by creating a new app in your [Alchemy Dashboard](https://dashboard.alchemy.com/apps). Make sure your desired network is enabled for your app under the Networks tab.
+
+## Step 1: Generate an Access Key
+
+Generate a secure access key for authentication. This key will never be sent to our servers - we'll derive a public key from it when interacting with Alchemy.
+
+
+**Critical: Save your access key securely!**
+
+This access key is required to control your server wallet and cannot be recovered if lost. Make sure to store it in a secure location.
+
+
+
+
+
+```ts twoslash evm.ts
+import { generateAccessKey } from "@account-kit/signer";
+
+// Generate a random access key
+const accessKey = generateAccessKey();
+console.log("Access key:", accessKey);
+
+// Store this securely - you'll need it to control the server signer
+```
+
+
+
+## Step 2: Create the Server Signer
+
+Before we can send transactions we need a way to sign them. Let's create a server signer using the access key you just created to handle this:
+
+
+
+```ts twoslash evm.ts
+import { createServerSigner } from "@account-kit/signer";
+
+const signer = await createServerSigner({
+ auth: {
+ accessKey: "your-access-key-here",
+ },
+ connection: {
+ apiKey: "your-alchemy-api-key",
+ },
+});
+
+console.log("Signer address:", await signer.getAddress());
+```
+
+
+
+
+ Want to use the same access key to control multiple server signers? Check out
+ the example below to learn how to use `auth.accountId` to create multiple
+ signers that you can control with the same access key.
+
+
+
+
+```ts twoslash
+import { createServerSigner } from "@account-kit/signer";
+
+const businessSigner = await createServerSigner({
+ auth: {
+ accessKey: "your-access-key-here",
+ accountId: "business",
+ },
+ connection: {
+ apiKey: "your-alchemy-api-key",
+ },
+});
+
+const personalSigner = await createServerSigner({
+ auth: {
+ accessKey: "your-access-key-here", // same access key
+ accountId: "personal", // different account ID
+ },
+ connection: {
+ apiKey: "your-alchemy-api-key",
+ },
+});
+
+// These will have different addresses despite using the same access key
+console.log("Business address:", await businessSigner.getAddress());
+console.log("Personal address:", await personalSigner.getAddress());
+```
+
+
+
+## Step 3: Send a Transaction
+
+We can now send transactions using the Smart Account Client:
+
+
+
+```ts twoslash evm.ts
+import { createSmartWalletClient } from "@account-kit/wallet-client";
+import { alchemy, baseSepolia } from "@account-kit/infra";
+
+// Create Smart Account Client using the signer from Step 2
+const client = createSmartWalletClient({
+ transport: alchemy({
+ apiKey: "your-alchemy-api-key",
+ }),
+ chain: baseSepolia,
+ signer,
+ policyId: "your-sponsor-gas-policy-id", // Make sure enabled on Base Sepolia
+});
+
+// Retrieve account
+const account = await client.requestAccount();
+console.log("Smart account address:", account.address);
+
+// Send a transaction
+const { preparedCallIds } = await client.sendCalls({
+ calls: [
+ {
+ to: "0x1234567890123456789012345678901234567890",
+ value: "0x0",
+ data: "0x",
+ },
+ ],
+ from: account.address,
+});
+console.log("Transaction sent:", preparedCallIds[0]);
+```
+
+
+
+## Solana Support
+
+Server wallet can also easily be used on Solana. To start sending sponsored transactions on Solana, set up a Solana sponsor gas policy in our [dashboard](https://dashboard.alchemy.com/gas-manager) and follow the guide below.
+
+
+
+```ts twoslash solana.ts
+import { createServerSigner } from "@account-kit/signer";
+import { Connection, SystemProgram, PublicKey } from "@solana/web3.js";
+
+// Set up Solana connection
+const connection = new Connection(
+ `https://solana-devnet.g.alchemy.com/v2/your-alchemy-api-key`,
+);
+
+const signer = await createServerSigner({
+ auth: { accessKey: "your-access-key" },
+ connection: { apiKey: "your-alchemy-api-key" },
+});
+
+// Convert the signer to a Solana-compatible signer
+const solanaSigner = signer.toSolanaSigner();
+
+// Build transaction instructions
+const instructions = [
+ SystemProgram.transfer({
+ fromPubkey: new PublicKey(solanaSigner.address),
+ toPubkey: new PublicKey(solanaSigner.address),
+ lamports: 0,
+ }),
+];
+
+// Add sponsorship
+const tx = await solanaSigner.addSponsorship(
+ instructions,
+ connection,
+ "your-solana-sponsorship-policy-id", // Make sure enabled on Solana devnet
+);
+
+// Sign the transaction
+await solanaSigner.addSignature(tx);
+
+// Send the transaction
+const hash = await connection.sendTransaction(tx);
+console.log("Transaction sent:", hash);
+```
+
+
+
+
+
diff --git a/fern/wallets/pages/signer/authentication/social-login.mdx b/fern/wallets/pages/signer/authentication/social-login.mdx
new file mode 100644
index 000000000..9d120547c
--- /dev/null
+++ b/fern/wallets/pages/signer/authentication/social-login.mdx
@@ -0,0 +1,32 @@
+---
+title: OAuth Authentication
+description: Authenticate a user using OAuth
+slug: wallets/signer/authentication/social-login
+---
+
+Social login authentication allows you to log in and sign up users using an OAuth provider, such as Google Sign-In or Facebook Login. You can also configure custom providers through Auth0.
+
+
+ To set up configurations to enable social login, see the [Signer
+ Quickstart](/wallets/signer/quickstart) to enable a social policy in the
+ dashboard.
+
+
+## Authenticate a user
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+
+await signer.authenticate({
+ type: "oauth",
+ authProviderId: "google", // Choose between the auth providers you selected to support from your auth policy
+ mode: "redirect", // Alternatively, you can choose "popup" mode
+ redirectUrl: "/", // After logging in, redirect to the index page
+});
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/export-private-key.mdx b/fern/wallets/pages/signer/export-private-key.mdx
new file mode 100644
index 000000000..e69ad8633
--- /dev/null
+++ b/fern/wallets/pages/signer/export-private-key.mdx
@@ -0,0 +1,107 @@
+---
+outline: deep
+title: Export Private Key
+description: Learn how to enable a user to export their private key with the Alchemy Signer
+slug: wallets/signer/export-private-key
+---
+
+The Alchemy Signer allows you to export a user's private key, allowing them a right to exit at any time. It is considered a best practice to allow your users to export their private key, as it gives them full control over their account. The private key export method does not rely on Alchemy's infrastructure, so even if Alchemy is down, a user can still export their private key.
+
+## Using [useExportAccount](/wallets/reference/account-kit/react/hooks/useExportAccount)
+
+A hook use to export the private key for an account. It returns the mutation functions to kick off the export process, as well as a component to render the account recovery details in an iframe.
+
+### Import
+
+```ts
+import { useExportAccount } from "@account-kit/react";
+```
+
+### Usage
+
+```ts
+import { useExportAccount } from "@account-kit/react";
+
+const {
+ exportAccount,
+ isExported,
+ isExporting,
+ error,
+ ExportAccountComponent,
+} = useExportAccount({
+ params: {
+ iframeContainerId: "my-iframe-container",
+ },
+});
+```
+
+## Using the signer
+
+To add export private key functionality to your app, you can use the `exportPrivateKey` method on the signer.
+
+
+
+```tsx ExportPrivateKey.tsx
+import React from "react";
+import { useMutation } from "@tanstack/react-query";
+import { signer } from "./signer";
+
+const TurnkeyExportWalletContainerId = "turnkey-export-wallet-container-id";
+const TurnkeyExportWalletElementId = "turnkey-export-wallet-element-id";
+
+// This allows us to style the embedded iframe
+const iframeCss = `
+iframe {
+ box-sizing: border-box;
+ width: 100%;
+ height: 120px;
+ border-radius: 8px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: rgba(216, 219, 227, 1);
+ padding: 20px;
+}
+`;
+
+export const ExportPrivateKeyView = () => {
+ // we are using react-query to handle loading states more easily, but feel free to use w/e state management library you prefer
+ const {
+ mutate: exportWallet,
+ isPending,
+ data,
+ } = useMutation({
+ mutationFn: () =>
+ signer.exportWallet({
+ iframeContainerId: TurnkeyExportWalletContainerId,
+ iframeElementId: TurnkeyExportWalletElementId,
+ }),
+ });
+
+ // Once the user clicks the button, a request will be sent to initialize private key export
+ // once the request is complete, the iframe will be rendered with either
+ // 1. the private key if the user is logged in with a passkey
+ // 2. the seed phrase if the user is logged in with email
+ return (
+
+ );
+};
+```
+
+
+
+
diff --git a/fern/wallets/pages/signer/overview.mdx b/fern/wallets/pages/signer/overview.mdx
new file mode 100644
index 000000000..bc1250228
--- /dev/null
+++ b/fern/wallets/pages/signer/overview.mdx
@@ -0,0 +1,18 @@
+---
+title: Signer overview
+description: Overview of the Alchemy Signer
+slug: wallets/signer/overview
+---
+
+The Alchemy Signer is a `SmartAccountSigner` that is protected by Alchemy's Signer Infrastructure. Using the Alchemy Signer, you can get started building embedded accounts with just an Alchemy API key!
+
+When using Smart Wallets with React via `@account-kit/react` or `@account-kit/core`, the assumption is that you're using the Alchemy Signer with our smart contract implementations and our Bundler and Gas Manager infrastructure.
+However, you can also use the Alchemy Signer as a standalone signer with your own smart contracts or 3rd party infrastructure. In these guides, you'll learn more about how to choose a signer and how to use our signer directly via
+the `@account-kit/signer` package to log users in with email auth, and create a embedded account with our signer to enable email, passkeys (i.e. biometrics), and soon social auth flows!
+
+
+The Alchemy Signer requires the use of an `iframe`, so must be used in the context of an application. You'll need to build your own custom frontend.
+
+See an example of a full Alchemy Embedded Account [here](https://github.com/alchemyplatform/embedded-accounts-demo.git)!
+
+
diff --git a/fern/wallets/pages/signer/policies/key-concepts-and-examples.mdx b/fern/wallets/pages/signer/policies/key-concepts-and-examples.mdx
new file mode 100644
index 000000000..6af84dc15
--- /dev/null
+++ b/fern/wallets/pages/signer/policies/key-concepts-and-examples.mdx
@@ -0,0 +1,48 @@
+---
+title: Key Concepts & Examples
+description: Key Concepts and Examples of Smart Wallet Policies
+slug: wallets/signer/policies/key-concepts-and-examples
+---
+
+Smart Wallet Policies are programmatic rules enforced through the offchain policy engine and/or onchain smart contract modules. They govern how a smart wallet interacts with the blockchain, from transaction execution to signer permissions. Unlike traditional embedded wallets, Alchemy Smart Wallets allow you to embed logic that restricts or automates behavior verifiably onchain.
+
+## **Core Concepts**
+
+* **Policies** are a set of rules that define the allowed or denied transactions on a Smart Wallet.
+* **Rules** are a set of conditions that determine if an action is allowed or denied by a policy.
+* **Conditions** are the criteria that are evaluated within a given ruleset like “only allow transfers below 1 ETH” or “restrict interactions to specific contracts” are encoded as conditions.
+
+## **Examples**
+
+Here are practical examples of policies adapted for Smart Wallets, showcasing how developers can tailor functionality:
+
+1. **Limit Transaction Value**
+ * **Description**: Restrict transfers to a maximum of 0.5 ETH per transaction.
+ * **Use Case**: Prevent accidental large transfers in an application.
+2. **Allowlisted Contracts**
+ * **Description**: Only permit interactions with a predefined set of smart contracts (e.g., Uniswap, Aave).
+ * **Use Case:** Limit the user journeys to trusted DeFi protocols in an application.
+3. **Gas Sponsorship Rules**
+ * **Description**: Only enable transactions if an ERC-20 paymaster is used
+ * **Use Case**: Require users to use your token as the gas token for all transactions.
+4. **Multi-Signature Requirements**
+ * **Description:** Require m-of-n signers to approve transactions (e.g. require the standard social login as well as onchain passkey signatures when spending over 5 ETH).
+ * **Use Case:** Secure high value transactions.
+5. **Time Restrictions**
+ * Description: Enable time based restrictions for transactions through a wallet.
+ * Use Case: Creating a temporary signer than can be used by offchain agents and that expires after a pre-defined period.
+6. **Gas Sponsorship Rules**
+ * Description: Only enable transactions if an ERC-20 paymaster (e.g., USDC) covers gas (on-chain or off-chain).
+ * Use Case: Require users to pay gas with your app’s token, boosting its adoption.
+7. **Asset-Specific Caps**
+ * Description: Limit ERC-20 transfers to 500 USDC per transaction (on-chain or off-chain).
+ * Use Case: Control in-game purchases to prevent overspending in a gaming economy.
+8. **Deny Lists**
+ * Description: Block transactions to a flagged phishing contract address (on-chain or off-chain).
+ * Use Case: Protect users from known scams in a wallet interface.
+9. **Multi-Factor Authentication (Offchain)**
+ * Description: Mandate MFA via an authenticator app like Authy (off-chain).
+ * Use Case: Add an extra security layer for user logins in a wallet.
+10. **Chain Restrictions (Offchain)**
+ * Description: Restrict wallet to only transact on a limited set of chains.
+ * Use Case: Enforce a curated chain list for multi-chain application consistency.
diff --git a/fern/wallets/pages/signer/policies/offchain-policy-configuration.mdx b/fern/wallets/pages/signer/policies/offchain-policy-configuration.mdx
new file mode 100644
index 000000000..d4292c9ae
--- /dev/null
+++ b/fern/wallets/pages/signer/policies/offchain-policy-configuration.mdx
@@ -0,0 +1,103 @@
+---
+title: Offchain Policy Configuration
+description: Offchain Smart Wallet Policy Configuration
+slug: wallets/signer/policies/offchain-policy-configuration
+---
+
+**Offchain policies** are a set of rules enforced by our secure wallet infrastructure that define the allowed or denied transactions on a smart wallet.
+
+An offchain policy is defined by the following schema:
+
+```tsx
+type Policy = {
+ version: "1.0";
+ // Allows you to assign a human readable name
+ // for your policy
+ name: string;
+ vm_kind: "EVM" | "SVM";
+ // This is the account that this policy targets
+ account: string;
+ // This is the list of rules that will be enforced
+ // when the policy is evaluated
+ // The supported rules differ based on the network
+ rules: Rule[];
+};
+```
+
+## Rules
+
+A rule defines the specific conditions that must be met for a given Wallet API method.
+
+```tsx
+type Rule =
+ | {
+ name: string;
+ type: "methods";
+ action: "ALLOW" | "DENY";
+ method:
+ | "eth_signTransaction" // EVM only
+ | "sign_operation" // EVM only
+ | "wallet_prepareCalls" // EVM only https://github.com/ethereum/ERCs/pull/758
+ | "wallet_sendPreparedCalls" // EVM only https://github.com/ethereum/ERCs/pull/758
+ | "signAndSendTransaction" // SVM only
+ | "sendTransaction"; // SVM only
+ conditions: Condition[];
+ }
+ | {
+ type: "recipients";
+ action: "ALLOW" | "DENY";
+ address: string[];
+ }
+ | {
+ type: "contracts";
+ action: "ALLOW" | "DENY";
+ address: string[];
+ };
+```
+
+## Conditions
+
+Conditions give you the flexibility to compose multiple requirements that must be met for a given Rule. All conditions must be met in order for the Rule to be enforced.
+
+```tsx
+type Condition =
+ | {
+ type: "field";
+ // This specifies where to evaluate the condition
+ // for EVM. `call` gives you the most flexibility
+ // on enforcement.
+ field_source:
+ | "call" // EVM only
+ | "user_operation" // EVM only
+ | "eth_transaction" // EVM only
+ | "solana_transaction" // Solana only
+ | "solana_instruction" // Solana only
+ | "spl_transaction"; // Solana only
+ field: string;
+ // should only be set when the field_source is `call`
+ // and field is `data`
+ abi?: JSON;
+ comparator: "==" | "!=" | "<" | "<=" | ">" | ">=" | "in";
+ // the value to compare the field against
+ value: string | number | string[];
+ }
+ | {
+ type: "batch_value";
+ max_batch_value: string;
+ }
+ | {
+ type: "gas_limit";
+ max_gas_limit: string;
+ };
+```
+
+The following Fields are supported for a given `field_source`
+
+| Field Source | Supported Fields |
+| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| `call` | `to`, `data`, `value` |
+| `user_operation` | The fields here are all of the fields that are defined in a User Operation Request that would be submitted to the bundler. This depends on the EntryPoint version. Most Conditions can be defined by using the above `call` source instead though. This field_source is useful for enforcing gas limits, fee limits, etc |
+| `eth_transaction` | Any of the fields that are included in an Ethereum Transaction |
+| `solana_transaction` | Any of the fields that are included in a Solana Transaction |
+| `solana_instruction` | Any of the fields defined in a Solana instruction |
+| `spl_transaction` | When a Solana transaction is detected to include an SPL token transaction, this allows enforcing the token transaction based on the `spl_transfer_recipient`, `spl_transfer_value`, `spl_token_address` |
diff --git a/fern/wallets/pages/signer/policies/onchain-policy-configuration.mdx b/fern/wallets/pages/signer/policies/onchain-policy-configuration.mdx
new file mode 100644
index 000000000..6d4af3df5
--- /dev/null
+++ b/fern/wallets/pages/signer/policies/onchain-policy-configuration.mdx
@@ -0,0 +1,11 @@
+---
+title: Onchain Policy Configuration
+description: Onchain Smart Wallet Policy Configuration
+slug: wallets/signer/policies/onchain-policy-configuration
+---
+
+## Onchain Policy Configuration
+
+**Onchain policies** are enforced onchain via ERC-6900 modules on the Smart Wallet.
+
+For more information see our [session key documentation](/wallets/smart-contracts/modular-account-v2/session-keys).
diff --git a/fern/wallets/pages/signer/policies/overview.mdx b/fern/wallets/pages/signer/policies/overview.mdx
new file mode 100644
index 000000000..58e1286c0
--- /dev/null
+++ b/fern/wallets/pages/signer/policies/overview.mdx
@@ -0,0 +1,43 @@
+---
+title: Overview
+description: Overview of Smart Wallet Policies
+slug: wallets/signer/policies/overview
+---
+
+
+ Smart Wallet Policies are in private beta. Please [contact
+ us](mailto:support@alchemy.com) if you would like to build with Policies. Beta
+ features may change.
+
+
+**Smart Wallets** enable seamless crypto experiences with gas sponsorship, batched transactions, and chain abstraction. Secured by non-custodial Trusted Execution Environments (TEEs) and enterprise-grade audited smart contract accounts, smart wallets protect user assets with both offchain and onchain safeguards.
+
+Policies allow you to set rules and constraints governing how smart wallets operate, ensuring security and control over onchain actions. Using the policy dashboard, you can easily configure rules such as spending limits, contract allowlists, or denylists. This overview explains how you can leverage policies to further secure your smart wallet.
+
+**Key Features:**
+
+* **Granular control**: Define allowable actions, such as approved contracts or maximum transfer amounts.
+* **Scalability**: Policies are built on Alchemy’s high-performance RPC and gas sponsorship systems, ensuring seamless operation at scale.
+* **Composable Security:** Policies can be defined onchain or offchain and seamlessly composed to authorize smart wallet operations to multi-layer beyond standard authentication mechanisms.
+
+
+
+**Offchain Policies (EVM & Solana):** Offchain policies shift rule enforcement to Alchemy’s offchain infrastructure. In EVM, these policies can also be composed with the onchain policies to provide multi-layered security.
+
+Offchain policies support many rules including:
+
+* **Transaction Limits:** Restrict the value of transfers (e.g., cap at 1 ETH per transaction or 10 ETH daily) to prevent overspending or unauthorized large moves.
+* **Contract Allowlists:** Limit interactions to approved smart contracts (e.g., Uniswap, Aave), enhancing security by blocking untrusted protocols.
+* **Multi-factor Authentication:** Add an extra layer of security by requiring multiple authentication factors to sign a transaction.
+* **Chain Restrictions:** Limit transactions to only certain chains
+* **Gas Sponsorship Rules:** Define custom conditions for gas sponsorship (e.g. sponsor up to $10 in gas, or first 10 transactions, allowlist/blocklist senders for sponsorship, custom rules, etc.).
+
+**Onchain Policies (EVM only):** Onchain policies lock in trust, transparency, and developer right to exit — rules baked into smart contracts enforce security and consistency without relying on fragile offchain servers or middlemen. Because the rules are defined onchain, they remain consistent regardless of which key provider is used.
+
+* **Transaction Limits:** Use onchain modules to restrict the value of transfers (e.g., cap at 1 ETH per transaction or 10 ETH daily) to prevent overspending or unauthorized large moves.
+* **Contract Allowlists:** Use onchain modules to limit interactions to approved smart contracts (e.g., Uniswap, Aave), enhancing security by blocking untrusted protocols.
+* **Multi-Signature Requirements:** Enforce quorum rules (e.g., 2-of-3 signers for high-value transactions), ideal for shared custody, treasury management, or onchain multi-factor authentication.
+* **Time Restrictions:** Allow transactions only within specific time windows (e.g., within the next 24 hours), enabling scheduled operations.
+* **Gas Sponsorship Rules:** Define requirements for paymaster definitions (e.g., require your token is used for gas or that transactions are sponsored under certain conditions).
+* **Asset-Specific Caps:** Use onchain session keys to set limits on ERC-20 token transfers (e.g., max 1000 USDC per action) or native assets.
+* **Deny Lists:** Block interactions with flagged addresses or contracts, mitigating risks from known vulnerabilities.
diff --git a/fern/wallets/pages/signer/quickstart.mdx b/fern/wallets/pages/signer/quickstart.mdx
new file mode 100644
index 000000000..59eeda15a
--- /dev/null
+++ b/fern/wallets/pages/signer/quickstart.mdx
@@ -0,0 +1,81 @@
+---
+title: Signer Quickstart
+description: Get started with the Alchemy Signer
+slug: wallets/signer/quickstart
+---
+
+
+ Using React? Follow [this](/wallets/react/quickstart) quickstart guide instead
+ for easy-to-use React hooks and integration with Smart Wallets.
+
+
+If you're not using React, use the `@account-kit/signer` package to create and use Smart Wallets.
+
+## Get your API key and create an account config
+
+
+
+## Install the Signer package
+
+**Prerequisites**
+
+* minimum Typescript version of 5
+
+**Installation**
+
+
+ ```bash yarn
+ yarn add @account-kit/signer
+ ```
+
+ ```bash npm
+ yarn add @account-kit/signer
+ ```
+
+
+## Create a signer instance
+
+
+
+## Authenticate a user
+
+Next, you'll need to authenticate your user before you can use the Signer as an owner on the account.
+
+
+ In this example, we use email auth, but we support a number of other auth
+ methods. See the other guides for more examples.
+
+
+
+ ```ts example.ts
+ import { signer } from "./signer";
+
+ const result = await signer.authenticate({
+ type: "email",
+ email: "example@mail.com",
+ });
+ ```
+
+
+
+
+## Use signer as owner on Smart Account
+
+Now that you have authenticated your user, you can use the signer as an owner on a Smart Contract Account.
+
+
+ ```ts example.ts
+ import { createLightAccount } from "@account-kit/smart-contracts";
+ import { sepolia } from "@account-kit/infra";
+ import { http } from "viem";
+ import { signer } from "./signer";
+
+ const account = await createLightAccount({
+ signer,
+ chain: sepolia,
+ transport: http(`${sepolia.rpcUrls.alchemy.http[0]}/API_KEY`),
+ });
+ ```
+
+
+
diff --git a/fern/wallets/pages/signer/solana-wallets/solana-signer-package.mdx b/fern/wallets/pages/signer/solana-wallets/solana-signer-package.mdx
new file mode 100644
index 000000000..162d9fcc3
--- /dev/null
+++ b/fern/wallets/pages/signer/solana-wallets/solana-signer-package.mdx
@@ -0,0 +1,133 @@
+---
+title: How to sign messages and send transactions on Solana
+description: How to sign messages and send transactions on Solana
+text: How to sign messages and send transactions on Solana
+link: /signer/solana-wallets/get-started
+slug: wallets/signer/solana-wallets/get-started
+---
+
+If you're not using React, use the `@account-kit/signer` package to create smart wallets on Solana, sign messages, sign transactions, and sponsor transaction fees.
+
+If you're using React, get started with Solana wallets [here](/wallets/react/solana-wallets/get-started).
+
+# Create a Solana signer
+
+Once you've [authenticated users](/wallets/signer/quickstart), convert the `AlchemyWebSigner` to a Solana compatible signer using either of these methods:
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+import { SolanaSigner } from "@account-kit/signer";
+
+const solanaSigner = signer.toSolanaSigner();
+// OR
+const solanaSigner2 = new SolanaSigner(signer);
+```
+
+
+
+
+
+# Sign Message
+
+To sign either a string or byte array with your Solana wallet use the `signMessage` method. The method accepts a `UInt8Array` as input:
+
+
+
+```ts example.ts
+import { signer } from "./signer";
+import { toBytes } from "viem";
+
+const solanaSigner = signer.toSolanaSigner();
+const signedMessage = await solanaSigner.signMessage(
+ toBytes("Message as a string converted into UInt8Array"),
+);
+```
+
+
+
+
+
+# Sign Transaction
+
+To sign a prepared transaction with your Solana wallet use the `addSignature` method. The following example demonstrates signing a transfer transaction:
+
+```ts signing-transaction-solana-signer.tsx
+import { Connection, PublicKey, SystemProgram } from "@solana/web3.js";
+
+async function signTransaction(
+ signer: SolanaSigner,
+ toAddress: string,
+ value: number,
+) {
+ const instructions = [
+ SystemProgram.transfer({
+ fromPubkey: new PublicKey(signer.address),
+ toPubkey: new PublicKey(params.transfer.toAddress),
+ lamports: params.transfer.amount,
+ }),
+ ];
+
+ // Construct transaction with the provided instructions
+ const transaction = await signer.createTransfer(instructions, connection);
+
+ // Sign the transaction using the Solana wallet
+ await signer.addSignature(transaction);
+
+ return transaction;
+}
+
+const signedTransaction = await signTransaction(signer, "", 1234);
+// Broadcast the signed transaction to the network using @solana/web3.js
+const connection = new Connection(
+ "https://solana-devnet.g.alchemy.com/v2/",
+);
+await connection.sendTransaction(signedTransaction);
+```
+
+# Sponsor transaction fees
+
+To sponsor transaction fees on Solana:
+
+1. Set up your [sponsorship policy](https://dashboard.alchemy.com/services/gas-manager/configuration) in the dashboard to retrieve a **policy ID**
+2. Add sponsorship to the preparred transaction before signing and sending
+
+```ts sponsored-solana-transaction-signer.tsx
+import { Connection, PublicKey, SystemProgram } from "@solana/web3.js";
+
+async function signTransaction(
+ signer: SolanaSigner,
+ toAddress: string,
+ value: number,
+) {
+ const instructions = [
+ SystemProgram.transfer({
+ fromPubkey: new PublicKey(signer.address),
+ toPubkey: new PublicKey(params.transfer.toAddress),
+ lamports: params.transfer.amount,
+ }),
+ ];
+
+ // First - add sponsorship to the preparred transaction
+ const transaction = await signer.addSponsorship(
+ instructions,
+ connection,
+ "", // Replace with your solana sponsorship policy ID: https://dashboard.alchemy.com/services/gas-manager/configuration
+ );
+
+ // Sign the transaction using the Solana wallet
+ await signer.addSignature(transaction);
+
+ return transaction;
+}
+
+const signedTransaction = await signTransaction(signer, "", 1234);
+
+// Broadcast the signed transaction to the network using @solana/web3.js
+const connection = new Connection(
+ "https://solana-devnet.g.alchemy.com/v2/", // Replace with your Alchemy API key
+);
+
+await connection.sendTransaction(signedTransaction);
+```
diff --git a/fern/wallets/pages/signer/user-sessions.mdx b/fern/wallets/pages/signer/user-sessions.mdx
new file mode 100644
index 000000000..42de82ffb
--- /dev/null
+++ b/fern/wallets/pages/signer/user-sessions.mdx
@@ -0,0 +1,27 @@
+---
+title: Manage user sessions
+description: Learn how to configure and leverage sessions for you users with the Alchemy Signer
+slug: wallets/signer/user-sessions
+---
+
+By default, `AlchemyWebSigner` user sessions are cached in `localStorage` for 15 minutes.
+
+You can customize session length by passing a [`sessionConfig`](/wallets/reference/account-kit/signer/classes/AlchemyWebSigner) to your `AlchemyWebSigner` constructor.
+
+You can check if the user has an active session with the following command:
+
+
+
+```ts getAuthDetails.ts
+import { signer } from "./signer";
+
+// NOTE: this method throws if there is no authenticated user
+// so we return null in the case of an error
+const user = await signer.getAuthDetails().catch(() => null);
+```
+
+
+
+
+
+If there is an existing session, then your signer is ready for use! If not, see the section above for logging users in.
diff --git a/fern/wallets/pages/signer/what-is-a-signer.mdx b/fern/wallets/pages/signer/what-is-a-signer.mdx
new file mode 100644
index 000000000..e53958887
--- /dev/null
+++ b/fern/wallets/pages/signer/what-is-a-signer.mdx
@@ -0,0 +1,38 @@
+---
+outline: deep
+title: Choosing a Signer
+description: Explore Smart Wallets integration guides for signers including Magic.Link, Privy, Web3Auth, EOAs, and many more!
+slug: wallets/signer/what-is-a-signer
+---
+
+A **Signer** is a service (e.g. Alchemy Signer) or application (e.g. MetaMask) that manages the private key and signs operations. Most web3 users today use an [Externally Owned Account (EOA)](https://ethereum.org/en/developers/docs/accounts/#externally-owned-accounts-and-key-pairs) with a self-custodial Signer such as MetaMask to manage the private key.
+
+With Smart Wallets, you will deploy a **smart account** for each user instead of an EOA wallet. This smart account stores the user's assets (e.g. tokens or NFTs).
+
+The signer connected to the `SmartAccountClient` is used to sign messages, data including user operations and transactions. The signatures for the user operation will be only valid and execute if the signer is the owner or one of the owners of the account.
+
+Using Smart Wallets, you can secure your user's account with an email, social login, or passkeys. You can also use a self-custodial wallet like MetaMask as the Signer. You can choose any Signer service or application to manage the Owner private key for the user.
+
+This doc provides a basic introduction to signers and the criteria you should consider when choosing which Signer to use with Smart Wallets in your application.
+
+## Role of a Signer
+
+The Signer plays a crucial role in your app because it controls the user’s smart account. The Signer is responsible for:
+
+* Securely storing the user’s private key which controls the user’s assets
+* Authenticating the user
+* Protecting the user’s account from phishing attacks
+* Signing user operations requested by the user, if and only if the user has authenticated
+* Optionally offering account recovery methods
+
+## Alchemy Signer
+
+User security is our top priority. Alchemy Signer is a non-custodial signer service where private keys are securely generated inside a [secure enclave](https://docs.turnkey.com/security/secure-enclaves). Only the end user can access their keys, not Alchemy nor your app. Read more about Turnkey's [security model](https://docs.turnkey.com/security/our-approach) that powers Alchemy Signer.
+
+Alchemy Signer is supported out of the box with Smart Wallets via easy to use [React Hooks](/wallets/react/getting-started) and lower-lever [SDKs](/wallets/signer/quickstart). Using Smart Wallets with the Alchemy Signer, you can secure your user's account with an email, social login, or passkeys.
+
+### Read more about signers [here](/wallets/signer/what-is-a-signer).
+
+##
+
+*Disclaimer: This page refers to third-party services, products software, technology, and content (collectively, “Third-Party Services”) that may be integrated or interact with Alchemy’s software and services. Alchemy is not responsible for any Third-Party Service, or for any compatibility issues, errors, or bugs caused in whole or in part by the Third-Party Service or any update or upgrade thereto. Your use of any Third-Party Service is at your own risk. You are responsible for obtaining any associated licenses and consents to the extent necessary for you to use the Third-Party Services. Your use of the Third-Party Services may be subject to separate terms and conditions set forth by the provider (including disclaimers or warnings), separate fees or charges, or a separate privacy notice. You are responsible for understanding and complying with any such terms or privacy notice.*
diff --git a/fern/wallets/pages/smart-contracts/choosing-a-smart-account.mdx b/fern/wallets/pages/smart-contracts/choosing-a-smart-account.mdx
new file mode 100644
index 000000000..423c82dd9
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/choosing-a-smart-account.mdx
@@ -0,0 +1,58 @@
+---
+title: Choosing a Smart Account
+description: Learn about different smart account implementations to use with
+slug: wallets/smart-contracts/choosing-a-smart-account
+---
+
+## Why Smart Accounts?
+
+Smart accounts are the key to unlocking the best online user experience ever had-- enabling features that have never been possible with traditional EOAs (externally-owned-accounts) like session keys, granular permissions, different authentication methods like email and passkeys, and much more.
+
+Beyond that, alchemy's Smart Wallets offers a simple & clean SDK to provide your users with top-tier ERC-4337 smart account features. With Smart Wallets's most powerful, most natively-supported account being Modular Account V2.
+
+MAv2 is the most fully-featured and efficient smart account out there, allowing for limitless modular expansion. All the while the account is secured by extensive [audits](https://github.com/alchemyplatform/modular-account/tree/develop/audits) as well as a [bug bounty on Cantina.](https://cantina.xyz/bounties/246de4d3-e138-4340-bdfc-fc4c95951491)
+
+MAv2 also strives to be maximally gas-efficient, being the **only** account where runtime deployment breaks the **sub-100k gas barrier**, which is cheaper than *one Uniswap swap*. For more details around MAv2, and for more sweet benchmarks, check out the [MAv2 overview!](/wallets/smart-contracts/modular-account-v2/overview)
+
+## Modular Account V2
+
+MAv2 is a zero-compromise smart account designed from the ground up for maximal security, modularity, and gas-efficiency. MAv2 abides by [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900), meaning if you have a use case, there's probably a module for it-- and if there isn't, the simple, straightforward module interface ensures building new innovative features is a walk in the park.
+
+By default, MAv2 includes a fallback validation. This allows a zero-customization setup where the account's owner (which, technically, could be another account!) has full control over the account. The fallback validation signer, or the account's owner, can be swapped or the entire validation can be disabled entirely in favor of one or more other validations.
+
+Validations are themselves modules that define an authorization scheme with granularly managed control over the account. You could use a [WebAuthn validation](https://github.com/alchemyplatform/modular-account/blob/develop/src/modules/validation/WebAuthnValidationModule.sol) to support passkeys or biometrics for example.
+
+MAv2 also supports [EIP-7702](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-7702.md) out of the box, allowing EOAs to seamlessly turn into maximally expandable smart accounts.
+
+## Other Smart Accounts
+
+While **MAv2** is the recommended choice, Smart Wallets also supports other audited and gas-optimized ERC-4337 smart account implementations:
+
+### [Light Account](/wallets/smart-contracts/other-accounts/light-account)
+
+Light Account is a minimalist ERC-4337 smart account optimized for **low gas costs**. It is based on **Ethereum Foundation's SimpleAccount** but adds key improvements like:
+
+* Ownership transfers
+* ERC-1271 signature validation
+
+While **Light Account is fully audited**, it does **not** support **advanced modular features like modules or session keys**.
+
+### [Multi-Owner Light Account](/wallets/smart-contracts/other-accounts/light-account)
+
+This account extends **Light Account** with support for **multiple owners**, allowing **shared control over an account** without needing a full modular setup.
+
+### Account features
+
+| Feature | LightAccount | MultiOwnerLightAccount | ModularAccountV1 | ModularAccountV2 |
+| ----------------- | ------------ | ---------------------- | ---------------- | ---------------- |
+| Multiple owners | ❌ | ✅ | ✅ | ✅ |
+| Session keys | ❌ | ❌ | ✅ | ✅ |
+| Multi-sig | ❌ | ❌ | ✅ | 🔜 |
+| Ecosystem modules | ❌ | ❌ | ✅ | ✅ |
+| EIP-7702 support | ❌ | ❌ | ❌ | ✅ |
+| Gas optimized | ✅ | ✅ | ❌ | ✅ |
+
+## Bring Your Own Smart Account
+
+If you have your own **ERC-4337-compatible** smart account, you can integrate it with Smart Wallets.\
+See our guide on [using a custom smart account](/wallets/third-party/smart-contracts).
diff --git a/fern/wallets/pages/smart-contracts/deployed-addresses.mdx b/fern/wallets/pages/smart-contracts/deployed-addresses.mdx
new file mode 100644
index 000000000..834548b75
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/deployed-addresses.mdx
@@ -0,0 +1,67 @@
+---
+title: Smart Contract Deployments
+description: Deployment addresses
+slug: wallets/smart-contracts/deployed-addresses
+---
+
+The following tables list the deployed factory and account implementation contract addresses for all account types. Smart Wallet contracts have the same address across all EVM chains. See the list of supported chains [here](/docs/wallets/supported-chains).
+
+## Modular Account V2
+
+| Version | Contract Name | Address |
+| ------- | ----------------------------- | -------------------------------------------- |
+| | **Implementation contracts** | |
+| v2.0.0 | SemiModularAccount7702 | `0x69007702764179f14F51cdce752f4f775d74E139` |
+| v2.0.0 | SemiModularAccountBytecode | `0x000000000000c5A9089039570Dd36455b5C07383` |
+| v2.0.0 | SemiModularAccountStorageOnly | `0x0000000000006E2f9d80CaEc0Da6500f005EB25A` |
+| v2.0.0 | ExecutionInstallDelegate | `0x0000000000008e6a39E03C7156e46b238C9E2036` |
+| v2.0.0 | ModularAccount | `0x00000000000002377B26b1EdA7b0BC371C60DD4f` |
+| | **Factory contracts** | |
+| v2.0.0 | AccountFactory | `0x00000000000017c61b5bEe81050EC8eFc9c6fecd` |
+| v2.0.1 | WebAuthnFactory | `0x55010E571dCf07e254994bfc88b9C1C8FAe31960` |
+| | **Validation modules** | |
+| v2.0.0 | SingleSignerValidationModule | `0x00000000000099DE0BF6fA90dEB851E2A2df7d83` |
+| v2.0.0 | WebAuthnValidationModule | `0x0000000000001D9d34E07D9834274dF9ae575217` |
+| | **Hook permission modules** | |
+| v2.0.1 | AllowlistModule | `0x00000000003e826473a313e600b5b9b791f5a59a` |
+| v2.0.0 | NativeTokenLimitModule | `0x00000000000001e541f0D090868FBe24b59Fbe06` |
+| v2.0.0 | PaymasterGuardModule | `0x0000000000001aA7A7F7E29abe0be06c72FD42A1` |
+| v2.0.0 | TimeRangeModule | `0x00000000000082B8e2012be914dFA4f62A0573eA` |
+
+[Source](https://github.com/alchemyplatform/modular-account/blob/develop/deployments/v2/Deployments.md)
+
+## LightAccount V2
+
+| Version | Contract Name | Address |
+| ------- | --------------------------------------- | -------------------------------------------- |
+| v2.0.0 | LightAccount (implementation) | `0x8E8e658E22B12ada97B402fF0b044D6A325013C7` |
+| v2.0.0 | LightAccountFactory | `0x0000000000400CdFef5E2714E63d8040b700BC24` |
+| v2.0.0 | MultiOwnerLightAccount (implementation) | `0xd2c27F9eE8E4355f71915ffD5568cB3433b6823D` |
+| v2.0.0 | MultiOwnerLightAccountFactory | `0x000000000019d2Ee9F2729A65AfE20bb0020AefC` |
+
+[Source](https://github.com/alchemyplatform/light-account/tree/develop/deployments)
+
+## LightAccount V1
+
+| Version | Contract Name | Address |
+| ------- | ----------------------------- | -------------------------------------------- |
+| v1.1.0 | LightAccount (implementation) | `0xae8c656ad28F2B59a196AB61815C16A0AE1c3cba` |
+| v1.1.0 | LightAccountFactory | `0x00004EC70002a32400f8ae005A26081065620D20` |
+| v1.0.2 | LightAccount (implementation) | `0x5467b1947F47d0646704EB801E075e72aeAe8113` |
+| v1.0.2 | LightAccountFactory | `0x00000055C0b4fA41dde26A74435ff03692292FBD` |
+| v1.0.1 | LightAccount (implementation) | `0xc1b2fc4197c9187853243e6e4eb5a4af8879a1c0` |
+| v1.0.1 | LightAccountFactory | `0x000000893A26168158fbeaDD9335Be5bC96592E2` |
+
+[Source](https://github.com/alchemyplatform/light-account/tree/develop/deployments)
+
+## Modular Account V1
+
+| Version | Contract Name | Address |
+| ------- | ------------------------------------------ | -------------------------------------------- |
+| v1.0.0 | UpgradeableModularAccount (implementation) | `0x0046000000000151008789797b54fdb500E2a61e` |
+| v1.0.0 | MultiOwnerModularAccountFactory | `0x000000e92D78D90000007F0082006FDA09BD5f11` |
+| | **Plugin contracts** | |
+| v1.0.0 | MultiOwnerPlugin | `0xcE0000007B008F50d762D155002600004cD6c647` |
+| v1.0.1 | SessionKeyPlugin | `0x0000003E0000a96de4058e1E02a62FaaeCf23d8d` |
+
+[Source](https://github.com/alchemyplatform/modular-account/blob/develop/deployments/v1/Deployments.md)
diff --git a/fern/wallets/pages/smart-contracts/gas-benchmarks.mdx b/fern/wallets/pages/smart-contracts/gas-benchmarks.mdx
new file mode 100644
index 000000000..43e1ce2e7
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/gas-benchmarks.mdx
@@ -0,0 +1,55 @@
+---
+title: Gas Benchmarks
+description: See gas benchmarks for multiple popular accounts and how they stack up.
+slug: wallets/smart-contracts/gas-benchmarks
+---
+
+Below is a collection of gas benchmarks resulting from the open-source [aa-benchmarks repository.](https://github.com/alchemyplatform/aa-benchmarks/blob/master/README.md) Each of these tables presents the gas costs for specific operations done via userOps-- the key feature of ERC-4337 account abstraction.
+
+Note that these benchmarks are denominated in EVM gas units. For reference, a simple Uniswap swap without smart accounts typically costs in the ballpark of `150,000` gas.
+
+## Deployment
+
+| | Gas Cost |
+| :------------------------- | --------: |
+| Alchemy Modular Account v2 | `233,004` |
+| Biconomy Nexus | `342,381` |
+| ZeroDev Kernel v3.1 | `338,419` |
+| Safe v1.4.1 | `435,486` |
+| Coinbase Smart Wallet | `317,904` |
+| Simple Account | `297,367` |
+
+## Native Transfer
+
+| | Gas Cost |
+| :------------------------- | --------: |
+| Alchemy Modular Account v2 | `158,725` |
+| Biconomy Nexus | `164,351` |
+| ZeroDev Kernel v3.1 | `190,912` |
+| Safe v1.4.1 | `176,479` |
+| Coinbase Smart Wallet | `156,812` |
+| Simple Account | `151,045` |
+
+## ERC-20 Transfer
+
+| | Gas Cost |
+| :------------------------- | --------: |
+| Alchemy Modular Account v2 | `182,665` |
+| Biconomy Nexus | `188,136` |
+| ZeroDev Kernel v3.1 | `214,817` |
+| Safe v1.4.1 | `200,732` |
+| Coinbase Smart Wallet | `181,014` |
+| Simple Account | `175,283` |
+
+## Uniswap V3 Swap
+
+| | Gas Cost |
+| :------------------------- | --------: |
+| Alchemy Modular Account v2 | `201,790` |
+| Biconomy Nexus | `207,286` |
+| ZeroDev Kernel v3.1 | `234,378` |
+| Safe v1.4.1 | `220,464` |
+| Coinbase Smart Wallet | `200,573` |
+| Simple Account | `194,829` |
+
+Take a look at more detailed benchmarks as well as $USD-denominated examples [here!](https://github.com/alchemyplatform/aa-benchmarks/blob/master/README.md)
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/getting-started.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/getting-started.mdx
new file mode 100644
index 000000000..2d00b0fdb
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/getting-started.mdx
@@ -0,0 +1,98 @@
+---
+title: Modular Account V2 • Getting started
+description: Getting started with Modular Account V2 in Smart Wallets
+slug: wallets/smart-contracts/modular-account-v2/getting-started
+---
+
+It is easy to get started with Modular Account v2! Below, you will create a new Modular Account v2 client that will be used to send user operations. Your MAv2 smart account will be deployed on-chain when you send the first User Operation from a unique signer.
+
+## Install packages
+
+**Prerequisites**
+
+* minimum Typescript version of 5
+
+**Installation**
+
+First, install the `@account-kit/smart-contracts` package.
+
+
+ ```bash yarn
+ yarn add @account-kit/smart-contracts
+ yarn add @account-kit/infra
+ ```
+
+ ```bash npm
+ npm install @account-kit/smart-contracts
+ npm install @account-kit/infra
+ ```
+
+
+
+ For Modular Account V2, the address of the smart account will be calculated as a combination of [the owner and the salt](https://github.com/alchemyplatform/modular-account/blob/v2.0.x/src/factory/AccountFactory.sol#L98-L104). You will get the same smart account address each time you supply the same `owner`, the signer(s) used to create the account for the first time. You can also optionally supply `salt` if you want a different address for the same `owner` param (the default salt is `0n`).
+
+ If you want to use a signer to connect to an account whose address does not map to the contract-generated address, you can supply the `accountAddress` to connect with the account of interest. In that case, the `signer` address is not used for address calculation, but only for signing the operation.
+
+
+## Creating a Modular Account V2 client
+
+```ts twoslash modular-account-v2.ts
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+
+const accountClient = await createModularAccountV2Client({
+ mode: "default", // optional param to specify the MAv2 variant (either "default" or "7702")
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }), // Get your API key at https://dashboard.alchemy.com/apps or http("RPC_URL") for non-alchemy infra
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+```
+
+**Choosing which mode to use**
+We currently offer two variants of Modular Account v2: `default` and `7702`.
+
+* (Recommended) `default` provides you with the cheapest, most flexible and advanced Smart Account
+* `7702` if you are looking for 7702 support, learn about how to set up and take adavantage of our EIP-7702 compliant account [here](/wallets/transactions/using-eip-7702)
+ :::
+
+Want to enable social login methods? Set up your [Alchemy Signer](/wallets/signer/quickstart).
+
+Alternatively, you can [bring a 3rd party signer](/wallets/third-party/signers/privy) as the owner of your new account.
+
+Not sure what signer to use? [Learn more](/wallets/signer/what-is-a-signer).
+
+## Sending a user operation
+
+Now that you have a client, you can send a User Operation. The first User Operation will also deploy the new Modular Account v2.
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+import { parseEther } from "viem";
+
+const accountClient = await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+
+const operation = await accountClient.sendUserOperation({
+ // simple UO sending no data or value to vitalik's address
+ uo: {
+ target: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // The address to call in the UO
+ data: "0x", // The calldata to send in the UO
+ value: parseEther("0"), // The value to send in the UO
+ },
+});
+
+console.log(
+ "User operation sent! \nUO hash: ",
+ operation.hash,
+ "\nModular Account v2 Address: ",
+ operation.request.sender,
+);
+```
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/managing-ownership.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/managing-ownership.mdx
new file mode 100644
index 000000000..55aa7d60f
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/managing-ownership.mdx
@@ -0,0 +1,41 @@
+---
+title: Managing ownership
+description: Managing ownership on your Modular Account V2
+slug: wallets/smart-contracts/modular-account-v2/managing-ownership
+---
+
+You can add an owner to your account, or transfer ownership of your account with Modular Account V2.
+
+To transfer ownership, we call the `updateFallbackSignerData` function. Modular Account V2s achieve huge savings on creation because we cache the owner address in immutable bytecode on account creation. When transferring ownership, we set the fallback signer to the new owner address and this will be used during validation. We set the boolean to false for the account to check this value in storage instead of the immutable cached owner address.
+
+Note that `updateFallbackSignerData` is an ownership transfer operation, and the previous owner would lose access to the account. To add an owner, you should [add a session key with root permissions instead](/wallets/smart-wallets/session-keys/index).
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { semiModularAccountBytecodeAbi } from "@account-kit/smart-contracts/experimental";
+import { type SmartAccountSigner, LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+import { encodeFunctionData } from "viem";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+const client = await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+
+const newOwner = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
+
+// The boolean parameter in updateFallbackSignerData is `isFallbackSignerDisabled`, and false indicates that we are using the value of the fallback signer
+await client.sendUserOperation({
+ uo: {
+ target: client.account.address,
+ value: 0n,
+ data: encodeFunctionData({
+ abi: semiModularAccountBytecodeAbi,
+ functionName: "updateFallbackSignerData",
+ args: [newOwner, false],
+ }),
+ },
+});
+```
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/overview.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/overview.mdx
new file mode 100644
index 000000000..6d416aa09
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/overview.mdx
@@ -0,0 +1,58 @@
+---
+title: Modular Account V2
+description: An overview of the Modular Account V2 smart account.
+slug: wallets/smart-contracts/modular-account-v2/overview
+---
+
+Alchemy's Modular Account V2 is the most robust and feature-rich smart account on the market with top-tier enterprise grade security. Being an ERC-6900 account, MAv2 can tap into a rich ecosystem of modules comprising different authentication methods like multisig or webauthn, as well as permissions and more!
+
+With multiple developer-years of work and two comprehensive audits from [ChainLight](https://github.com/alchemyplatform/modular-account/blob/develop/audits/2024-12-03_chainlight_14afcd8.pdf) and [Quantstamp](https://github.com/alchemyplatform/modular-account/blob/develop/audits/2024-12-11_quantstamp_14afcd8.pdf), MAv2 is the most advanced smart account available:
+
+* 40%+ reduced gas costs
+* Rich ecosystem of modules from teams like Circle
+* Super simple & clean developer experience
+
+## Cost
+
+Cost matters, so gas optimization was essential throughout the whole development process. As a result, MAv2 is one of the most optimized smart contracts ever written.
+
+As an example, see the gas comparison for deployment execution costs below:
+
+| | **Runtime gas for account creation** | **% More expensive than MAv2** |
+| ------------------------------ | ------------------------------------ | ------------------------------ |
+| **Alchemy Modular Account v2** | **97,764** | - |
+| **ZeroDev Kernel v3** | 180,465 | 84.6% |
+| **Safe (with 4337 module)** | 289,207 | 195.8% |
+
+We have a whole host of open-source benchmarks [here!](https://github.com/alchemyplatform/aa-benchmarks)
+
+At scale, these optimizations could save tens of thousands of dollars in gas fees if not more-- all without compromising on features or security.
+
+## Modularity
+
+Along with Circle, Trust Wallet, Quantstamp and the Ethereum Foundation, we've already developed multiple plug & play modules for use with ERC-6900 accounts like MAv2. Out of the box, on the validation side, MAv2 supports [session keys](/wallets/smart-contracts/modular-account-v2/session-keys), Webauthn validation (passkeys/biometrics).
+
+On the permissions front, MAv2 already has first-class support for the following:
+
+* Allowlists, ensuring a validation can only access what you want it to and nothing else.
+* ERC-20 spend limits, think [session keys with spend limits!](/wallets/smart-contracts/modular-account-v2/session-keys)
+* Native spend limits, which take gas into account.
+* Enforcing expiries, so your session keys can last as short or as long as you need them to.
+
+Note that these features are only possible thanks to the modular nature of ERC-6900, as they're all separate modules. This also means it's possible to combine any number of modules to perfectly fine-tune your permission set.
+
+Want a session key that's only valid for 24 hours, can only spend up to 0.001 ETH (incl. gas) and 100 USDC, all the while it's only able to authenticate calls to one specific address with one specific function? [Go for it!](/wallets/smart-contracts/modular-account-v2/session-keys)
+
+## Simplicity
+
+Regardless of how feature-rich MAv2 is, it's all for nothing if it's not easy to use. The account standard MAv2 adheres to has over 5,000 words-- it's understandably intimidating. However, the depth of ERC-6900 allows each feature in MAv2 to be secure and ultimately extremely simple for you, the developer.
+
+Leveraging the standard's lack of ambiguity, aa-sdk can safely create high-level zero-cost abstractions, which translate down to RPC calls exactly how you expect them to. No more fighting with your tools.
+
+The SDK is designed from the ground up to get out of your way so you can focus on what matters-- building the best smart wallet experience for your app!
+
+## EIP-7702 support
+
+EIP-7702 is an upcoming Ethereum upgrade set to launch in the coming months as part of the Pectra hardfork. Specifically, EIP-7702 enables Externally Owned Accounts (EOAs) to use smart contract account features.
+
+You can create an EIP-7702 compliant version of an MAv2 account, learn how to [here](/wallets/transactions/using-eip-7702)
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/adding-session-keys.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/adding-session-keys.mdx
new file mode 100644
index 000000000..d717ac773
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/adding-session-keys.mdx
@@ -0,0 +1,492 @@
+---
+title: Adding Session Keys to your Modular Account V2
+description: Adding Session Keys to your Modular Account V2
+slug: wallets/smart-contracts/modular-account-v2/session-keys/adding-session-keys
+---
+
+Adding a session key is simple with Smart Wallets! To add a session key, you need to 1) decide what permissions you want to grant the new key and 2) call the [`installValidation`](/wallets/smart-contracts/modular-account-v2/session-keys/adding-session-keys#install-validation-method) method on the account. This method will send a single user operation that will add the session key with scoped permission to your smart contract account on-chain. You can then use that session key to sign transactions for your account within the defined permissions!
+
+Adding scoped permissions to keys will happen via permission modules that you can pass as configuration parameters on the `installValidation` method. Permissions can be combined to fit your use case (e.g. you can limit a session key to only be able to spend 10 USDC within the next 24 hours on behalf of your account).
+
+## Adding a global session key (i.e. additional owner)
+
+This example shows:
+
+1. Adding a global session key to your Modular Account V2. This essentially gives the session key full control of your account. Functionally, this is how you can add another owner on your smart account.
+2. Adding a session key that can only call 'execute' on the account. Functionally, this allows the session key to have full control of the account other than changing the underlying account implementation.
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ semiModularAccountBytecodeAbi,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+import { toFunctionSelector, getAbiItem } from "viem";
+import { type SmartAccountSigner } from "@aa-sdk/core";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+const ecdsaValidationModuleAddress =
+ getDefaultSingleSignerValidationModuleAddress(client.chain);
+const sessionKeySigner: SmartAccountSigner =
+ LocalAccountSigner.mnemonicToAccountSigner("SESSION_KEY_MNEMONIC");
+
+// 1. Adding a session key with full permissions
+await client.installValidation({
+ validationConfig: {
+ moduleAddress: ecdsaValidationModuleAddress,
+ entityId: sessionKeyEntityId,
+ isGlobal: true,
+ isSignatureValidation: true,
+ isUserOpValidation: true,
+ },
+ selectors: [],
+ installData: SingleSignerValidationModule.encodeOnInstallData({
+ entityId: sessionKeyEntityId,
+ signer: await sessionKeySigner.getAddress(), // Address of the session key
+ }),
+ hooks: [],
+});
+
+// 2. Adding a session key that can only call `execute` or `executeBatch` on the account
+sessionKeyEntityId = 2;
+const executeSelector = toFunctionSelector(
+ getAbiItem({
+ abi: semiModularAccountBytecodeAbi,
+ name: "execute",
+ }),
+);
+
+const executeBatchSelector = toFunctionSelector(
+ getAbiItem({
+ abi: semiModularAccountBytecodeAbi,
+ name: "executeBatch",
+ }),
+);
+
+await client.installValidation({
+ validationConfig: {
+ moduleAddress: ecdsaValidationModuleAddress,
+ entityId: sessionKeyEntityId,
+ isGlobal: false,
+ isSignatureValidation: false,
+ isUserOpValidation: true,
+ },
+ selectors: [executeSelector, executeBatchSelector],
+ installData: SingleSignerValidationModule.encodeOnInstallData({
+ entityId: sessionKeyEntityId,
+ signer: await sessionKeySigner.getAddress(), // Address of the session key
+ }),
+ hooks: [],
+});
+```
+
+## Adding a session key with permissions
+
+### Time range
+
+Configuring a session key with a time range allows you to limit how long the session key is valid for (e.g. only allow this key to sign on my account for the next day). The Time Range Module is used to enforce time-based validation for User Operations (UOs) in the system. This example will show you how to add a session key that starts in a day and expires in two days.
+
+Additional Notes
+
+* the interval is inclusive i.e. `[beginningOfInterval, endOfInterval]`
+* the values `beginningOfInterval` and `endOfInterval` are unix timestamps with a maximum size of uint32
+* the timestamp specifying the end of the interval must be **strictly greater than** the beginning of the interval
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ HookType,
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ getDefaultTimeRangeModuleAddress,
+ TimeRangeModule,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+import { type SmartAccountSigner } from "@aa-sdk/core";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+const ecdsaValidationModuleAddress =
+ getDefaultSingleSignerValidationModuleAddress(client.chain);
+const sessionKeySigner: SmartAccountSigner =
+ LocalAccountSigner.mnemonicToAccountSigner("SESSION_KEY_MNEMONIC");
+
+const hookEntityId = 0; // Make sure that the account does not have a hook with this entity id on the module yet
+const validAfter = 0; // valid once added
+const validUntil = validAfter + 2 * 86400; // validity ends 2 days from now
+
+// Adding a session key that starts in a day and expires in two days
+await client.installValidation({
+ validationConfig: {
+ moduleAddress: ecdsaValidationModuleAddress,
+ entityId: sessionKeyEntityId,
+ isGlobal: true,
+ isSignatureValidation: true,
+ isUserOpValidation: true,
+ },
+ selectors: [],
+ installData: SingleSignerValidationModule.encodeOnInstallData({
+ entityId: sessionKeyEntityId,
+ signer: await sessionKeySigner.getAddress(), // Address of the session key
+ }),
+ hooks: [
+ {
+ hookConfig: {
+ address: getDefaultTimeRangeModuleAddress(client.chain),
+ entityId: hookEntityId,
+ hookType: HookType.VALIDATION, // fixed value
+ hasPreHooks: true, // fixed value
+ hasPostHooks: false, // fixed value
+ },
+ initData: TimeRangeModule.encodeOnInstallData({
+ entityId: hookEntityId,
+ validAfter,
+ validUntil,
+ }),
+ },
+ ],
+});
+```
+
+### Paymaster guard
+
+#### Purpose
+
+This module provides the ability to limit a session key to only be able to use a specific single paymaster.
+
+#### Additional Notes
+
+* you MUST specify a paymaster when using this module
+* if the paymaster that is registered with this module decides to no longer sponsor your user operations, the entity associated with this hook would no longer be able to send user operations.
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ HookType,
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ getDefaultPaymasterGuardModuleAddress,
+ PaymasterGuardModule,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+import { type SmartAccountSigner } from "@aa-sdk/core";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+const ecdsaValidationModuleAddress =
+ getDefaultSingleSignerValidationModuleAddress(client.chain);
+const sessionKeySigner: SmartAccountSigner =
+ LocalAccountSigner.mnemonicToAccountSigner("SESSION_KEY_MNEMONIC");
+
+const hookEntityId = 0; // Make sure that the account does not have a hook with this entity id on the module yet
+const paymasterAddress = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
+
+// Adding a session key that can only use the above paymaster for user operations
+await client.installValidation({
+ validationConfig: {
+ moduleAddress: ecdsaValidationModuleAddress,
+ entityId: sessionKeyEntityId,
+ isGlobal: true,
+ isSignatureValidation: true,
+ isUserOpValidation: true,
+ },
+ selectors: [],
+ installData: SingleSignerValidationModule.encodeOnInstallData({
+ entityId: sessionKeyEntityId,
+ signer: await sessionKeySigner.getAddress(), // Address of the session key
+ }),
+ hooks: [
+ {
+ hookConfig: {
+ address: getDefaultPaymasterGuardModuleAddress(client.chain),
+ entityId: hookEntityId,
+ hookType: HookType.VALIDATION, // fixed value
+ hasPreHooks: true, // fixed value
+ hasPostHooks: false, // fixed value
+ },
+ initData: PaymasterGuardModule.encodeOnInstallData({
+ entityId: hookEntityId,
+ paymaster: paymasterAddress,
+ }),
+ },
+ ],
+});
+```
+
+### Native token and/or gas limit
+
+This module provides native token spending limits for modular accounts. Below we will show an example of adding a session key that has a 1 eth native token spend limit. Functionally, this module is enabled by:
+
+* Tracking and limiting total native token spending across transactions
+* Monitoring both direct transfers and gas costs from UserOperations
+* Supporting special paymaster configurations for complex gas payment scenarios
+
+#### Token Limit Features
+
+* Tracks native token spending across:
+ * Direct transfers via `execute`
+ * Batch transfers via `executeBatch`
+ * Contract creation via `performCreate`
+ * UserOperation gas costs (when applicable)
+* Supports special paymaster configurations for:
+ * Standard paymasters (gas costs don't count against limit)
+ * Special paymasters (gas costs do count against limit)
+* Maintains separate limits per entity ID
+
+#### Gas Cost Tracking
+
+For UserOperations, the module tracks:
+
+* Pre-verification gas
+* Verification gas
+* Call gas
+* Paymaster verification gas (for special paymasters)
+* Paymaster post-op gas (for special paymasters)
+
+#### Additional Notes
+
+* The module must be installed with both validation and execution hooks, the validation hook track gas, whereas the execution hook tracks value
+* The module maintains a global singleton state for all accounts
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ HookType,
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ getDefaultNativeTokenLimitModuleAddress,
+ NativeTokenLimitModule,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+import { parseEther } from "viem";
+import { type SmartAccountSigner } from "@aa-sdk/core";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+const ecdsaValidationModuleAddress =
+ getDefaultSingleSignerValidationModuleAddress(client.chain);
+const sessionKeySigner: SmartAccountSigner =
+ LocalAccountSigner.mnemonicToAccountSigner("SESSION_KEY_MNEMONIC");
+
+const hookEntityId = 0; // Make sure that the account does not have a hook with this entity id on the module yet
+
+// Adding a session key that has a 1 eth native token spend limit
+await client.installValidation({
+ validationConfig: {
+ moduleAddress: ecdsaValidationModuleAddress,
+ entityId: sessionKeyEntityId,
+ isGlobal: true,
+ isSignatureValidation: true,
+ isUserOpValidation: true,
+ },
+ selectors: [],
+ installData: SingleSignerValidationModule.encodeOnInstallData({
+ entityId: sessionKeyEntityId,
+ signer: await sessionKeySigner.getAddress(), // Address of the session key
+ }),
+ hooks: [
+ {
+ hookConfig: {
+ address: getDefaultNativeTokenLimitModuleAddress(client.chain),
+ entityId: hookEntityId,
+ hookType: HookType.VALIDATION, // fixed value
+ hasPreHooks: true, // fixed value
+ hasPostHooks: false, // fixed value
+ },
+ initData: NativeTokenLimitModule.encodeOnInstallData({
+ entityId: hookEntityId,
+ spendLimit: parseEther("1"),
+ }),
+ },
+ {
+ hookConfig: {
+ address: getDefaultNativeTokenLimitModuleAddress(client.chain),
+ entityId: hookEntityId,
+ hookType: HookType.EXECUTION, // fixed value
+ hasPreHooks: true, // fixed value
+ hasPostHooks: false, // fixed value
+ },
+ initData: "0x", // no initdata required as the limit was set up in the above installation call
+ },
+ ],
+});
+```
+
+### Allowlist or an ERC20 token limit
+
+This module provides two key security features for modular accounts:
+
+* **Allowlisting** - Controls which addresses and functions can be called
+* **ERC-20 Spend Limits** - Manages spending limits for ERC-20 tokens
+
+#### Allowlist Features
+
+* Can specify permissions for:
+ * Specific addresses + specific functions
+ * Specific addresses + all functions (wildcard)
+ * All addresses + specific functions (wildcard)
+* Only applies to execute and executeBatch functions
+* Permission checks follow this order:
+ * If wildcard address → Allow
+ * If wildcard function → Allow
+ * If specific address + specific function match → Allow
+ * Otherwise → Revert
+
+#### ERC-20 Spend Limit Features
+
+* Only allows transfer and approve functions for tracked tokens
+* Works with standard execution functions:
+ * execute
+ * executeWithRuntimeValidation
+ * executeUserOp
+ * executeBatch
+
+#### Additional Notes
+
+* Module must be installed/uninstalled on an entity ID basis
+* Uninstalling for one entity ID doesn't affect other entities
+* Settings are stored in a global singleton contract
+* All permissions and limits can be updated dynamically
+* The module is intentionally restrictive about which ERC-20 functions are allowed to prevent edge cases (e.g., DAI's non-standard functions)
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ HookType,
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ getDefaultAllowlistModuleAddress,
+ AllowlistModule,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+import { parseEther } from "viem";
+import { type SmartAccountSigner } from "@aa-sdk/core";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+const ecdsaValidationModuleAddress =
+ getDefaultSingleSignerValidationModuleAddress(client.chain);
+const sessionKeySigner: SmartAccountSigner =
+ LocalAccountSigner.mnemonicToAccountSigner("SESSION_KEY_MNEMONIC");
+
+const hookEntityId = 0; // Make sure that the account does not have a hook with this entity id on the module yet
+const allowlistInstallData = AllowlistModule.encodeOnInstallData({
+ entityId: hookEntityId,
+ inputs: [
+ {
+ target: "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
+ hasSelectorAllowlist: false, // whether to limit the callable functions on call targets
+ hasERC20SpendLimit: false, // If "target" is an ERC20 token with a spend limit
+ erc20SpendLimit: parseEther("100"), // The spend limit to set, if relevant
+ selectors: [], // The function selectors to allow, if relevant
+ },
+ ],
+});
+
+// Adding a session key that has a 100 ERC token spend limit
+await client.installValidation({
+ validationConfig: {
+ moduleAddress: ecdsaValidationModuleAddress,
+ entityId: sessionKeyEntityId,
+ isGlobal: true,
+ isSignatureValidation: true,
+ isUserOpValidation: true,
+ },
+ selectors: [],
+ installData: SingleSignerValidationModule.encodeOnInstallData({
+ entityId: sessionKeyEntityId,
+ signer: await sessionKeySigner.getAddress(), // Address of the session key
+ }),
+ hooks: [
+ {
+ hookConfig: {
+ address: getDefaultAllowlistModuleAddress(client.chain),
+ entityId: hookEntityId,
+ hookType: HookType.VALIDATION, // fixed value
+ hasPreHooks: true, // fixed value
+ hasPostHooks: false, // fixed value
+ },
+ initData: allowlistInstallData,
+ },
+ ],
+});
+```
+
+## Install validation method
+
+This method is used to add session keys to your account, with the following configurable parameters.
+
+`validationConfig`: The validation configuration for the session key, containing the following fields:
+
+* `validationModule`: This is the address of the validation module to use for this key. SingleSignerValidationModule provides ECDSA validation and WebauthnModule provides WebAuthn validation. If you wish to use a custom validation module such as a multisig validation, this would be specified here.
+* `entityId`: This is a uint32 identifier for validation chosen by the developer. The only rule here is that you cannot pick an entityId that already is used on the account. Since the owner's entityId is 0, you can start from 1.
+* `isGlobal`: This is a boolean that specifies if the validation can be used to call any function on the account. If this is set to false, the validation can only be used to call functions that are specified in the `selectors` array. It's recommended to leave this as `false` and use the selector array instead, as a key with global permissions has the authority to upgrade the account to any other implementation, which can change the ownership of the account in the same transaction.
+* `isSignatureValidation`: This is a boolean that specifies if the validation can be used for ERC-1271 signature validation, which can be used for signing permit2 token permits. It's recommended to leave this as `false` for security reasons.
+* `isUserOpValidation`: This is a boolean that specifies if the key can perform user operations on behalf of the account. For most use cases, this should be set to `true`.
+
+`selectors`: This is an array of function selectors that the key can call. If `isGlobal` is set to `true`, the limits in this array will not be applied. If `isGlobal` is set to `false`, the key can only call functions that are specified in this array. It's recommended to only have `ModularAccount.execute.selector` and `ModularAccount.executeBatch.selector` in this array.
+
+`installData`: This is the installation data that is passed to the validation module on installation. Each module has their own encoding for this data, so you would need to use the helper functions provided by that module.
+
+`hooks`: This is an array of hooks to be installed on the key. Each element in the array contains a hookConfig object as well as initData to pass to the hook module.
+
+`hookConfig`: This is the hook configuration to be applied to the session key.
+
+* `address`: This is the address of the hook module to be installed on the session key.
+* `entityId`: This is a hook module entity id that is different from the validation entity id. The decoupling enables multiple hooks provided by the same module to be applied on the same key. The only restriction here is that the hook module entity id should not be an entity id that's currently in use for the account.
+* `hookType`: This specifies which phase should the hook be applied on, either HookType.VALIDATION to be a validation hook, or HookType.EXECUTION to be a pre and/or post-execution hook.
+* `hasPreHooks`: This specifies if the hook is supposed to run before validation or execution.
+* `hasPostHooks`: This specifies if the hook is supposed to run after validation or execution.
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/index.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/index.mdx
new file mode 100644
index 000000000..12d05844d
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/index.mdx
@@ -0,0 +1,55 @@
+---
+title: What Are Session Keys?
+description: Learn what session keys are, why they exist, and how they enable secure delegation, automation, and streamlined UX in smart accounts.
+slug: wallets/smart-contracts/modular-account-v2/session-keys
+---
+
+Session Keys allow you to add multiple signers to smart wallet with scoped permissions that are validated onchain. This unlocks:
+
+* Multiple owners for wallets
+* Automated tasks like claiming rewards or recurring payments
+* Stronger security by limiting exposure of the main account key
+
+They're ideal for reducing UX friction while maintaining onchain guarantees.
+
+### Skip duplicate confirmations
+
+With session keys, users don't need to approve every dapp interaction using their main wallet. Instead, a dapp can use a session key to perform follow-up actions — fast and frictionless — all within a defined scope.
+
+**Example**: Enabling auto-approval for repeated steps like claiming testnet tokens or submitting votes to reduce user friction of having to sign many transactions.
+
+Session keys unlock a simplified authentication process by allowing users to interact with apps without needing to confirm each action using their primary key. Instead, users create a session key with permissions specific to the app, then the app can use that key for future actions from the apps server or client. This speeds up the user interaction and provides a smoother experience and allows apps to have secure server side wallet control.
+
+### Automate actions
+
+Use session keys to run workflows without needing user input each time. Automate:
+
+* Claim flows
+* Token approvals
+* Recurring transactions
+
+By granting only the necessary permissions, session keys ensure automation stays secure.
+
+Users and apps can automate actions within predefined limits using specific on-chain permissions. Session keys can be used to streamline processes like recurring payments, contract interactions, or any activity that benefits from automation.
+
+### Securely delegate access
+
+Session keys reduce exposure of the main private key by creating temporary or restricted alternatives. Even if compromised, session keys limit damage because they're restricted to only the permissions you defined, not your entire account.
+
+Session keys reduce risk by limiting access:
+
+* They're scoped to specific actions
+* Can expire after a set time
+* Can be revoked independently of the main account key
+
+By delegating authorization to a separate key, the exposure of the main private key is minimized.
+
+### Build with granular permissions
+
+Session keys are safe because they're built on a flexible permission system. You control exactly what a session key can do, when it can act, and how much it can spend.
+
+Modular Account V2 includes a growing library of permission types — from spending limits and time windows to contract allowlists and function restrictions. You can also build custom permission modules for specialized use cases.
+
+This granular control lets you create session keys that are perfectly scoped for each use case, whether that's a one-time NFT mint, recurring DeFi interactions, or complex multi-step workflows.
+
+> Want to build your own permission system? Let us know — Modular Account V2 is designed to plug in custom modules easily.
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/removing-session-keys.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/removing-session-keys.mdx
new file mode 100644
index 000000000..1d30a6e99
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/removing-session-keys.mdx
@@ -0,0 +1,112 @@
+---
+title: Removing Session Keys
+description: Removing Session Keys from your Modular Account V2
+slug: wallets/smart-contracts/modular-account-v2/session-keys/removing-session-keys
+---
+
+Removing session keys is done with the `uninstallValidation` method.
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ modularAccountAbi,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+
+// Removing a basic session key
+await client.uninstallValidation({
+ moduleAddress: getDefaultSingleSignerValidationModuleAddress(client.chain),
+ entityId: sessionKeyEntityId,
+ uninstallData: SingleSignerValidationModule.encodeOnUninstallData({
+ entityId: sessionKeyEntityId,
+ }),
+ hookUninstallDatas: [],
+});
+
+// Removing a session key with hooks
+await client.uninstallValidation({
+ moduleAddress: getDefaultSingleSignerValidationModuleAddress(client.chain),
+ entityId: sessionKeyEntityId,
+ uninstallData: SingleSignerValidationModule.encodeOnUninstallData({
+ entityId: sessionKeyEntityId,
+ }),
+ hookUninstallDatas: [],
+});
+```
+
+If there are hooks on the validation, you have to provide the hook uninstallation data to uninstall them too. Each module provides an `encodeOnUninstallData` helper function to generate the uninstallation hook for that module.
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import {
+ installValidationActions,
+ getDefaultSingleSignerValidationModuleAddress,
+ SingleSignerValidationModule,
+ modularAccountAbi,
+ AllowlistModule,
+} from "@account-kit/smart-contracts/experimental";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+
+const client = (
+ await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ })
+).extend(installValidationActions);
+
+let sessionKeyEntityId = 1;
+
+// Removing a basic session key
+await client.uninstallValidation({
+ moduleAddress: getDefaultSingleSignerValidationModuleAddress(client.chain),
+ entityId: sessionKeyEntityId,
+ uninstallData: SingleSignerValidationModule.encodeOnUninstallData({
+ entityId: sessionKeyEntityId,
+ }),
+ hookUninstallDatas: [],
+});
+
+const hookEntityId = 1;
+const target = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";
+
+// Removing a session key with an allowlist hook
+await client.uninstallValidation({
+ moduleAddress: getDefaultSingleSignerValidationModuleAddress(client.chain),
+ entityId: sessionKeyEntityId,
+ uninstallData: SingleSignerValidationModule.encodeOnUninstallData({
+ entityId: sessionKeyEntityId,
+ }),
+ hookUninstallDatas: [
+ AllowlistModule.encodeOnUninstallData({
+ entityId: hookEntityId,
+ inputs: [
+ {
+ target,
+ hasSelectorAllowlist: false,
+ hasERC20SpendLimit: false,
+ erc20SpendLimit: 0n,
+ selectors: [],
+ },
+ ],
+ }),
+ ],
+});
+```
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/session-key-permissions.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/session-key-permissions.mdx
new file mode 100644
index 000000000..77dceca66
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/session-key-permissions.mdx
@@ -0,0 +1,47 @@
+---
+title: Supported Permissions for Session Keys
+description: Explore the supported permission types you can apply to session keys to safely delegate access in smart accounts.
+slug: wallets/smart-contracts/modular-account-v2/session-key-permissions
+---
+
+## What Permissions Can You Apply to Session Keys?
+
+Session keys work best when they are tightly scoped. Our smart wallets, specifically Modular Account V2, gives you a flexible set of built-in permission types. These permissions are validated onchain removing any dependency on an offchain engine and increasing security of users' wallets.
+
+Use these to limit what a key can do, for how long, where it can call, and how much it can spend.
+
+### Time Range
+
+Supports limiting keys with a start and/or expiry time ranges
+
+### ERC-20 Spending Limits
+
+Supports limiting how much of a specific ERC-20 token a key may spend
+
+### **Gas and native token spending limits**
+
+Supports limiting how much native tokens, e.g. ETH on mainnet, a key may spend
+
+### Access Control Lists
+
+Supports limiting function selectors and/or external contracts that a key may interact with.
+
+### Internal Selector Allowlist
+
+Supports limiting what functions a key may call on the Modular Account (e.g. installing or uninstalling modules, or upgrading the account)
+
+> You can combine multiple permissions on a single session key. These can be layered to match the minimum required access for your use case.
+
+## Composing Permissions: Fine-Grained Control
+
+Permissions are composable. You can apply multiple permissions to a single session key to tightly define its behavior.
+
+Example: You could create a session key that:
+
+* Only works between June 1–June 15
+* Can call just one staking contract
+* Spend no more than 100 USDC
+
+Use Case: In Alchemy's modular wallet [demo](https://demo.alchemy.com/), one key is scoped to auto-stake a user's funds once a day, but only within a capped budget and only on a verified contract.
+
+By combining permissions, you can build tailored, safe delegation schemes — perfect for dapps, relayers, and complex workflows.
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/using-session-keys.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/using-session-keys.mdx
new file mode 100644
index 000000000..f04198d4d
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/session-keys/using-session-keys.mdx
@@ -0,0 +1,48 @@
+---
+title: Using Session Keys
+description: Using Session Keys with your Modular Account V2
+slug: wallets/smart-contracts/modular-account-v2/session-keys/using-session-keys
+---
+
+Once session keys are added, using them is straightforward - just create another client instance with the session key connected along with properties of the session key (session key entityId and global validation is used). These properties were set during the installValidation call.
+
+```ts twoslash
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+import { type SmartAccountSigner } from "@aa-sdk/core";
+import { parseEther } from "viem";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+const client = await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+
+let sessionKeyEntityId = 1;
+const sessionKeySigner: SmartAccountSigner =
+ LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey());
+
+const sessionKeyClient = await createModularAccountV2Client({
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }),
+ signer: sessionKeySigner,
+ accountAddress: client.getAddress(client.account),
+ initCode: await client.account.getInitCode(),
+ signerEntity: {
+ entityId: sessionKeyEntityId,
+ isGlobalValidation: true,
+ },
+});
+
+await sessionKeyClient.sendUserOperation({
+ uo: {
+ target: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // The address to call in the UO
+ data: "0x", // The calldata to send in the UO
+ value: parseEther("1"), // The value to send in the UO
+ },
+});
+```
+
+Note that you have to pass in `accountAddress` and `initCode` to session key clients. By default, the client uses an account address counterfactual that assumes that the connected signer is the owner.
diff --git a/fern/wallets/pages/smart-contracts/modular-account-v2/upgrading-to-MAv2.mdx b/fern/wallets/pages/smart-contracts/modular-account-v2/upgrading-to-MAv2.mdx
new file mode 100644
index 000000000..9f91561f8
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/modular-account-v2/upgrading-to-MAv2.mdx
@@ -0,0 +1,155 @@
+---
+slug: wallets/smart-contracts/modular-account-v2/upgrading-to-MAv2
+---
+
+## **0. Construct your clients**
+
+First we need to check what kind of account address you have! The way `client.account.address` is derived depends on the type of account and the salt. We'll have to handle different cases depending on whether your client's
+address is a light account address or a modular account address. So we construct two clients:
+
+Our **Light Account** client:
+
+```ts
+import { createLightAccountClient } from "@account-kit/smart-contracts";
+const lightAccountClient = await createLightAccountClient({
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ signer: yourSigner
+```
+
+Our **Modular Account** client:
+
+```ts
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+const modularAccountV2Client = await createModularAccountV2Client({
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ signer: yourSigner,
+});
+```
+
+## **1. Check the implementation of your account (using `client.account.getImplementationAddress()`):**
+
+* **LightAccount** deployment addresses [here](https://github.com/alchemyplatform/light-account/tree/v2.0.0/deployments)
+* **ModularAccount** deployment addresses [here](https://github.com/alchemyplatform/modular-account/tree/develop/deployments)
+
+Depending on the result, take different actions based on the state of the account.
+
+For your **Modular Account** client:
+
+```ts
+import { client } from "docs/shared/infra/client";
+const impl = await client.account.getImplementationAddress();
+
+// implementation slot exists and is a deployed modular account. this user has a modular account at the modular account counterfactual address. no work needs to be done.
+if (impl == "ModularAccountImpl") {
+ // done
+}
+```
+
+For your **Light Account** client:
+
+```ts
+import { client } from "docs/shared/infra/client";
+const impl = await client.account.getImplementationAddress();
+
+// implementation slot exists and is a deployed modular account. this user has a modular account at the light account address. no need to upgrade.
+if (impl == "ModularAccountImpl") {
+ // done
+}
+
+// implementation slot exists and is a deployed light account. this user has a light account at the light account address. we need to initiate an upgrade for the user.
+else if (impl == "LightAccountImpl") {
+ // go to step A
+}
+
+// implementation slot is empty
+else if (impl == NullAddress) {
+ // account is not deployed and has assets at the counterfactual
+ const balance = await client.getBalance({ address: client.account.address });
+ if (balance) {
+ // go to step A
+ }
+ // account does not exist, new user!
+ else {
+ // go to step B
+ }
+}
+```
+
+## **2. Upgrade Account!**
+
+### **Case A: Implementation Slot Exists and is a LightAccount**
+
+If the implementation slot exists and points to **LightAccount**, it means the account is a legacy **LightAccount.** Follow this path if the account has been deployed or if the account hasn’t been deployed but has assets at the counterfactual.
+
+Steps:
+
+1. Retrieve the upgrade data `upgradeToData` and method `createMAV2Account` to create the MAv2 account using `getMAV2UpgradeToData` .
+2. Call `upgradeAccount` to prepare the upgrade data for MAv2 with the initializer set to give it the same owner as the LA.
+3. Call `createSmartAccountClient` to create the MAv2 client from your newly upgraded account
+
+```ts
+const { createMAV2Account, ...upgradeToData } = await getMAV2UpgradeToData(
+ lightAccountclient,
+ { account: lightAccountClient.account },
+);
+
+await lightAccountClient.upgradeAccount({
+ upgradeTo: upgradeToData,
+ waitForTx: true,
+});
+
+const maV2Client = createSmartAccountClient({
+ client: createBundlerClient({
+ chain: yourchain,
+ transport: yourTransport,
+ }),
+ account: await createMAV2Account(),
+});
+```
+
+### **Case B: Implementation Slot is Empty (No Account or Account Not Deployed With No Assets)**
+
+If the implementation slot is empty, this means that the account doesn’t exist. Or, if the account exists but has no assets, we still want to follow this path.
+
+* Construct a **MAv2** client with the signer to handle both cases:
+
+1. **New user with no account**.
+2. **New user with an existing but un-deployed account**.
+
+```ts twoslash modular-account-v2.ts
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { generatePrivateKey } from "viem/accounts";
+
+const maV2Client = await createModularAccountV2Client({
+ mode: "default", // optional param to specify the MAv2 variant (either "default" or "7702")
+ chain: sepolia,
+ transport: alchemy({ apiKey: "your-api-key" }), // Get your API key at https://dashboard.alchemy.com/apps or http("RPC_URL") for non-alchemy infra
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+```
+
+This ensures that operations are executed on the already upgraded **MAv2** account.
+
+***
+
+### **3. Send a User Operation!**
+
+Send a user operation to deploy your upgraded account!
+
+```tsx
+const result = await maV2Client.sendUserOperation({
+ uo: {
+ target: target,
+ value: sendAmount,
+ data: "0x",
+ },
+});
+```
+
+And now you’ve upgraded your account to MAV2!
+
+You can use a [Modular Account v2 client](/wallets/smart-contracts/modular-account-v2/getting-started#creating-a-modular-account-v2-client) to send user operations or use the [React packages](/wallets/authentication/overview#usage) directly to simplify integration - this hook defaults to MAv2. **Make sure to pass in an `accountAddress` param to the client to connect the client to the existing account.**
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/light-account/getting-started.mdx b/fern/wallets/pages/smart-contracts/other-accounts/light-account/getting-started.mdx
new file mode 100644
index 000000000..5d59e6dcf
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/light-account/getting-started.mdx
@@ -0,0 +1,65 @@
+---
+title: Light Account • Getting started
+description: Getting started with Light Account in Smart Wallets
+slug: wallets/smart-contracts/other-accounts/light-account/getting-started
+---
+
+It is easy to get started with Light Account! We will show you how to create and send user operations for both `LightAccount` and `MultiOwnerLightAccount` using `@alchemy/aa-alchemy`.
+
+### Install packages
+
+**Prerequisites**
+
+* minimum Typescript version of 5
+
+**Installation**
+
+
+ ```bash npm
+ npm i @account-kit/smart-contracts
+ ```
+
+ ```bash yarn
+ yarn add@account-kit/smart-contracts
+ ```
+
+
+### Create a client and send a user operation
+
+The code snippets below demonstrate how to use `LightAccount` and `MultiOwnerLightAccount` with Smart Wallets. They create the account and send a `UserOperation` from it.
+
+
+ ```ts light-account.ts
+ import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+ import { sepolia, alchemy } from "@account-kit/infra";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { generatePrivateKey } from "viem";
+
+ const lightAccountClient = await createLightAccountAlchemyClient({
+ transport: alchemy({ apiKey: "your-api-key" })
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ });
+ ```
+
+ ```ts multi-owner-light-account.ts
+ import { createMultiOwnerLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+ import { sepolia, alchemy } from "@account-kit/infra";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { generatePrivateKey } from "viem";
+
+ const lightAccountClient = await createMultiOwnerLightAccountAlchemyClient({
+ transport: alchemy({ apiKey: "your-api-key" })
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ });
+ ```
+
+
+
+ For `LightAccount`, the address of the smart account will be calculated as a combination of the version, [the owner, and the salt](https://github.com/alchemyplatform/light-account/blob/v2.0.0/src/LightAccountFactory.sol#L24-L33). You will get the same smart account address each time you supply the same `version` and `owner`. Alternatively, you can supply `salt` if you want a different address for the same `version` and `owner` params (the default salt is `0n`). For `MultiOwnerLightAccount`, the same pattern follows, except that it takes an array of owner addresses instead of a single owner address.
+
+ If you want to use a signer to connect to an account whose address does not map to the contract-generated address, you can supply the `accountAddress` to connect with the account of interest. In that case, the `signer` address is not used for address calculation, but only used for signing the operation.
+
+ Reference: https://eips.ethereum.org/EIPS/eip-4337#first-time-account-creation
+
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/light-account/index.mdx b/fern/wallets/pages/smart-contracts/other-accounts/light-account/index.mdx
new file mode 100644
index 000000000..5f48704a6
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/light-account/index.mdx
@@ -0,0 +1,33 @@
+---
+title: Light Account
+description: What is Light Account?
+slug: wallets/smart-contracts/other-accounts/light-account
+---
+
+## Overview
+
+Light Account is a collection of lightweight [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) smart accounts. We started with the Ethereum Foundation's canonical [SimpleAccount](https://github.com/eth-infinitism/account-abstraction/blob/cc3893bcaf2272c163ce89d5eb9eadb8e6b52db7/contracts/accounts/SimpleAccount.sol#L22) and added key improvements. It is fully production-ready [multiple](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-01-09_quantstamp_aa8196b.pdf) [audits](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-04-26_quantstamp_93f46a2.pdf), gas optimizations, and [ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) signature support. Additionally, Light Account supports ownership transfers to ensure you and your users don't get locked into a particular signer.
+
+## Light Account variants
+
+Light Account has two variants catered to particular use cases. Both variants inherit the characteristics and features listed above.
+
+### `LightAccount`
+
+This is the default variant of Light Account that supports a single ECDSA or SCA owner. It is slightly more gas efficient than `MultiOwnerLightAccount`, and can be useful when you want to maximally optimize for gas spend or ensure that only one signer has access to the account at any given time.
+
+`LightAccount` comes in versions v1.1.0 and v2.0.0, which make use of the v0.6 and v0.7 entry points respectively.
+
+For backwards compatibility, `LightAccount` defaults to version v2.0.0. However, once a version is chosen and the Light Account is created, the version must remain consistent in order for the Light Account client to work with the existing Light Account.
+
+### `MultiOwnerLightAccount`
+
+Multi-Owner Light Account is a variant of Light Account that supports multiple ECDSA or SCA owners at once rather than a single one. Each owner has full control over the account, including the ability to add or remove other owners. This lets your account integrate with multiple signers at once, and supports recovering your account if one signer is lost.
+
+Multi-Owner Light Account uses v0.7 of the entry point.
+
+## Developer links
+
+* [Light Account deployment addresses](/wallets/smart-contracts/deployed-addresses)
+* [Light Account GitHub repo](https://github.com/alchemyplatform/light-account)
+* [Quantstamp audit report](https://github.com/alchemyplatform/light-account/blob/develop/audits/2024-04-26_quantstamp_93f46a2.pdf)
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/light-account/multi-owner-light-account.mdx b/fern/wallets/pages/smart-contracts/other-accounts/light-account/multi-owner-light-account.mdx
new file mode 100644
index 000000000..3940142ec
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/light-account/multi-owner-light-account.mdx
@@ -0,0 +1,61 @@
+---
+title: How to manage ownership of a Multi-Owner Light Account
+description: Follow this guide to manage ownership of a Multi-Owner Light
+slug: wallets/smart-contracts/other-accounts/light-account/multi-owner-light-account
+---
+
+A `MultiOwnerLightAccount` has one or more ECDSA or SCA owners. This lets your account integrate with multiple signers at once, and supports recovering your account if one signer is lost.
+
+The `MultiOwnerLightAccount` is able to:
+
+* Update (add or remove) owners for an account.
+* Show all owners of an account.
+* Validate signed signatures of ERC-4337 enabled user operations as well as regular transactions.
+
+When you connect your `MultiOwnerLightAccount` to `SmartAccountClient` you can extend the client with `multiOwnerLightAccountClientActions`, which exposes a set of methods available to call the `MultiOwnerLightAccount` with the client connected to the account.
+
+
+ When using `createMultiOwnerLightAccountAlchemyClient` in
+ `@account-kit/smart-contracts`, the `SmartAccountClient` comes automatically
+ extended with `multiOwnerLightAccountClientActions` as defaults available for
+ use.
+
+
+### 1. Get all current owners of a `MultiOwnerLightAccount`
+
+You can use the `getOwnerAddresses` method on the `MultiOwnerLightAccount` object, which can be accessed from a connected client.
+
+
+ ```ts example.ts
+ import { multiOwnerLightAccountClient } from "./client";
+
+ const owners = await multiOwnerLightAccountClient.account.getOwnerAddresses();
+ ```
+
+
+
+
+### 2. Add or remove owners for a `MultiOwnerLightAccount`
+
+You can use the `updateOwners` method on the `multiOwnerLightAccountClientActions` extended smart account client to add or remove owners from the `MultiOwnerLightAccount`.
+
+
+ ```ts example.ts
+ import { multiOwnerLightAccountClient } from "./client";
+
+ const ownersToAdd = []; // the addresses of owners to be added
+ const ownersToRemove = []; // the addresses of owners to be removed
+
+ const opHash = await multiOwnerLightAccountClient.updateOwners({
+ ownersToAdd,
+ ownersToRemove,
+ });
+
+ const txHash =
+ await multiOwnerLightAccountClient.waitForUserOperationTransaction({
+ hash: opHash,
+ });
+ ```
+
+
+
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/light-account/transfer-ownership-light-account.mdx b/fern/wallets/pages/smart-contracts/other-accounts/light-account/transfer-ownership-light-account.mdx
new file mode 100644
index 000000000..92c385cc5
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/light-account/transfer-ownership-light-account.mdx
@@ -0,0 +1,79 @@
+---
+title: How to transfer ownership of a Light Account
+description: Follow this guide to transfer ownership of a Light Account with
+slug: wallets/smart-contracts/other-accounts/light-account/transfer-ownership-light-account
+---
+
+Not all smart account implementations support transferring the ownership (e.g. `SimpleAccount`). However, a number of the accounts in this guide and in Smart Wallets do, including our `LightAccount`! Let's see a few different ways we can transfer ownership of an Account (using `LightAccount` as an example).
+
+## Usage
+
+`LightAccount` exposes the following method which allows the existing owner to transfer ownership to a new owner address:
+
+```solidity
+function transferOwnership(address newOwner) public virtual onlyOwner
+```
+
+There a number of ways you can call this method using Smart Wallets.
+
+### 1. Using `transferOwnership` client action
+
+
+
+```ts example.ts
+import { lightAccountClient } from "./client";
+import { createLightAccountClient } from "@account-kit/smart-contracts";
+
+// this will return the signer of the smart account you want to transfer ownerhip to
+const newOwner = LocalAccountSigner.mnemonicToAccountSigner(NEW_OWNER_MNEMONIC);
+const accountAddress = lightAccountClient.getAddress();
+
+// [!code focus:99]
+const hash = lightAccountClient.transferOwnership({
+ newOwner,
+ waitForTxn: true,
+});
+
+// after transaction is mined on the network,
+// create a new light account client for the transferred Light Account
+const transferredClient = await createLightAccountClient({
+ transport: custom(smartAccountClient),
+ chain: smartAccountClient.chain,
+ signer: newOwner,
+ accountAddress, // NOTE: you MUST specify the original smart account address to connect using the new owner/signer
+ version: "v2.0.0", // NOTE: if the version of the light account is not v2.0.0, it must be specified here
+});
+```
+
+
+
+
+
+Since `@alchemy/aa-accounts` exports a `LightAccount` ABI, the above approach makes it easy to transfer ownership. That said, you can also directly call `sendUserOperation` to execute the ownership transfer. As you will see below, however, it is a bit verbose:
+
+### 2. Using `sendUserOperation`
+
+
+
+```ts example.ts
+import { encodeFunctionData } from "viem";
+import { lightAccountClient } from "./client";
+
+// this will return the address of the smart account you want to transfer ownerhip of
+const accountAddress = lightAccountClient.getAddress();
+const newOwner = "0x..."; // the address of the new owner
+
+// [!code focus:99]
+const result = await lightAccountClient.sendUserOperation({
+ to: accountAddress,
+ data: lightAccountClient.encodeTransferOwnership(newOwner),
+});
+// wait for txn with UO to be mined
+await lightAccountClient.waitForUserOperationTransaction(result);
+```
+
+
+
+
+
+See the [`LightAccount`](/wallets/smart-contracts/other-accounts/light-account/) docs for more details about our `LightAccount implementation.
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/getting-started.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/getting-started.mdx
new file mode 100644
index 000000000..a67b1efdd
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/getting-started.mdx
@@ -0,0 +1,78 @@
+---
+title: Modular Account • Getting started
+description: Getting started with Modular Account in Smart Wallets
+slug: wallets/smart-contracts/other-accounts/modular-account/getting-started
+---
+
+It is easy to get started with Modular Account! We will show you two different ways using Alchemy Infra or 3rd party infra.
+
+## Install packages
+
+**Prerequisites**
+
+* minimum Typescript version of 5
+
+**Installation**
+
+First, install the `@account-kit/smart-contracts` package.
+
+
+ ```bash yarn
+ yarn add @account-kit/smart-contracts
+ yarn add @account-kit/infra
+ ```
+
+ ```bash npm
+ npm install @account-kit/smart-contracts
+ npm install @account-kit/infra
+ ```
+
+
+## With Alchemy Infra
+
+Then you can do the following:
+
+```ts twoslash
+import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+
+const alchemyAccountClient = await createModularAccountAlchemyClient({
+ transport: alchemy({ apiKey: "your-api-key" }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+```
+
+
+ For Modular Account, the address of the smart account will be calculated as a combination of [the owners and the salt](https://github.com/alchemyplatform/modular-account/blob/v1.0.x/src/factory/MultiOwnerModularAccountFactory.sol#L79-L82). You will get the same smart account address each time you supply the same `owners`, the signer(s) used to create the account for the first time. You can also optionally supply `salt` if you want a different address for the same `owners` param (the default salt is `0n`).
+
+ If you want to use a signer to connect to an account whose address does not map to the contract-generated address, you can supply the `accountAddress` to connect with the account of interest. In that case, the `signer` address is not used for address calculation, but only for signing the operation.
+
+
+## With 3rd-party infra
+
+If you're using a 3rd-party for infra, we also expose a client that you can use to interact with Modular Account using other RPC providers.
+
+```ts twoslash
+import { createMultiOwnerModularAccountClient } from "@account-kit/smart-contracts";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { sepolia } from "viem/chains";
+import { http } from "viem";
+import { generatePrivateKey } from "viem/accounts";
+
+const accountClient = await createMultiOwnerModularAccountClient({
+ chain: sepolia,
+ transport: http("RPC_URL"),
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+```
+
+Next, if you want to use a different `signer` with a smart account signer, check out [choosing a signer](/wallets/signer/what-is-a-signer). Otherwise, if you are ready to get on-chain, go to [send user operations](/wallets/transactions/send/send-user-operations).
+
+## Installing plugins
+
+Modular Accounts follow the [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900) standard for extensibility using plugins. Plugins can add new validation logic, execution capabilities, and hooks to your account, and can be installed or uninstalled via the account's Plugin Manager.
+
+To learn how to add and remove plugins (for example, installing the Session Key Plugin), see the guide on [Installing & uninstalling plugins](/wallets/transactions/send/batch-user-operations).
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/index.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/index.mdx
new file mode 100644
index 000000000..5cd090053
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/index.mdx
@@ -0,0 +1,50 @@
+---
+title: Modular Account Smart Contract
+description: Follow this guide to use Modular Accounts with Smart Wallets
+slug: wallets/smart-contracts/other-accounts/modular-account
+---
+
+## Overview
+
+Modular Account is an ERC-4337 smart account that supports customizable features with ERC-6900 plugins. It is fully production-ready with multiple security audits, three prebuilt plugins in `MultiOwnerPlugin`, `SessionKeyPlugin`, and `MultisigPlugin`, and the capability to support any custom account behavior you need.
+
+## Why Modular Account?
+
+### Make the most of Account Abstraction
+
+Smart accounts unlock lots of customizable ways to improve the wallet experience. Still, it requires writing this behavior into the smart contract for the account, which is difficult and security-critical. Modular Account uses the [ERC-6900](https://eips.ethereum.org/EIPS/eip-6900) framework to simplify creating powerful features for smart accounts. We have created three plugins to level up your smart accounts, and we look forward to what new plugins you create!
+
+### Multi Owner Plugin
+
+The Multi Owner plugin lets your smart accounts have one or more ECDSA or SCA owners. This allows your account to integrate with multiple signers simultaneously and supports recovering your account if one is lost.
+
+### Session Key Plugin
+
+The Session Key plugin lets your smart account add additional signers with specific permissions to your account.
+Session keys can be customized and configured to:
+
+* Contract Restrictions: restrict to only interact with specific contracts and/or a subset of their methods
+* Spending Limits: spend up to a set amount of ERC-20 tokens or native token amount
+* Time Period: expire after specific time periods
+
+Session keys let you streamline interactions by reducing confirmation steps or automating actions on behalf of the account. These features are kept secure through the permission system, which protects the account from malicious use of the session key.
+
+### Multisig Plugin
+
+The Multisig plugin allows your account to have multiple ECDSA or SCA signers and require multiple signatures to perform actions on the account. This is commonly referred to as a k-of-n signature scheme and would create Modular Accounts that are similar to [Gnosis Safe](https://safe.global/) accounts. This plugin is recommended for accounts that require maximum security.
+
+### Full compatibility
+
+Modular Account also supports the same baseline set of account abstraction features as Light Account: sponsoring gas, batching transactions, rotating owners, and checking ERC-1271 signatures.
+
+### Build your own Plugin
+
+Have an idea for more account features? Modular Account supports ERC-6900 for installing and uninstalling additional plugins to your account, letting you fully customize the account logic.
+
+Check out the plugin development guide [here](https://www.notion.so/alchemotion/How-to-write-an-ERC-6900-Plugin-8ef518630b1a43a1b301723925407ec5?utm_content=8ef51863-0b1a-43a1-b301-723925407ec5\&utm_campaign=T06RY9YKG\&n=slack\&n=slack_link_unfurl\&pvs=6) if you’re interested!
+
+### Secure, audited, open source
+
+Modular Account has been audited by Spearbit and Quanstamp. You can find the audit reports [here](https://github.com/alchemyplatform/modular-account/tree/develop/audits). Modular Account is fully open source, so you can validate the [source code](https://github.com/alchemyplatform/modular-account).
+
+
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-ownership-mav1.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-ownership-mav1.mdx
new file mode 100644
index 000000000..5ed671ca0
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-ownership-mav1.mdx
@@ -0,0 +1,95 @@
+---
+title: How to manage ownership of a Modular Account
+description: Follow this guide to manage ownership of a Modular Account with
+slug: wallets/smart-contracts/other-accounts/modular-account/manage-ownership-mav1
+---
+
+The Multi Owner plugin lets your smart accounts have one or more ECDSA or SCA owners. This lets your account integrate with multiple signers at once, and supports recovering your account if one signer is lost.
+
+The Multi-Owner Plugin is able to:
+
+* Update (add or remove) owners for an MSCA.
+* Check if an address is an owner address of an MSCA.
+* Show all owners of an MSCA.
+* Validate signed signatures of ERC-4337 enabled user operations as well as regular transactions.
+
+All Modular Accounts have `MultiOwnerPlugin` pre-installed upon creation, exposing following methods for account owners to update (add or remove) and read the current owners of the account:
+
+```solidity
+/// @notice Update owners of the account. Owners can update owners.
+/// @param ownersToAdd The address array of owners to be added.
+/// @param ownersToRemove The address array of owners to be removed.
+function updateOwners(address[] memory ownersToAdd, address[] memory ownersToRemove) external;
+
+/// @notice Get the owners of `account`.
+/// @param account The account to get the owners of.
+/// @return The addresses of the owners of the account.
+function ownersOf(address account) external view returns (address[] memory);
+```
+
+When you connect your Modular Account to `SmartAccountClient` you can extend the client with `multiOwnerPluginActions`, which exposes a set of methods available to call the installed `MultiOwnerPlugin` with the client connected to the account.
+
+### 1. Check if an address is one of the current owners of a Modular Account
+
+You should first extend the `SmartAccountClient` connected to a Modular Account, which already comes with `MultiOwnerPlugin` installed upon creation, with client to `multiOwnerPluginActions` for the client to include the `MultiOwnerPlugin` actions.
+
+
+ When using `createModularAccountAlchemyClient` in
+ `@account-kit/smart-contracts`, the `SmartAccountClient` comes automatically
+ extended with `multiOwnerPluginActions`, `pluginManagerActions`, and
+ `accountLoupeActions` decorators as defaults available for use.
+
+
+Then, you can use the `readOwners` method of the `multiOwnerPluginActions` extended smart account client to check if a given address is one of the current owners of a Modular Account.
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client";
+
+ const ownerToCheck = "0x..."; // the address of the account to check the ownership of
+
+ // returns a boolean whether an address is an owner of account or not
+ const isOwner = await modularAccountClient.isOwnerOf({
+ address: ownerToCheck,
+ });
+ ```
+
+
+
+
+### 2. Get all current owners of a Modular Account
+
+You can use the `readOwners` method on the `multiOwnerPluginActions` extended smart account client to fetch all current owners of the connected Modular Account.
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client";
+
+ // owners is an array of the addresses of the account owners
+ const owners = await modularAccountClient.readOwners();
+ ```
+
+
+
+
+### 3. Add or remove owners for a Modular Account
+
+You can use the `updateOwners` method on the `multiOwnerPluginActions` extended smart account client to add or remove owners from the Modular Account.
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client";
+
+ const ownersToAdd = []; // the addresses of owners to be added
+ const ownersToRemove = []; // the addresses of owners to be removed
+
+ const result = await modularAccountClient.updateOwners({
+ args: [ownersToAdd, ownersToRemove],
+ });
+
+ const txHash =
+ await modularAccountClient.waitForUserOperationTransaction(result);
+ ```
+
+
+
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-plugins/get-installed-plugins.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-plugins/get-installed-plugins.mdx
new file mode 100644
index 000000000..f2ec9ccc1
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-plugins/get-installed-plugins.mdx
@@ -0,0 +1,63 @@
+---
+title: Extending Smart Accounts • Get installed plugins of a Modular Account
+description: Follow this guide to get installed plugins of a Modular Account
+slug: wallets/smart-contracts/other-accounts/modular-account/manage-plugins/get-installed-plugins
+---
+
+[ERC-6900](https://eips.ethereum.org/EIPS/eip-6900) Modular Accounts implements Plugin inspection interface [`IAccountLoupe.sol`](https://eips.ethereum.org/EIPS/eip-6900#iaccountloupesol) to support visibility in plugin configuration on-chain. This contract interface defines the method `getInstalledPlugins()` that clients can use to fetch the currently installed plugins on a Modular Account.
+
+```solidity
+/// @notice Get an array of all installed plugins.
+/// @return The addresses of all installed plugins.
+function getInstalledPlugins() external view returns (address[] memory);
+```
+
+Smart Wallets provides a streamlined experience of interacting with Modular Account AccoutLoupe interface easily by providing `accountLoupeActions` defined in `@account-kit/smart-contracts` package. When you connect your Modular Account to `SmartAccountClient` you can extend the client with `accountLoupeActions`, which exposes a set of methods available to call the account `AccountLoupe` with the client connected to the account.
+
+## Get installed plugins of a Modular Account
+
+You should first extend the `SmartAcountClient` connected to a Modular Account, which has `AccountLoupe` implemented, with `accountLoupeActions` for the client to include the `AccountLoupe` actions.
+
+Then, you can use the `getInstalledPlugins` method of the `accountLoupeActions` extended smart account client to get the list of installed plugin addresses for the connected Modular Account.
+
+
+ When using `createModularAccountAlchemyClient` in
+ `@account-kit/smart-contracts`, the `SmartAccountClient` comes automatically
+ extended with `multiOwnerPluginActions`, `pluginManagerActions`, and
+ `accountLoupeActions` decorators as defaults available for use.
+
+
+
+
+```ts example.ts
+import { modularAccountClient } from "./client";
+import { IPluginAbi } from "@account-kit/smart-contracts";
+
+// returns addresses of all installed plugins
+const installedPlugins = await modularAccountClient.getInstalledPlugins({});
+
+if (installedPlugins.length === 0) {
+ console.log("account has no plugins installed.");
+ return;
+}
+
+const pluginAddress = installedPlugins[0];
+// read plugin metadata of a plugin
+const metadata = await modularAccountClient.readContract({
+ address: pluginAddress,
+ abi: IPluginAbi,
+ functionName: "pluginMetadata",
+});
+
+console.log(JSON.stringify(metadata, null, 2));
+// {
+// name: 'MultiOwnerPlugin',
+// version: '1.0.0',
+// }
+```
+
+
+
+
+
+By checking if a certain plugin address exists in the list of installed plugin addresses of a Modular Account, you can check whether a particular plugin is installed or not on a Modular Account.
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-plugins/install-plugins.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-plugins/install-plugins.mdx
new file mode 100644
index 000000000..2360b4926
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/manage-plugins/install-plugins.mdx
@@ -0,0 +1,108 @@
+---
+title: Extending Smart Accounts • Installing & uninstalling plugins on a Modular
+description: Follow this guide to install and uninstall plugins on a Modular
+slug: wallets/smart-contracts/other-accounts/modular-account/manage-plugins/install-plugins
+---
+
+[ERC-6900](https://eips.ethereum.org/EIPS/eip-6900) Modular Accounts implements Plugin manager interface [`IPluginManager.sol`](https://eips.ethereum.org/EIPS/eip-6900#ipluginmanagersol) to support installing and uninstalling plugins on a Modular Account. This contract interface defines the method `installPlugin()` and `uninstallPlugin()` that clients can use to install or uninstall plugins on a Modular Account.
+
+Smart Wallets provides a streamlined experience of interacting with the Modular Account AccoutLoupe interface easily by providing `pluginManagerActions` defined in `@account-kit/smart-contracts` package. When you connect your Modular Account to `SmartAccountClient` you can extend the client with `pluginManagerActions`, which exposes a set of methods available to call the account `AccountLoupe` with the client connected to the account.
+
+There are two ways to install plugins. The first method is to use the `pluginManagerActions`'s generic `installPlugin` method, but this method requires the [`PluginGenConfig`](https://github.com/alchemyplatform/aa-sdk/blob/a9a11ec23b1084fa43edaa3cb933ff36318ca573/packages/accounts/plugindefs/types.ts) configure the correct plugin dependencies and function references for the plugin.
+
+Smart Wallets provides a more robust, easier way to install plugins with `pluginActions`. Each plugin comes with the its own `pluginActions` that includes already configured install method, named `install`, for installing any plugin of interest. For example, `MultiOwnerPlugin` has `multiOwnerPluginActions` that includes `installMultiOwnerPlugin()` method, and `SessionKeyPlugin` has `sessionKeyPluginActions` that includes `installSessionKeyPlugin()` method, all exported from the `@account-kit/smart-contracts` package.
+
+This guide will use the `SessionKeyPlugin` as an example to show how you can install `SessionKeyPlugin` easily using the `SmartAccountClient` extended with `sessionKeyPluginActions`.
+
+
+**Please use caution when uninstalling plug-ins to avoid "bricking" your account!**
+
+If the account only has 1 plug-in installed and you uninstall it, there will no longer be a validator on the account (no owner) and you will not be able to use or recover it.
+
+For example, do not call uninstall plugin of the multi-owner plugin on a Modular Account in a single action.
+
+
+
+## 1. Installing the Session Key Plugin
+
+You should first extend the `SmartAccountClient` connected to a Modular Account with `sessionKeyPluginActions`.
+
+Then, you can use the `installSessionKeyPlugin()` method exposed on `sessionKeyPluginActions` extended smart account client to install the session key plugin for the connected account.
+
+
+ When using `createModularAccountAlchemyClient` in
+ `@account-kit/smart-contracts`, the `SmartAccountClient` comes automatically
+ extended with `multiOwnerPluginActions`, `pluginManagerActions`, and
+ `accountLoupeActions` decorators as defaults available for use.
+
+
+
+
+```ts example.ts
+import { modularAccountClient } from "./client";
+import { sessionKeyPluginActions } from "@account-kit/smart-contracts";
+
+// [!code focus:99]
+// extend smart account client with sessionKeyPluginActions to call SessionKeyPlugin methods
+const sessionKeyExtendedClient = modularAccountClient.extend(
+ sessionKeyPluginActions,
+);
+
+const { hash } = await sessionKeyExtendedClient.installSessionKeyPlugin({
+ // 1st arg is the initial set of session keys
+ // 2nd arg is the tags for the session keys
+ // 3rd arg is the initial set of permissions
+ args: [[], [], []],
+});
+
+await client.waitForUserOperationTransaction({ hash });
+```
+
+
+
+
+
+## 2. Uninstalling the Session Key Plugin
+
+On the other hand, uninstalling plugins usually does not involve configuring contract dependencies or function references. You can use the `pluginManagerActions`'s generic `uninstallPlugin` method to uninstall a particular plugin of interest.
+First, extend the `SmartAccountClient` connected to a Modular Account with `pluginManagerActions`.
+
+Then, you can use the `uninstallPlugin()` method exposed on `pluginManagerActions` extended smart account client to uninstall the session key plugin for the connected account.
+
+
+**Please use caution when uninstalling plug-ins to avoid "bricking" your account!**
+
+If the account only has 1 plug-in installed and you uninstall it, there will no longer be a validator on the account (no owner) and you will not be able to use or recover it.
+
+For example, do not call uninstall plugin of the multi-owner plugin on a Modular Account in a single action.
+
+
+
+
+
+```ts example.ts
+import { chain, modularAccountClient } from "./client";
+import { SessionKeyPlugin } from "@account-kit/smart-contracts";
+
+const { hash } = await modularAccountClient.uninstallPlugin({
+ pluginAddress: SessionKeyPlugin.meta.addresses[chain.id],
+});
+
+await modularAccountClient.waitForUserOperationTransaction({ hash });
+```
+
+
+
+
+
+### Extend Modular Account with Multisig
+
+As mentioned above, you should not uninstall the multiowner plugin from a Modular Account in a single action. This will leave your account without a validator, no owner, and therefore will be unusable.
+
+**Recommended flow today**: Rather than switch ownership post creation, use `createModularAccountAlchemyClient` OR the `createMultisigAccountAlchemyClient` on account creation directly depending on if you want multi-owner or multi-sig ownership respectively.
+
+**Problem**: In order to switch from multi-owner (installed in Modular Account by default) to multi-sig, you will need to batch call an uninstall of the multi-owner plugin and an install of the multi-sig plugin. **It is required to batch these steps!** If you uninstall the multi-owner plugin in a single action, the account will no longer have a validation function, and will be bricked (unusable).
+
+**Solution for extending Modular Account AFTER deployment**: You will need to manually encode 1) the uninstall of the multi-owner plugin and 2) the install of the multi-sig plugin and batch those together in one UO (using `encodeFunctionData` and [batching](/wallets/transactions/send/send-user-operations#batch-user-operations) using `sendUserOperation`). We are working to make this workflow easier in the next SDK version.
+
+See a working example in this [discussion](https://github.com/alchemyplatform/aa-sdk/discussions/865#discussioncomment-10206160).
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/details.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/details.mdx
new file mode 100644
index 000000000..1b9b8cfb6
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/details.mdx
@@ -0,0 +1,25 @@
+---
+title: Multisig Plugin Smart Contract Technical Details
+description: What are the technical details for the Multisig Plugin?
+slug: wallets/smart-contracts/other-accounts/modular-account/multisig-plugin/details
+---
+
+## Storing Signatures
+
+In the usage guide, for a threshold of `k` signatures, we obtain the first signature with `proposeUserOperation` and the next `k-2` signatures with `signMultisigUserOperation`. In practice, since signers would likely use different clients and sign at different times, the client or dapp will need to store the first `k-1` signatures. This would be loaded and combined with the final signature to be used with `sendUserOperation`.
+
+## Gas Estimation
+
+Gas estimations must be performed for user operations before the first signature is obtained. This is done in aa-sdk with built in gas and fee estimation middlewares.
+
+We perform gas estimation assuming that the `k` signatures to be obtained are EOAs, and not other smart accounts. In the case that smart accounts own a modular account, the validation step could require more gas. If used with default gas estimation, it would cause the user operation to fail with an insufficient gas error.
+
+In these cases, clients are expected to implement their own gas estimations for the signature validation step, and use gas overrides when first calling `proposeUserOperation`.
+
+## Variable Gas Feature
+
+For normal User Operations, gas prices and values have to be decided before the first signer's signature (and the paymaster's signature, if used). However, since it might take time to gather the `k` signatures, it is likely that when the `k`th signature is collected, the network gas values would have changed significantly. In this case, the account would be overpaying on gas, or the User Operation would be underpriced, and the `k` signatures have to be collected again on a new User Operation.
+
+This multisig plugin includes a variable gas feature to address this problem. The fee values selected and signed over by the first `k-1` signers are treated as a "maximum fee" and the `k`th signer is able to choose final fee values to use based on the current network conditions. With this feature, there is no longer a risk of overpaying, or having to re-collect the `k` signatures.
+
+However, note that the use of this feature would likely not work with regular paymaster services, including Alchemy's Gas Manager product.
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/getting-started.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/getting-started.mdx
new file mode 100644
index 000000000..08e322cac
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/getting-started.mdx
@@ -0,0 +1,140 @@
+---
+title: Modular Account with Multisig Plugin • Getting started
+description: Getting started with the Modular Account with Multisig Plugin in Smart Wallets
+slug: wallets/smart-contracts/other-accounts/modular-account/multisig-plugin/getting-started
+---
+
+## 1. Create a Multisig Account Client
+
+Initialize a Multisig Modular Account client and set the `n` accounts as signers.
+
+
+It is recommended to use the `createMultisigAccountAlchemyClient` directly to create new accounts with multi-sig ownership, rather than extending the Modular Account client.
+
+If you have an existing Modular Account (which has multi-owner plugin by default), please see details [here](#TODO/smart-contracts/extending-smart-accounts/install-plugins#extend-modular-account-with-multisig) for installing the plugin before proceeding.
+
+
+
+
+
+## 3. Propose a User Operation
+
+To propose a new user operation for review by the multisig signers, you will use the `proposeUserOperation` method. This estimates gas, constructs the user operation struct, and if `gasManagerConfig` is used then it attempts to use a paymaster. Lastly, a signature is generated with the pre-provided signer.
+
+
+
+```ts example.ts
+import { multisigAccountClient } from "./client";
+import { createMultisigAccountAlchemyClient } from "@account-kit/smart-contracts";
+
+const {
+ request,
+ aggregatedSignature,
+ signatureObj: firstSig,
+} = await multisigAccountClient.proposeUserOperation({
+ uo: {
+ target: targetAddress,
+ data: "0x",
+ },
+});
+```
+
+
+
+
+
+## 4. Get the threshold signatures
+
+Next, you have to collect the next k-2 signatures, excluding the first signature which you already provided and the last signature which we'll deal with in step 5 when we send the user operation. Each member of the multisig can sign with the `signMultisigUserOperation` method.
+
+
+
+```ts example.ts
+import { createMultisigAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { signers, owners, threshold } from "./client";
+
+const multisigAccountClient = await createMultisigAccountAlchemyClient({
+ chain,
+ signer: signers[1], // using the second signer
+ owners,
+ threshold,
+ apiKey: "YOUR_API_KEY",
+});
+
+const { aggregatedSignature, signatureObj: secondSig } =
+ await multisigAccountClient.signMultisigUserOperation({
+ account: multisigAccountClient.account,
+ // output from step 1, and from this step if k-2 > 1
+ signatures: [previousAggregatedSignature],
+ userOperationRequest: request,
+ });
+```
+
+
+
+
+
+## 5. Send the User Operation
+
+After collecting k-1 signatures, you're ready to collect the last signature and send the user operation. This is done with the `sendUserOperation` method. `sendUserOperation` also formats this aggregated signature, sorting its parts in ascending order by owner address as expected by the Multisig Plugin smart contract.
+
+
+
+```ts example.ts
+import { createMultisigAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { signers, owners, threshold } from "./client";
+
+const multisigAccountClient = await createMultisigAccountAlchemyClient({
+ chain,
+ // using the last signer
+ signer: signers[2],
+ owners,
+ threshold,
+ apiKey: "YOUR_API_KEY",
+});
+
+const result = await multisigAccountClient.sendUserOperation({
+ uo: request.callData,
+ context: {
+ aggregatedSignature,
+ signatures: [firstSig, secondSig],
+ userOpSignatureType: "ACTUAL",
+ },
+});
+```
+
+
+
+
+
+By default, we use the variable gas feature in the Multisig Plugin smart contract. For this, we need `userOpSignatureType` to be set to "ACTUAL". If you do not wish to use this feature, gas overrides should be passed in `sendUserOperation`, and `userOpSignatureType` should be set to "UPPERLIMIT".
+
+
+
+```ts example.ts
+import { multisigAccountClient } from "./client";
+
+const result = await multisigAccountClient.sendUserOperation({
+ uo: request.callData,
+ overrides: {
+ callGasLimit: request.callGasLimit,
+ verificationGasLimit: request.verificationGasLimit,
+ preVerificationGas: request.preVerificationGas,
+ maxFeePerGas: request.maxFeePerGas,
+ maxPriorityFeePerGas: request.maxPriorityFeePerGas,
+ },
+ context: {
+ aggregatedSignature,
+ signatures: [firstSig, secondSig],
+ userOpSignatureType: "UPPERLIMIT",
+ },
+});
+```
+
+
+
+
+
+## Conclusion
+
+That's it! You've initialized a modular account with three multisig members, proposed a user operation, collected the necessary signatures, and sent the user operation to the bundler.
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/index.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/index.mdx
new file mode 100644
index 000000000..071e12247
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/multisig-plugin/index.mdx
@@ -0,0 +1,17 @@
+---
+title: Multisig Plugin Smart Contract
+description: Follow this guide to use the Multisig Plugin on Modular Account
+slug: wallets/smart-contracts/other-accounts/modular-account/multisig-plugin
+---
+
+## Overview
+
+The Multisig plugin adds `k of n` threshold signature scheme to your Modular Account, similar to [Gnosis Safe](https://safe.global/) accounts. This is a powerful security layer that is recommended for accounts that require additional security and redundancy beyond a single signer.
+
+The `n` signers can be any combination of ECDSA or SCA signers. A threshold of `k` signatures will be required to execute any user operation on the smart account.
+
+## Secure, audited, open source
+
+The Multisig Plugin has been audited by Quantstamp. You can find the audit reports [here](https://github.com/alchemyplatform/multisig-plugin/blob/develop/audits).
+
+It is also fully open source so you can validate the [source code](https://github.com/alchemyplatform/multisig-plugin).
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/session-keys/getting-started.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/session-keys/getting-started.mdx
new file mode 100644
index 000000000..74b727f37
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/session-keys/getting-started.mdx
@@ -0,0 +1,251 @@
+---
+title: Getting started with Session Keys
+description: Learn how to use Alchemy's Session Key Plugin.
+slug: wallets/smart-contracts/other-accounts/modular-account/session-keys/getting-started
+---
+
+`@account-kit/smart-contracts` exports all of the definitions you need to use session keys with a Modular Account. We provide a simple `SessionKeySigner` class that generates session keys on the client and can be used as the `signer` for the Multi Owner Modular Account.
+We also export the necessary decorators which can be used to extend your `modularAccountClient` to make interacting with session keys easy.
+
+## Usage
+
+Let's take a look at a full example that demonstrates how to use session keys with a Modular Account.
+
+
+
+## Breaking it down
+
+### Determine where the session key is stored
+
+Session keys can be held on the client side or on a backend agent. Client side session keys are useful for skipping confirmations, and agent side keys are useful for automations.
+
+In the above example, we use a client-side key using the `SessionKeySigner` exported from `@account-kit/smart-contracts`.
+
+```ts twoslash
+import { SessionKeySigner } from "@account-kit/smart-contracts";
+
+const sessionKeySigner = new SessionKeySigner();
+```
+
+If you are using backend agent controlled session keys, then the agent should generate the private key and send only the address to the client. This protects the private key by not exposing it to the user.
+
+### Extend your client with Modular Account Decorators
+
+The base `modularAccountClient` and `AlchemymodularAccountClient`, only include base functionality for sending user operations. If you are using a `ModularAccount`, then you will want to extend your client with the various decorators exported by `@account-kit/smart-contracts`.
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client";
+ import { sessionKeyPluginActions } from "@account-kit/smart-contracts";
+
+ const extendedClient = modularAccountClient.extend(sessionKeyPluginActions);
+ ```
+
+
+
+
+### Check if the Session Key Plugin is installed
+
+Before you can start using session keys, you need to check whether the user’s account has the session key plugin installed. You can perform this check using the account loupe decorator, which lets you inspect the state of installed plugins on a Modular Account.
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client.js";
+ import {
+ accountLoupeActions,
+ multiOwnerPluginActions,
+ sessionKeyPluginActions,
+ pluginManagerActions,
+ } from "@account-kit/smart-contracts";
+
+ const extendedClient = modularAccountClient.extend(sessionKeyPluginActions);
+
+ //---cut---
+
+ import { SessionKeyPlugin } from "@account-kit/smart-contracts";
+
+ // 1. check if the plugin is installed
+ const isPluginInstalled = await modularAccountClient
+ .getInstalledPlugins({})
+ // This checks using the default address for the chain, but you can always pass in your own plugin address here as an override
+ .then((x) => x.includes(SessionKeyPlugin.meta.addresses[chain.id]));
+ ```
+
+
+
+
+### Install the Session Key Plugin
+
+If the Session Key Plugin is not yet installed, you need to install it before it can be used. To simplify the workflow, it is also possible to batch the plugin installation along with creating session keys and performing other actions, which combines all of these steps into one user operation.
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client";
+ import {
+ accountLoupeActions,
+ multiOwnerPluginActions,
+ sessionKeyPluginActions,
+ pluginManagerActions,
+ } from "@account-kit/smart-contracts";
+
+ const extendedClient = modularAccountClient.extend(sessionKeyPluginActions);
+
+ import { SessionKeyPlugin } from "@account-kit/smart-contracts";
+
+ // 1. check if the plugin is installed
+ const isPluginInstalled = await extendedClient
+ .getInstalledPlugins({})
+ // This checks using the default address for the chain, but you can always pass in your own plugin address here as an override
+ .then((x) => x.includes(SessionKeyPlugin.meta.addresses[chain.id]));
+
+ //---cut---
+ import { SessionKeyPermissionsBuilder } from "@account-kit/smart-contracts";
+
+ // 2. if the plugin is not installed, then install it and set up the session key
+ if (!isPluginInstalled) {
+ // lets create an initial permission set for the session key giving it an eth spend limit
+ // if we don't set anything here, then the key will have 0 permissions
+ const initialPermissions =
+ new SessionKeyPermissionsBuilder().setNativeTokenSpendLimit({
+ spendLimit: 1000000n,
+ });
+
+ const { hash } = await extendedClient.installSessionKeyPlugin({
+ // 1st arg is the initial set of session keys
+ // 2nd arg is the tags for the session keys
+ // 3rd arg is the initial set of permissions
+ args: [
+ [await sessionKeySigner.getAddress()],
+ [zeroHash],
+ [initialPermissions.encode()],
+ ],
+ });
+
+ await extendedClient.waitForUserOperationTransaction({ hash });
+ }
+ ```
+
+
+
+
+### Construct the initial set of permissions
+
+Session keys are powerful because of permissions that limit what actions they can take. When you add a session key, you should also specify the initial permissions that apply over the key.
+
+Let's use the permission builder to build a set of permissions that sets a spend limit:
+
+
+ ```ts example.ts
+ import { modularAccountClient } from "./client";
+ import {
+ accountLoupeActions,
+ multiOwnerPluginActions,
+ sessionKeyPluginActions,
+ pluginManagerActions,
+ } from "@account-kit/smart-contracts";
+
+ const extendedClient = modularAccountClient.extend(sessionKeyPluginActions);
+
+ // ---cut---
+ import { SessionKeyPermissionsBuilder } from "@account-kit/smart-contracts";
+
+ const initialPermissions =
+ new SessionKeyPermissionsBuilder().setNativeTokenSpendLimit({
+ spendLimit: 1000000n,
+ });
+
+ const result = await extendedClient.updateKeyPermissions({
+ args: [sessionKeyAddress, initialPermissions.encode()],
+ });
+ ```
+
+
+
+
+## Managing Session Keys
+
+The Session Key Plugin allows you to:
+
+* Add session keys, and set the key's initial permissions.
+* Remove session keys.
+* Update key permissions.
+* Rotate session keys. This action replaces the previous session key with a new session key, while keeping the existing permissions.
+
+### Add a Session Key
+
+Session keys can be added either during installation, or using the `addSessionKey` function.
+
+
+ ```ts add-session-key.ts
+ import { SessionKeyPermissionsBuilder } from "@account-kit/smart-contracts";
+ import { keccak256 } from "viem";
+ import { client } from "./base-client";
+
+ const result = await client.addSessionKey({
+ key: "0xSessionKeyAddress",
+ // tag is an identifier for the emitted SessionKeyAdded event
+ tag: keccak256(new TextEncoder().encode("session-key-tag")),
+ permissions: new SessionKeyPermissionsBuilder().encode(),
+ });
+ ```
+
+
+
+
+### Remove a Session Key
+
+Session keys can be removed using the `removeSessionKey` function.
+
+
+ ```ts remove-session-key.ts
+ import { client } from "./base-client";
+
+ const result = await client.removeSessionKey({
+ key: "0xSessionKeyAddress",
+ });
+ ```
+
+
+
+
+### Update a Key's permissions
+
+Session key permissions can be edited after creation using the `updateKeyPermissions` function. Note that you should configure initial permissions when the key is added, and not rely on a second user operation to set the permissions.
+
+
+ ```ts update-session-key.ts
+ import { SessionKeyPermissionsBuilder } from "@account-kit/smart-contracts";
+ import { client } from "./base-client";
+
+ const result = await client.updateSessionKeyPermissions({
+ key: "0xSessionKeyAddress",
+ // add other permissions to the builder
+ permissions: new SessionKeyPermissionsBuilder()
+ .setTimeRange({
+ validFrom: Math.round(Date.now() / 1000),
+ // valid for 1 hour
+ validUntil: Math.round(Date.now() / 1000 + 60 * 60),
+ })
+ .encode(),
+ });
+ ```
+
+
+
+
+### Rotate a Session Key
+
+If the key is no longer available, but there exists a tag identifying a previous session key configured for your application, you may instead choose to rotate the previous key’s permissions. This can be performed using `rotateKey` .
+
+
+ ```ts rotate-session-key.ts
+ import { client } from "./base-client.js";
+
+ const result = await client.rotateSessionKey({
+ oldKey: "0xOldKey",
+ newKey: "0xNewKey",
+ });
+ ```
+
+
+
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/session-keys/index.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/session-keys/index.mdx
new file mode 100644
index 000000000..ab29002fb
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/session-keys/index.mdx
@@ -0,0 +1,49 @@
+---
+title: Introduction to using Session Keys
+description: Learn about Alchemy's ERC-6900 Compatible Session Key Plugin.
+slug: wallets/smart-contracts/other-accounts/modular-account/session-keys
+---
+
+The Session Key plugin lets your smart account add additional signers to your Modular Account with specific permissions.
+
+## Why Session Keys?
+
+### Skip duplicate confirmations
+
+Session keys unlock a simplified authentication process by allowing users to interact with apps without needing to confirm each action using their primary key. Instead, users create a session key with permissions specific to the app, then the app can use that key for future actions. This speeds up the user interaction and provides a smoother experience.
+
+### Automate actions
+
+With the ability to delegate specific permissions to session keys, users can automate actions within predefined limits. Session keys can be used to streamline processes like recurring payments, contract interactions, or any activity that benefits from automation.
+
+### Enhance security with permissions
+
+By using session keys, the exposure of the main private key is minimized. Even if a session key is compromised, the attacker would not gain access to the user's main account and funds. This layered approach to security helps in mitigating risks associated with key management.
+
+## Supported permissions
+
+The session key plugin supports the following types of permissions for each key:
+
+### Time range
+
+Supports a start time and an end time for each key.
+
+### Access control lists
+
+Supports either an allowlist or a denylist for addresses. Optionally, access control lists may also specify specific functions on contracts to allow or deny.
+
+### ERC-20 spending Limits
+
+Supports limiting how much of a specific ERC-20 token a key may spend. This may be a total for the key, or refreshing on an interval (e.g. 100 USDC per week).
+
+### Native token spending limits
+
+Supports limiting how much of the native token, e.g. ETH or MATIC, a key may spend. This may be a total for the key, or refreshing on an interval (e.g. 1 ETH per week).
+
+### Gas spending limits
+
+Supports limiting how much of the native token (e.g. ETH or MATIC) a session key can spend on gas. This may be a total for the key, or refreshing on an interval (e.g. 1 ETH per week).
+
+Alternatively, you can also require that a session key uses a specific paymaster address, instead of spending the account’s native token for gas.
+
+Note that the gas limit is tracked in terms of native token units (wei), not in units of gas. The gas usage of a user operation is considered to be the maximum gas a user operation can spend, i.e. `total gas limit * maxFeePerGas`. This can overestimate when compared to the actual gas cost of each user operation.
diff --git a/fern/wallets/pages/smart-contracts/other-accounts/modular-account/upgrading-to-modular-account.mdx b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/upgrading-to-modular-account.mdx
new file mode 100644
index 000000000..2b085bb69
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/other-accounts/modular-account/upgrading-to-modular-account.mdx
@@ -0,0 +1,93 @@
+---
+title: Modular Account • Upgrading to a Modular Account using Smart Wallets
+description: Upgrading to a Modular Account using Smart Wallets
+slug: wallets/smart-contracts/other-accounts/modular-account/upgrading-to-modular-account
+---
+
+Upgrading a `SmartContractAccount` can be done easily using Smart Wallets. It just involves a simple call to a single function on the `SmartAccountClient`, namely `upgradeAccount`, along with the necessary call data, `UpgradeToData`, for the account targeted for the upgrade. For upgrading to a Modular Account, you can use the utility function `getMSCAUpgradeToData` provided by the `@account-kit/smart-contracts` package to retrieve the call data for the upgrade. This process applies to any account with upgrade capabilities.
+
+Using the Light Account as an example, here is an overview of how the upgrade can be executed using a Smart Account Client:
+
+
+
+```ts example.ts
+import { lightAccountClient } from "./lightAccountClient";
+import { getMSCAUpgradeToData } from "@account-kit/smart-contracts";
+
+const { createMAAccount, ...upgradeToData } = await getMSCAUpgradeToData(
+ lightAccountClient,
+ { account: lightAccountClient.account },
+);
+
+const hash = await lightAccountClient.upgradeAccount({
+ upgradeTo: upgradeToData,
+ waitForTx: true,
+});
+
+const upgradedAccount = await createMAAccount();
+```
+
+```ts lightAccountClient.ts filename="lightAccountClient.ts"
+import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+
+export const lightAccountClient = await createLightAccountAlchemyClient({
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+```
+
+
+
+That is all! Now, you can create a smart account client to connect with the upgraded account as a Modular Account.
+
+
+
+```ts example.ts
+import { createAlchemySmartAccountClient, alchemy } from "@account-kit/infra";
+import { multiOwnerPluginActions } from "@account-kit/smart-contracts";
+import { upgradedAccount } from "./upgradedAccount";
+
+const upgradedAccountClient = await createAlchemySmartAccountClient({
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: lightAccountClient.chain,
+ account: upgradedAccount,
+}).extend(multiOwnerPluginActions);
+
+const owners = await upgradedAccountClient.readOwners();
+```
+
+```ts upgradedAccount.ts filename="upgradedAccount.ts"
+import { lightAccountClient } from "./lightAccountClient";
+import { getMSCAUpgradeToData } from "@account-kit/smart-contracts";
+
+const { createMAAccount, ...upgradeToData } = await getMSCAUpgradeToData(
+ lightAccountClient,
+ { account: lightAccountClient.account },
+);
+
+const hash = await lightAccountClient.upgradeAccount({
+ upgradeTo: upgradeToData,
+ waitForTx: true,
+});
+
+export const upgradedAccount = await createMAAccount();
+```
+
+```ts lightAccountClient.ts filename="lightAccountClient.ts"
+import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+
+export const lightAccountClient = await createLightAccountAlchemyClient({
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+});
+```
+
+
diff --git a/fern/wallets/pages/smart-contracts/overview.mdx b/fern/wallets/pages/smart-contracts/overview.mdx
new file mode 100644
index 000000000..8f9758f28
--- /dev/null
+++ b/fern/wallets/pages/smart-contracts/overview.mdx
@@ -0,0 +1,17 @@
+---
+title: Smart Contracts Overview
+description: An overview of the Smart Contracts available in Smart Wallets and how to use them
+slug: wallets/smart-contracts/overview
+---
+
+One of the low level packages available in Smart Wallets is our Smart Contracts package. This package contains all of the smart contract interfaces and utilities you need to use any of our
+secure, audited smart contracts.
+
+In these guides, we'll show you how to pick and choose the right smart contract for you and how to use them in your application.
+
+
+ For the easiest integration, we recommend using
+ [`@account-kit/react`](/wallets/react/quickstart) or
+ [`@account-kit/core`](/wallets/core/overview) which support all of the
+ contracts in this package by default.
+
diff --git a/fern/wallets/pages/smart-wallets/how-to-stamp-requests.mdx b/fern/wallets/pages/smart-wallets/how-to-stamp-requests.mdx
new file mode 100644
index 000000000..45e895110
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/how-to-stamp-requests.mdx
@@ -0,0 +1,99 @@
+---
+title: How to stamp requests
+description: Overview for how to send stamped (or verified) requests required for using Wallet APIs directly.
+subtitle: Overview for how to send stamped (or verified) requests required for using Wallet APIs directly.
+url: https://alchemy.com/docs/wallets/reference/how-to-stamp-requests
+slug: wallets/reference/how-to-stamp-requests
+---
+
+### What is stamping?
+
+Stamping, or verifying requests, is used to verify the integrity and authenticity of the requests in order to manage and use wallets in a secure and non-custodial manner.
+
+Every request made to create, authenticate, and sign Wallet APIs must include a signature over the POST body attached as a HTTP header. You’ll need to stamp with either a `targetPublicKey` in the body and/or an `X-Stamp` attached to the HTTP POST request headers. A secure enclave will use these parameters to verify and encrypt the authenticity of your requests, ensuring that there are no man in the middle attacks.
+
+### SDKs
+
+We provide [SDKs](https://accountkit.alchemy.com/) to abstract this stamping logic and simplify your integration:
+
+* [React](https://accountkit.alchemy.com/react/quickstart)
+* [React Native](https://accountkit.alchemy.com/react-native/overview)
+* [Vanilla JS](https://accountkit.alchemy.com/core/quickstart)
+* Java (Coming soon)
+
+### How to stamp in another language
+
+If we do not have an out of the box SDK in the language you need, you can build your own custom stamping logic to use our Wallet APIs.
+
+For any endpoint the requires a `stampedRequest` body for the verification logic you’ll need to:
+
+1. Generate and store a P256 Public Private Key Pair (TEK) on device for future API requests
+
+ 1. The public key will be used as the `targetPublicKey` in Wallet API requests like Create Wallet.
+
+2. Generate a private key bundle
+
+ 1. Exchange the `targetPublicKey` for an encrypted `bundle` by calling [create wallet](/wallets/reference/account-kit/core/functions/createAccount) (e.g., OAuth, email login)
+
+3. Decrypt the bundle using Hybrid Public Key Encryption (HPKE).
+
+ 1. Take the received bundle and decode it using Base58Check. The decoded bundle consists of:
+
+ 1. The first 33 bytes: an ephemeral public key.
+ 1. Convert into NIST P256 uncompressed format to be used in HPKE as `ephemeralUncompressedPublicKeyBytes`.
+ 2. The remaining bytes: the ciphertext to decrypt.
+
+ 2. Pass the above in to HPKE decrypt using the TEK private key
+
+ 1. KemId: `DHKEM_P256_HKDF_SHA256`
+ 2. KdfId: `HKDF_SHA256`
+ 3. AeadId: `AES_256_GCM`
+ 4. info: `turnkey_hpke`
+ 5. aad: `ephemeralUncompressedPublicKeyBytes + tekPublicKeyBytes`
+
+4. Sign the JSON-encoded POST body with your private key (generated in step 3) to produce a `signature` *(DER-encoded)*
+
+ 1. And hex encode the `signature`
+
+5. Create a JSON-encoded stamp:
+
+ * `publicKey`: the public key of TEK Key Pair
+ * `signature`: the signature produced in step 2
+ * `scheme`: `SIGNATURE_SCHEME_TK_API_P256`
+
+6. `Base64URL` encode the stamp
+
+7. Attach the encoded string to your request as a `stampedRequest` body as such:
+
+ ```shell
+ {
+ "stampedRequest": {
+ "body": "{\"organizationId\": ...}, // the JSON stringified body of the request
+ "stamp": {
+ "stampHeaderName": "X-Stamp", // this is hardcoded
+ "stampHeaderValue": "eyJ..." // generated from step 5
+ },
+ "url": "https://example.com" // this doesn't matter you can hard code this.
+ }
+ }
+ ```
+
+8. Submit the stamped request to our APIs
+
+If you're building in React, Vanilla JS, or (soon) Java, use our [SDK](https://accountkit.alchemy.com/) to which will handle this stamping logic for you. If you're building in another language not supported by our SDKs see the following implementations:
+
+* (Coming soon) Java SDK
+
+* [JS SDK stamper](https://github.com/tkhq/sdk/tree/24c399bd8e6c04ceb3e75ba9438b859cef796fda/packages/http)
+
+ * Specify the [body parameters for a request](https://github.com/tkhq/sdk/blob/main/packages/api-key-stamper/src/index.ts#L75-L78)
+ * Given a TEK [generate the payloads and generate the signatures for the request](https://github.com/tkhq/sdk/blob/24c399bd8e6c04ceb3e75ba9438b859cef796fda/packages/http/src/base.ts#L216-L255).
+
+* [JS SDK React Native stamper](https://github.com/alchemyplatform/aa-sdk/blob/9ad59f2d6673bc6f587e6a57343b1486c92f382f/account-kit/rn-signer/src/NativeTEKStamper.ts#L9)
+
+ * [Generating keys, stamping requests on iOS](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/rn-signer/ios/implementation/NativeTEKStamperImpl.swift)
+ * [Generating keys, stamping requests on Android](https://github.com/alchemyplatform/aa-sdk/blob/main/account-kit/rn-signer/android/src/main/java/com/accountkit/reactnativesigner/NativeTEKStamperModule.kt)
+
+* [More examples of stamping via our partner Turnkey](https://docs.turnkey.com/developer-reference/api-overview/stamps#stampers)
+
+If you have more questions, please reach out.
diff --git a/fern/wallets/pages/smart-wallets/quickstart/api.mdx b/fern/wallets/pages/smart-wallets/quickstart/api.mdx
new file mode 100644
index 000000000..45f35de73
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/quickstart/api.mdx
@@ -0,0 +1,172 @@
+---
+title: Wallets API Quickstart (API)
+subtitle: Learn to interact with Wallet APIs using any RPC client
+url: https://www.alchemy.com/docs/wallets/smart-wallet-quickstart/api
+slug: wallets/smart-wallet-quickstart/api
+---
+
+We'll demonstrate how to use the [`wallet_requestAccount`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account), [`wallet_prepareCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls), [`wallet_sendPreparedCalls`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls), and [`wallet_getCallsStatus`](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status) endpoints.
+
+```mermaid
+flowchart LR
+ A[requestAccount] --> B[prepareCalls]
+ B --> C[sendPreparedCalls]
+ C --> D[getCallsStatus]
+```
+
+### 1. Request an Account for the Owner Signer
+
+Given an owner address, call `wallet_requestAccount` to return the smart account address for that owner. The [owner](/docs/wallets/signer/what-is-a-signer) address can be any signer (or public key) that has the ability to sign transactions.
+
+* If you want to use social sign up / log in, you can simply use the [SDK](/docs/wallets/react/getting-started) to authenticate user's and retrieve their signer address
+* If instead, you want to generate and control wallets with a custodied owner, you can generate any public private key pair (e.g. any EOA)
+
+This will return the account address associated with the given signer, as well as a uuid you could use to differentiate between accounts for the same signer in the future.
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "0xOWNER_ADDRESS"
+ }
+ ]
+}
+'
+```
+
+This will return the smart account address associated with the given signer:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "accountAddress": "0xACCOUNT_ADDRESS",
+ "id": "af638-a8..."
+ }
+}
+```
+
+### 2. Prepare Calls
+
+Now that we have an account, it's time to prepare some calls!
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "capabilities": {
+ "paymasterService": {
+ "policyId": GAS_MANAGER_POLICY_ID // put your gas manager policy ID here
+ }
+ },
+ "calls": [
+ {
+ "to": "0x0000000000000000000000000000000000000000"
+ }
+ ],
+ "from": "0xACCOUNT_ADDRESS",
+ "chainId": "0xCHAIN_ID"
+ }
+ ]
+}
+'
+```
+
+This will return the userop request (the `data` field) and a signature request, for example:
+
+```json
+{
+ "type": "user-operation-v070",
+ "data": {...useropRequest},
+ "chainId": "0xCHAIN_ID",
+ "signatureRequest": {
+ "type": "personal_sign",
+ "data": {
+ "raw": "0xHASH_TO_SIGN",
+ },
+ "rawPayload": "0xRAW_PAYLOAD_TO_SIGN"
+ }
+}
+```
+
+If using Alchemy's signer service, this is the full raw payload to sign.
+If using a library like Viem, you can instead use `personal_sign` to sign the hash.
+
+### 3. Sign the userop
+
+With the returned signature request, all you have to do is sign the signature request (returned in step 2). You'll sign this using the account owner (from step 1).
+
+If using an Alchemy Signer, you can learn how to stamp the request on the frontend [here](/docs/reference/how-to-stamp-requests). When using the Alchemy Signer, it's easiest to sign the `signatureRequest.rawPayload` directly.
+
+If you are using a library such as Viem that supports the `personal_sign` method, you should use that to sign the hash (since the `signatureRequest.type` is `"personal_sign"`).
+
+### 4. Send the Prepared Calls!
+
+With the signature from step 3 and the `useropRequest` from step 2, you're good to send the call!
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_sendPreparedCalls",
+ "params": [
+ {
+ "type": "user-operation-v070",
+ "data": {...useropRequest},
+ "chainId": "0xCHAIN_ID", // E.g. "0x66eee" for Arbitrum Sepolia
+ "signature": {
+ "type": "secp256k1"
+ "data": "0xUSEROP_SIGNATURE"
+ }
+ }
+ ]
+}
+'
+```
+
+This will return the array of prepared call IDs!
+
+### 5. Check The Calls Status
+
+Now you can simply call `wallet_getCallsStatus` to check the status of the calls. A “pending” state (1xx status codes) is expected for some time before the transition to “confirmed,” so this endpoint should be polled while the status is pending. If the call does not progress, refer to the [retry guide](/wallets/transactions/retry-transactions).
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_getCallsStatus",
+ "params": [
+ "0xPREPARED_CALL_ID"
+ ]
+}
+'
+```
+
+See [here](/docs/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status) for all of the possible results.
diff --git a/fern/wallets/pages/smart-wallets/quickstart/index.mdx b/fern/wallets/pages/smart-wallets/quickstart/index.mdx
new file mode 100644
index 000000000..91b353232
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/quickstart/index.mdx
@@ -0,0 +1,37 @@
+---
+title: Wallets API Quickstart
+description: How to go from zero to hero with Wallet APIs
+subtitle: Learn to interact with Wallet APIs
+url: https://alchemy.com/docs/wallets/reference/smart-wallet-quickstart
+slug: wallets/reference/smart-wallet-quickstart
+---
+
+This quickstart shows you how to prepare and send a User Operation (UO) in minutes.
+
+To use this guide, you'll need:
+
+* An account you can sign with (e.g. an [Alchemy Signer](/docs/wallets/signer/what-is-a-signer#alchemy-signer) or an EOA)
+* An Alchemy API key
+* A [gas manager policy ID](/wallets/transactions/sponsor-gas) if sponsoring gas
+
+
+ Start using the Alchemy Wallets API today! [Get started for
+ free](https://dashboard.alchemy.com/signup/?a=f8afc2202c).
+
+
+You can complete this tutorial using either the [TypeScript SDK](/wallets/smart-wallet-quickstart/sdk) or raw [JSON-RPC APIs](/wallets/smart-wallet-quickstart/api).
+
+
+
+ Start building in minutes using the TypeScript SDK.
+
+
+
+ Integrate with any RPC client using the JSON-RPC APIs.
+
+
+
+
+ Want to use EIP-7702? Check out the [7702
+ Quickstart](/docs/wallets/transactions/using-eip-7702) instead.
+
diff --git a/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx b/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx
new file mode 100644
index 000000000..7fe69a7f8
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/quickstart/sdk.mdx
@@ -0,0 +1,120 @@
+---
+title: Wallets API Quickstart (SDK)
+subtitle: Learn to interact with Wallet APIs using the Wallet Client SDK
+url: https://www.alchemy.com/docs/wallets/smart-wallet-quickstart/sdk
+slug: wallets/smart-wallet-quickstart/sdk
+---
+
+### 1. Install Prerequisities
+
+You're going to need `@account-kit/wallet-client`, `@account-kit/infra`, optionally `@aa-sdk/core` if you use a `LocalAccountSigner`, and optionally `viem` if you want to verify a signed message.
+
+
+```shell npm
+npm install @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+```shell bun
+bun add @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+```shell yarn
+yarn add @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+
+
+### 2. Create A Smart Account Client
+
+Given a signer (e.g. a `LocalAccountSigner` imported from `@aa-sdk/core` or an [Alchemy Signer](/docs/wallets/signer/what-is-a-signer#alchemy-signer)), all you need to do is follow a few simple steps to start sending user ops with Wallet APIs!
+
+```ts
+import { createSmartWalletClient } from "@account-kit/wallet-client";
+import { alchemy, arbitrumSepolia } from "@account-kit/infra";
+import { LocalAccountSigner } from "@aa-sdk/core";
+
+const signer = LocalAccountSigner.privateKeyToAccountSigner(PRIVATE_KEY); // we use a private key signer as an example here
+
+const transport = alchemy({
+ apiKey: ALCHEMY_API_KEY, // use your Alchemy app api key here!
+});
+
+const client = createSmartWalletClient({
+ transport,
+ chain: arbitrumSepolia, // use any chain imported from @account-kit/infra here!
+ signer,
+});
+```
+
+### 3. Request The Account
+
+A counterfactual address is the account address associated with the given signer-- but the account contract hasn't been deployed yet.
+
+```ts
+const account = await client.requestAccount();
+
+// get the address
+const address = account.address;
+```
+
+### 4. Sign A Message
+
+```ts
+import { createPublicClient } from "viem";
+
+const message = "we are so back";
+
+const signature = await client.signMessage({ message });
+
+const publicClient = createPublicClient({
+ chain: arbitrumSepolia,
+ transport: transport,
+});
+
+const isValid = await publicClient.verifyMessage({
+ address: account.address, // fetched from await client.requestAccount()
+ message,
+ signature,
+});
+```
+
+### 5. Sign Typed Data
+
+```ts
+// assuming you have a typedData variable
+const signature = await client.signTypedData({ typedData });
+
+const isValid = await publicClient.verifyTypedData({
+ address: account.address, // fetched from await client.requestAccount()
+ ...typedData,
+ signature,
+});
+```
+
+### 6. Send A UserOp
+
+```ts
+import { zeroAddress } from "viem";
+
+const account = await client.requestAccount();
+
+const preparedCalls = await client.prepareCalls({
+ calls: [{ to: zeroAddress, value: "0x0" }], // callData is optional in a "data" parameter
+ from: account.address,
+ // "capabilities" is a data structure that hold gas manager data (as seen below) or permission data
+ capabilities: {
+ paymasterService: {
+ policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here
+ },
+ },
+});
+
+// Sign the calls
+const signedCalls = await client.signPreparedCalls(preparedCalls);
+
+// Send the userOp
+const result = await client.sendPreparedCalls(signedCalls);
+
+// Check calls status.
+const status = await client.getCallsStatus(result.preparedCallIds[0]!);
+```
diff --git a/fern/wallets/pages/smart-wallets/session-keys/api.mdx b/fern/wallets/pages/smart-wallets/session-keys/api.mdx
new file mode 100644
index 000000000..d197ffc29
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/session-keys/api.mdx
@@ -0,0 +1,216 @@
+---
+title: Session Keys (API)
+subtitle: Learn how to use session keys using any RPC client
+url: https://alchemy.com/docs/wallets/reference/wallet-apis-session-keys/api
+slug: wallets/reference/wallet-apis-session-keys/api
+---
+
+### 1. Request an Account for the Owner Signer
+
+Given an owner address, call `wallet_requestAccount` to return the smart account address for that owner. The [owner](/docs/wallets/signer/what-is-a-signer) address can be any signer (or public key) that has the ability to sign transactions.
+
+* If you want to use social sign up / log in, you can simply use the [SDK](/docs/wallets/react/getting-started) to authenticate users and retrieve their signer address
+* If instead, you want to generate and control wallets with a custodied owner, you can generate any public private key pair (e.g. any EOA)
+
+This will return the account address associated with the given signer, as well as a uuid you could use to differentiate between accounts for the same signer in the future.
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "0xOWNER_ADDRESS"
+ }
+ ]
+}
+'
+```
+
+This will return the smart account address associated with the given signer:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "accountAddress": "0xACCOUNT_ADDRESS",
+ "id": "af638-a8..."
+ }
+}
+```
+
+### 2. Create a Session With the Session Key Signer
+
+
+ If you are using an EIP-7702 account, the account must be delegated onchain
+ before creating the session. If the account has already sent calls, it will
+ already be delegated. If it hasn't sent any calls before creating the session
+ key, you can delegate it by sending an empty call as the owner.
+
+
+To create a session key using onchain policies:
+
+* Get the public address of a key you want to use as a session key. This can be any key pair that has the ability to sign (aka a [signer](/docs/wallets/signer/what-is-a-signer) that is either an local [signer](/wallets/reference/aa-sdk/core/classes/LocalAccountSigner) like an EOA or signer generated with a signer provider).
+* Create a session for that key, by passing it as the `publicKey` in a call to `wallet_createSession`. (Note that this must be the public key **address**, not the full public key.)
+
+Note that the expiry is in seconds and represents a UNIX timestamp (e.g. 1776657600 for April 20th, 2077).
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_createSession",
+ "params": [
+ {
+ "account": "0xACCOUNT_ADDRESS",
+ "chainId": "0xCHAIN_ID",
+ "expirySec": UNIX_TIMESTAMP_EXPIRY_IN_SECONDS,
+ "key": {
+ "publicKey": "0xSESSION_KEY_ADDRESS",
+ "type": "secp256k1",
+ },
+ "permissions": [
+ {
+ "type": "root"
+ }
+ ]
+ }
+ ]
+}'
+```
+
+This will return two key elements:
+
+1. The session ID
+2. The signature request that must be signed by the account owner to authorize the session key
+
+Keep note of the session ID, you'll need it later!
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "sessionId": "0xSESSION_ID",
+ "signatureRequest": {
+ "type": "eth_signTypedData_v4",
+ "data": {...},
+ "rawPayload": "0xRAW_PAYLOAD_TO_SIGN"
+ }
+ }
+}
+```
+
+### 3. Sign the Session Key Authorization
+
+Sign the signature request using the account owner's key (used in step 1), then store the resulting signature. If you are using an Alchemy Signer, you can directly sign the `rawPayload`. If you are using a library that supports signing typed data, you can use it to sign the typed data.
+
+### 4. Prepare Calls With the Session Key
+
+With the session ID received in step 2 and the signature from step 3, we're now ready to prepare some calls!
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "GAS-MANAGER-POLICY-ID", // put your gas manager policy ID here
+ },
+ "permissions": {
+ "sessionId": "0xSESSION_ID",
+ "signature": "0xPERMISSION_SIG",
+ }
+ },
+ "calls": [
+ {
+ "to": "0x0000000000000000000000000000000000000000"
+ }
+ ],
+ "from": "0xACCOUNT_ADDRESS",
+ "chainId": "0xCHAIN_ID"
+ }
+ ]
+}
+'
+```
+
+This will return the userop request (the `data` field) and a signature request, for example:
+
+```json
+{
+ "type": "user-operation-v070",
+ "data": {...useropRequest},
+ "chainId": "0xCHAIN_ID",
+ "signatureRequest": {
+ "type": "personal_sign",
+ "data": {
+ "raw": "0x_HASH_TO_SIGN",
+ },
+ "rawPayload": "0xRAW_PAYLOAD_TO_SIGN"
+ }
+}
+```
+
+### 5. Sign the userop
+
+With the returned signature request, all you have to do is sign the userop hash returned in the `signatureRequest` field. This should be signed with the session key. This signature will be valid as long as it is within the permissions the session key has.
+
+Note that the `type` field in the `signatureRequest` indicates the signature type needed. In this case, we need to `personal_sign` the hash, or, if using an Alchemy Signer, you can directly sign the `rawPayload` instead.
+
+### 6. Send the Prepared Calls!
+
+With the signature from step 5 and the `useropRequest` from step 4, you're good to go to send the call!
+
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/API_KEY \
+ --header 'accept: application/json' \
+ --header 'content-type: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_sendPreparedCalls",
+ "params": [
+ {
+ "type": "user-operation-v070",
+ "data": {...useropRequest},
+ "chainId": "0xCHAIN_ID", // e.g. "0x66eee" for Arbitrum Sepolia
+ "capabilities": {
+ "permissions": {
+ "sessionId": "0xSESSION_ID",
+ "signature": "0xPERMISSION_SIG",
+ }
+ },
+ "signature": {
+ "type": "secp256k1",
+ "data": "0xUSEROP_SIGNATURE",
+ }
+ }
+ ]
+}
+'
+```
+
+This will return the array of prepared call IDs!
diff --git a/fern/wallets/pages/smart-wallets/session-keys/index.mdx b/fern/wallets/pages/smart-wallets/session-keys/index.mdx
new file mode 100644
index 000000000..2daef1780
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/session-keys/index.mdx
@@ -0,0 +1,152 @@
+---
+title: Session Keys
+subtitle: Learn how to use session keys with Wallet APIs
+url: https://alchemy.com/docs/wallets/reference/wallet-apis-session-keys
+slug: wallets/reference/wallet-apis-session-keys
+---
+
+Session keys are a powerful feature of the Alchemy Wallets API that allow you to create a session for a user's smart account with specific permissions. This enables secure, permissioned access to the user's wallet, allowing your app's server to perform actions on behalf of the user without needing their private key. Session keys allow another account to operate on a user's smart account with given permissions. After creating a session, you will be able to sign transactions for the generated wallet within the defined permissions using that session key. See [here for a list of permissions!](#permission-types)
+
+To use this guide, you'll need:
+
+* An account you can sign with (e.g. an [Alchemy Signer](/docs/wallets/signer/what-is-a-signer#alchemy-signer) or an EOA)
+* An Alchemy API key
+* A [gas manager policy ID](/docs/wallets/transactions/sponsor-gas) if sponsoring gas
+
+
+ Start using the Alchemy Wallets API today! [Get started for
+ free](https://dashboard.alchemy.com/signup/?a=f8afc2202c).
+
+
+### Create A Session With Permissions
+
+We'll demonstrate how to create and use session keys [using the SDK client](/docs/reference/wallet-apis-session-keys/sdk) or by using platform-agnostic [JSON-RPC APIs](/docs/reference/wallet-apis-session-keys/api).
+
+
+
+ Start building in minutes using the TypeScript SDK.
+
+
+
+ Integrate with any RPC client using the JSON-RPC APIs.
+
+
+
+## Permission Types
+
+To specify permissions during a session key installation, include them in the `permissions` array when calling `client.grantPermission()` via the SDK or `wallet_createSession` via the API.
+
+```ts
+const permissions = await client.grantPermissions({
+ account: account.address,
+ expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
+ key: {
+ publicKey: await sessionKey.getAddress(),
+ type: "secp256k1",
+ },
+ permissions: [{ PERMISSION_ONE }, { PERMISSION_TWO }],
+});
+```
+
+### Native Token Transfer
+
+This permission allows transfer of native tokens (like Ether) from the account.
+
+```ts
+{
+ type: "native-token-transfer";
+ data: {
+ allowance: Hex; // a hexadecimal encoded transfer limit, for example, 1 ETH would be 0xde0b6b3a7640000 (1e18 in hex)
+ }
+}
+```
+
+### ERC20 Token Transfer
+
+This permission allows transfer or approval of ERC-20 tokens from the account. The specified allowance represents the total cumulative spend limit for all transfers and approvals (it is not a per-operation allowance).
+
+```ts
+{
+ type: "erc20-token-transfer";
+ data: {
+ address: Address; // erc20 token contract address
+ allowance: Hex; // a hexadecimal encoded transfer limit
+ }
+}
+```
+
+### Gas Limit
+
+This permission allows the session key to spend gas for user operations up to a specified limit.
+
+```ts
+{
+ type: "gas-limit";
+ data: {
+ limit: Hex; // a hexadecimal encoded gas limit, for example 300000 gas would be 0x493e0
+ }
+}
+```
+
+### Contract Access
+
+This permission grants access to **all** functions in a specific contract.
+
+```ts
+{
+ type: "contract-access";
+ data: {
+ address: Address; // the target contract’s address
+ }
+}
+```
+
+### Account Functions
+
+This permission grants access to specific functions on the smart account itself.
+
+```ts
+{
+ type: "account-functions";
+ data: {
+ functions: Hex[]; // array of allowed function selectors, e.g. ["0xabcdef01", "0x12345678"]
+ };
+}
+```
+
+### Functions On All Contracts
+
+This permission grants access to a set of function selectors **across any** address.
+
+```ts
+{
+ type: "functions-on-all-contracts";
+ data: {
+ functions: Hex[]; // array of function selectors allowed globally, e.g. ["0xddf252ad"]
+ };
+}
+```
+
+### Functions On Contract
+
+This permission grants access to specific function selectors on **one** contract.
+
+```ts
+{
+ type: "functions-on-contract";
+ data: {
+ address: Address; // the contract address you’re targeting
+ functions: Hex[]; // array of allowed function selectors for that contract, e.g. ["0xddf252ad"]
+ };
+}
+```
+
+### Root
+
+This permission grants full access to everything. Needless to say, this is a very dangerous permission to grant.
+
+```ts
+{
+ type: "root"; // no additional data required
+}
+```
diff --git a/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx b/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx
new file mode 100644
index 000000000..c88acd114
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/session-keys/sdk.mdx
@@ -0,0 +1,131 @@
+---
+title: Session Keys (SDK)
+subtitle: Learn how to use session keys using the Wallet Client SDK
+url: https://alchemy.com/docs/wallets/reference/wallet-apis-session-keys/sdk
+slug: wallets/reference/wallet-apis-session-keys/sdk
+---
+
+### 1. Install Prerequisites
+
+You're going to need the `@account-kit/wallet-client` and `@account-kit/infra`. We'll also be using `LocalAccountSigner` from `@aa-sdk/core` as the signer for demonstration purposes.
+
+
+```shell npm
+npm install @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+```shell bun
+bun add @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+```shell yarn
+yarn add @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+```shell pnpm
+pnpm install @account-kit/wallet-client @account-kit/infra @aa-sdk/core
+```
+
+
+
+### 2. Create A Smart Wallet Client
+
+Create a client for a given signer (e.g. a `LocalAccountSigner` imported from `@aa-sdk/core` or an [Alchemy Signer](/docs/wallets/signer/what-is-a-signer#alchemy-signer)).
+
+```ts
+import { createSmartWalletClient } from "@account-kit/wallet-client";
+import { alchemy, arbitrumSepolia } from "@account-kit/infra";
+import { LocalAccountSigner } from "@aa-sdk/core";
+
+const signer = LocalAccountSigner.privateKeyToAccountSigner(PRIVATE_KEY); // we use a private key signer as an example here
+
+const transport = alchemy({
+ apiKey: ALCHEMY_API_KEY, // use your Alchemy app api key here
+});
+
+const client = createSmartWalletClient({
+ transport,
+ chain: arbitrumSepolia, // use any chain imported from @account-kit/infra here
+ signer,
+});
+```
+
+### 3. Create the session key
+
+
+
+If you are using an EIP-7702 account, the account must be delegated onchain
+before creating the session. If the account has already sent calls, it will
+already be delegated. If it hasn't sent any calls before creating the session
+key, you can delegate it by sending an empty call as the owner:
+
+```ts
+const preparedCalls = await client.prepareCalls({
+ calls: [], // empty array since you don't want to send any calls
+ from: account.address,
+ capabilities: {
+ paymasterService: {
+ policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here
+ },
+ },
+});
+
+const signedCalls = await client.signPreparedCalls(preparedCalls);
+
+const { preparedCallIds } = await client.sendPreparedCalls(signedCalls);
+
+const status = await client.getCallsStatus(preparedCallIds[0]);
+```
+
+Now you can continue to create the session key as described below.
+
+
+
+```ts
+const account = await client.requestAccount(); // Request an account for the owner signer
+
+// This is where you would use your session key signer!
+const sessionKey = LocalAccountSigner.generatePrivateKeySigner();
+
+const permissions = await client.grantPermissions({
+ account: account.address,
+ expirySec: Math.floor(Date.now() / 1000) + 60 * 60,
+ key: {
+ publicKey: await sessionKey.getAddress(),
+ type: "secp256k1",
+ },
+ permissions: [{ type: "root" }], // Here we grant root permissions as an example, but this is not advised in production!
+});
+```
+
+### 4. Send calls using the session key
+
+```ts
+import { signPreparedCalls } from "@account-kit/wallet-client";
+
+const preparedCalls = await client.prepareCalls({
+ calls: [{ to: "0x0000000000000000000000000000000000000000", value: "0x0" }],
+ from: account.address,
+ capabilities: {
+ paymasterService: {
+ policyId: GAS_MANAGER_POLICY_ID, // put your gas manager policy ID here
+ },
+ permissions,
+ },
+});
+
+const signedCalls = await signPreparedCalls(
+ sessionKey, // Note that we now sign with the session key!
+ preparedCalls,
+);
+
+const { preparedCallIds } = await client.sendPreparedCalls({
+ ...signedCalls,
+ capabilities: {
+ permissions,
+ },
+});
+
+// Check calls status.
+const status = await client.getCallsStatus(preparedCallIds[0]);
+```
diff --git a/fern/wallets/pages/smart-wallets/wallets-api-endpoints/createaccount.mdx b/fern/wallets/pages/smart-wallets/wallets-api-endpoints/createaccount.mdx
new file mode 100644
index 000000000..de3fe1767
--- /dev/null
+++ b/fern/wallets/pages/smart-wallets/wallets-api-endpoints/createaccount.mdx
@@ -0,0 +1,14 @@
+---
+title: Create Wallet
+description: Programmatically create a new wallet. A new Ethereum and Solana private key will be generated inside our non-custodial key management infrastructure. The wallet is secured by the user’s authentication credentials.
+subtitle: Programmatically create a new wallet. A new Ethereum and Solana private key will be generated inside our non-custodial key management infrastructure. The wallet is secured by the user’s authentication credentials.
+slug: reference/createaccount
+---
+
+post https://api.g.alchemy.com/signer/v1/signup
+
+Programmatically create a new wallet. A new Ethereum and Solana private key will be generated inside our non-custodial key management infrastructure. The wallet is secured by the user’s authentication credentials.
+
+**Using React or Vanilla JS?** Use our [Account Kit SDK](https://accountkit.alchemy.com/) to create and use wallets. The SDK handles all signing and authentication complexity for you, making development faster and easier. We provide an [SDK](https://github.com/alchemyplatform/aa-sdk) in React, Vanilla Typescript, and (soon) Java.
+
+If you want to use APIs directly, please first see [this guide](/reference/how-to-stamp-requests) for implementing custom logic to send stamped requests.
diff --git a/fern/wallets/pages/third-party/signers/custom-integration.mdx b/fern/wallets/pages/third-party/signers/custom-integration.mdx
new file mode 100644
index 000000000..0e658fb7e
--- /dev/null
+++ b/fern/wallets/pages/third-party/signers/custom-integration.mdx
@@ -0,0 +1,185 @@
+---
+title: Custom Integration
+description: Bring your own signer to Alchemy Smart Wallets via EIP-7702
+slug: wallets/third-party/signers/custom-integration
+---
+
+If you have an existing custom signer or another third-party embedded wallet provider, you can upgrade your embedded EOAs to smart wallets by connecting your existing signer.
+This will allow you to use EIP-7702 to get features like gas sponsorship, batching, and more.
+
+
+ **Requirement:** Your signer or embedded wallet provider must support signing
+ EIP-7702 authorizations in order to delegate to a smart account.
+
+
+To bring your own signer, you create a `SmartAccountSigner` that implements `signAuthorization`. See the details for the interface requirements [here](/wallets/resources/types#smartaccountsigner).
+
+
+
+ For example, you can upgrade an existing embedded EOA by extending a viem `WalletClient` to use your provider's EIP-7702 authorization signing.
+
+ The bulk of the logic happens in a function that returns a client. Your embedded wallet is wrapped in a `WalletClientSigner` that supports `signAuthorization`, then passed to `createSmartWalletClient` to construct a client for sending transactions.
+
+ Steps:
+
+ 1. Wrap your embedded wallet with `WalletClientSigner` that implements `signAuthorization`.
+ 2. Create a `SmartWalletClient` with your signer and Alchemy API key (optionally a gas `policyId`).
+ 3. Send calls with `eip7702Auth: true` (and optional `paymasterService.policyId`).
+
+
+
+```ts twoslash title="create-smart-embedded-wallet-client.ts"
+import {
+ type Address,
+ type Authorization,
+ createWalletClient,
+ custom,
+ type EIP1193Provider,
+} from "viem";
+import { type AuthorizationRequest, WalletClientSigner } from "@aa-sdk/core";
+import { baseSepolia, alchemy } from "@account-kit/infra";
+import {
+ createSmartWalletClient,
+ type SmartWalletClient,
+} from "@account-kit/wallet-client";
+
+export type EmbeddedWallet = {
+ address: string;
+ getEthereumProvider: () => Promise;
+};
+
+export type SignAuthorizationFn = (
+ req: AuthorizationRequest & { contractAddress?: string },
+) => Promise<{ r: string; s: string; yParity: number }>;
+
+export type CreateSmartWalletParams = {
+ embeddedWallet: EmbeddedWallet;
+ signAuthorization: SignAuthorizationFn;
+ apiKey: string;
+ policyId?: string;
+};
+
+/**
+ * Creates an Alchemy Smart Wallet client for a generic embedded wallet using EIP-7702.
+ */
+export async function createSmartEmbeddedWalletClient({
+ embeddedWallet,
+ signAuthorization,
+ apiKey,
+ policyId,
+}: CreateSmartWalletParams): Promise {
+ if (!apiKey) {
+ throw new Error("Missing Alchemy API key");
+ }
+ const provider = await embeddedWallet.getEthereumProvider();
+
+ const baseSigner = new WalletClientSigner(
+ createWalletClient({
+ account: embeddedWallet.address as Address,
+ chain: baseSepolia,
+ transport: custom(provider),
+ }),
+ "custom",
+ );
+
+ const signer = {
+ ...baseSigner,
+ async signAuthorization(
+ unsignedAuth: AuthorizationRequest,
+ ): Promise> {
+ const { address, ...rest } = unsignedAuth;
+ const signature = await signAuthorization({
+ ...rest,
+ contractAddress: (address ?? rest.contractAddress) as `0x${string}`,
+ });
+
+ return {
+ ...unsignedAuth,
+ ...signature,
+ } as Authorization;
+ },
+ };
+
+ return createSmartWalletClient({
+ chain: baseSepolia,
+ transport: alchemy({ apiKey }),
+ signer,
+ policyId,
+ });
+}
+```
+
+
+
+ When using the `SmartWalletClient` you must set the `eip7702Auth` capability to `true`:
+
+
+
+```ts twoslash title="send-with-eip7702.ts"
+import type { Address, EIP1193Provider } from "viem";
+import {
+ createSmartEmbeddedWalletClient,
+ type SignAuthorizationFn,
+} from "./getClient";
+
+async function main() {
+ const embeddedWallet =
+ /* your embedded wallet instance */ null as unknown as {
+ address: string;
+ getEthereumProvider: () => Promise;
+ };
+ const signAuthorization: SignAuthorizationFn =
+ /* your EIP-7702 sign function */ async () => {
+ return { r: "0x", s: "0x", yParity: 1 };
+ };
+
+ const client = await createSmartEmbeddedWalletClient({
+ embeddedWallet,
+ signAuthorization,
+ apiKey: "YOUR_ALCHEMY_API_KEY",
+ policyId: "YOUR_GAS_POLICY_ID", // Optional: for gas sponsorship
+ });
+
+ // Send transaction with EIP-7702 and optional gas sponsorship
+ const result = await client.sendCalls({
+ from: embeddedWallet.address as Address,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x0",
+ data: "0x",
+ },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: "YOUR_GAS_POLICY_ID",
+ },
+ },
+ });
+
+ if (!result.preparedCallIds?.length) {
+ throw new Error("No prepared call IDs returned");
+ }
+
+ // Wait for transaction confirmation
+ const txStatus = await client.waitForCallsStatus({
+ id: result.preparedCallIds[0],
+ timeout: 60_000,
+ });
+
+ const txnHash = txStatus.receipts?.[0]?.transactionHash;
+ if (!txnHash) {
+ throw new Error("Transaction hash not found in receipt");
+ }
+
+ console.log("Transaction confirmed:", txnHash);
+}
+
+main();
+```
+
+
+
+
+
diff --git a/fern/wallets/pages/third-party/signers/privy.mdx b/fern/wallets/pages/third-party/signers/privy.mdx
new file mode 100644
index 000000000..35c9b06e4
--- /dev/null
+++ b/fern/wallets/pages/third-party/signers/privy.mdx
@@ -0,0 +1,620 @@
+---
+title: Privy
+description: Use Privy with Alchemy Smart Wallets for EIP-7702, sponsorship, swaps, and batching
+slug: wallets/third-party/signers/privy
+---
+
+Upgrade existing Privy wallets to Smart Wallets to enable gasless transactions (EVM & Solana), batching, token swaps, and more in under 10 minutes. Keep Privy for authentication, no wallet migration needed. Add our battle-tested transaction infrastructure using EIP-7702 to upgrade your wallets to Smart Wallets:
+
+* [#1 gas abstraction infrastructure](https://www.bundlebear.com/erc4337-bundlers/all) on the market
+* [370M+](https://www.bundlebear.com/erc4337-paymasters/all) sponsored transactions
+* 99.9% SLAs
+* Trusted by Worldcoin, JP Morgan, Gensyn, and more
+* Solana gas sponsorship support
+
+
+ Don't have social login or a wallet set up yet? Start with the
+ Smart Wallet Quickstart to create one in
+ minutes.
+
+
+
+
+ ## Setup
+
+ Follow these steps to integrate Privy with Smart Wallets.
+
+ ### Installation
+
+ ```bash
+ npm install @account-kit/privy-integration
+ # or
+ yarn add @account-kit/privy-integration
+ # or
+ pnpm add @account-kit/privy-integration
+ ```
+
+
+ **Solana Support (Optional):** To use `useAlchemySolanaTransaction`, install `@solana/web3.js` and import from `@account-kit/privy-integration/solana`. This dependency is optional—skip it if you only need EVM features.
+
+
+ ### Prerequisites: Get your keys (API key, Policy ID, Privy App ID)
+
+ * Alchemy API Key:
+ * Go to the [Alchemy Dashboard](https://dashboard.alchemy.com/)
+ * Create or select an app and copy the API key
+ * Gas sponsorship Policy ID (Gas Manager):
+ * Create a gas sponsorship policy in the [dashboard](https://dashboard.alchemy.com/services/gas-manager/configuration) and copy its Policy ID
+ * Privy App ID:
+ * Go to the [Privy Dashboard](https://dashboard.privy.io/)
+ * Create or select an app and copy the App ID
+
+
+ Both the Smart Wallets configuration and the gas sponsorship policy must be
+ linked to the application behind your Alchemy API key for sponsorship to work.
+
+
+ ### 1. Add AlchemyProvider
+
+ ```tsx
+ import { PrivyProvider } from "@privy-io/react-auth";
+ import { AlchemyProvider } from "@account-kit/privy-integration";
+
+ function App() {
+ return (
+
+
+
+
+
+ );
+ }
+ ```
+
+ Props for `AlchemyProvider`:
+
+ * `apiKey` | `jwt` | `rpcUrl` (pick a valid transport config)
+ * `policyId` (optional) for EVM gas sponsorship
+ * `solanaPolicyId` (optional) for Solana gas sponsorship
+ * `solanaRpcUrl` (optional) for Solana RPC URL (separate from EVM)
+ * `disableSponsorship` (optional) to opt-out per default
+ * `accountAuthMode` (optional) - `'eip7702'` (default, recommended) or `'owner'` for a traditional smart account
+ * `walletAddress` (optional) - specify which wallet address to use if user has multiple wallets (defaults to first wallet)
+
+ ### 2. Send Gasless Transactions
+
+ ```tsx
+ import { useAlchemySendTransaction } from "@account-kit/privy-integration";
+
+ function SendButtons() {
+ const { sendTransaction, isLoading } = useAlchemySendTransaction();
+
+ const single = async () =>
+ await sendTransaction({ to: "0x...", data: "0x...", value: "0x0" });
+
+ const batch = async () =>
+ await sendTransaction([
+ { to: "0x...", data: "0x..." },
+ { to: "0x...", data: "0x..." },
+ ]);
+
+ return (
+ <>
+ Send
+ Send Batch
+ >
+ );
+ }
+ ```
+
+ Control sponsorship per call:
+
+ ```tsx
+ await sendTransaction({ to: "0x...", data: "0x..." });
+ await sendTransaction({ to: "0x...", data: "0x..." }, { disableSponsorship: true });
+ ```
+
+ ### 3. Execute Token Swaps
+
+ ```tsx
+ import { useAlchemyPrepareSwap, useAlchemySubmitSwap } from "@account-kit/privy-integration";
+
+ function SwapButton() {
+ const { prepareSwap } = useAlchemyPrepareSwap();
+ const { submitSwap, isLoading } = useAlchemySubmitSwap();
+
+ const handleSwap = async () => {
+ const prepared = await prepareSwap({
+ fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
+ toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum mainnet
+ fromAmount: "0xde0b6b3a7640000",
+ });
+ await submitSwap(prepared);
+ };
+
+ return Swap;
+ }
+ ```
+
+ ### 4. Send Solana Transactions
+
+ ```tsx
+ import { useAlchemySolanaTransaction } from "@account-kit/privy-integration/solana";
+
+ function SolanaButton() {
+ const { sendTransactionAsync, isPending } = useAlchemySolanaTransaction({
+ rpcUrl: "https://solana-mainnet.g.alchemy.com/v2/your-api-key",
+ policyId: "your-solana-policy-id", // optional
+ });
+
+ const handleTransfer = async () => {
+ const result = await sendTransactionAsync({
+ transfer: {
+ amount: 1_000_000_000, // 1 SOL in lamports
+ toAddress: "recipient-base58-address",
+ },
+ });
+ console.log("Transaction hash:", result.hash);
+ };
+
+ return Send SOL;
+ }
+ ```
+
+ ### Advanced: Access the Smart Wallet Client
+
+ ```tsx
+ import { useAlchemyClient } from "@account-kit/privy-integration";
+
+ function Advanced() {
+ const { getClient } = useAlchemyClient();
+
+ const doOp = async () => {
+ const { client, account } = await getClient();
+ await client.sendCalls({
+ from: account.address,
+ calls: [{ to: "0x...", data: "0x" }],
+ capabilities: { eip7702Auth: true, paymasterService: { policyId: "your-policy-id" } },
+ });
+ };
+
+ return Advanced Op;
+ }
+ ```
+
+ ### Authorization Modes
+
+ The integration supports two modes via `accountAuthMode`:
+
+ * **`'eip7702'` (default, recommended)**: Uses EIP-7702 to delegate the Privy wallet to a smart account. No deployment needed, funds stay at the wallet address.
+ * **`'owner'`**: Uses a traditional smart account with Privy wallet as owner. The smart account has a separate address. Use for environments without EIP-7702 support.
+
+ ```tsx
+ // Default (EIP-7702) - recommended
+
+
+
+
+ // Traditional smart account mode
+
+
+
+ ```
+
+ **Getting the Smart Account Address (owner mode):**
+
+ When using `owner` mode, access the smart account address (different from the Privy signer address):
+
+ ```tsx
+ import { useAlchemyClient } from "@account-kit/privy-integration";
+
+ function MyComponent() {
+ const { getClient } = useAlchemyClient();
+
+ const getAddress = async () => {
+ const { account } = await getClient();
+ console.log("Smart account:", account.address);
+ // Different from Privy signer address in owner mode
+ };
+ }
+ ```
+
+ ### Notes
+
+ * **EVM**: EIP-7702 (default) delegates your wallet to a smart account at send time. Alternatively, use `accountAuthMode="owner"` for a traditional smart account.
+ * **Solana**: Gas sponsorship via Alchemy's fee payer service for Privy's embedded Solana wallets.
+ * The hooks API mirrors Privy's transaction hooks for easy migration.
+
+
+
+ ## Setup
+
+ Follow these steps to integrate Privy with Smart Wallets in React Native using Expo.
+
+ ### Installation
+
+ ```bash
+ npm install @account-kit/privy-integration @privy-io/expo
+ # or
+ yarn add @account-kit/privy-integration @privy-io/expo
+ # or
+ pnpm add @account-kit/privy-integration @privy-io/expo
+ ```
+
+ ### Prerequisites: Get your keys (API key, Policy ID, Privy App ID & Client ID)
+
+ * Alchemy API Key:
+ * Go to the [Alchemy Dashboard](https://dashboard.alchemy.com/)
+ * Create or select an app and copy the API key
+ * Gas sponsorship Policy ID (Gas Manager):
+ * Create a gas sponsorship policy in the [dashboard](https://dashboard.alchemy.com/services/gas-manager/configuration) and copy its Policy ID
+ * Privy App ID & Client ID:
+ * Go to the [Privy Dashboard](https://dashboard.privy.io/)
+ * Create or select an app and copy both the App ID and Client ID (needed for Expo)
+
+
+ Both the Smart Wallets configuration and the gas sponsorship policy must be
+ linked to the application behind your Alchemy API key for sponsorship to work.
+
+
+ ### 1. Add AlchemyProvider
+
+ **Important:** Import from `@account-kit/privy-integration/react-native` for React Native apps.
+
+ ```tsx
+ import { PrivyProvider } from "@privy-io/expo";
+ import { AlchemyProvider } from "@account-kit/privy-integration/react-native";
+
+ function App() {
+ return (
+
+
+
+
+
+ );
+ }
+ ```
+
+ Props for `AlchemyProvider`:
+
+ * `apiKey` | `jwt` | `rpcUrl` (pick a valid transport config)
+ * `policyId` (optional) for EVM gas sponsorship
+ * `disableSponsorship` (optional) to opt-out by default
+ * `accountAuthMode` (optional) - `'eip7702'` (default, recommended) or `'owner'` for a traditional smart account
+ * `walletAddress` (optional) - specify which wallet address to use if user has multiple wallets (defaults to first wallet)
+
+ ### 2. Send Gasless Transactions
+
+ ```tsx
+ import { useAlchemySendTransaction } from "@account-kit/privy-integration/react-native";
+
+ function SendButtons() {
+ const { sendTransaction, isLoading } = useAlchemySendTransaction();
+
+ const single = async () =>
+ await sendTransaction({ to: "0x...", data: "0x...", value: "0x0" });
+
+ const batch = async () =>
+ await sendTransaction([
+ { to: "0x...", data: "0x..." },
+ { to: "0x...", data: "0x..." },
+ ]);
+
+ return (
+ <>
+
+
+ >
+ );
+ }
+ ```
+
+ Control sponsorship per call:
+
+ ```tsx
+ await sendTransaction({ to: "0x...", data: "0x..." });
+ await sendTransaction({ to: "0x...", data: "0x..." }, { disableSponsorship: true });
+ ```
+
+ ### 3. Execute Token Swaps
+
+ ```tsx
+ import { useAlchemyPrepareSwap, useAlchemySubmitSwap } from "@account-kit/privy-integration/react-native";
+
+ function SwapButton() {
+ const { prepareSwap } = useAlchemyPrepareSwap();
+ const { submitSwap, isLoading } = useAlchemySubmitSwap();
+
+ const handleSwap = async () => {
+ const prepared = await prepareSwap({
+ fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
+ toToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC on Ethereum mainnet
+ fromAmount: "0xde0b6b3a7640000",
+ });
+ await submitSwap(prepared);
+ };
+
+ return ;
+ }
+ ```
+
+ ### 4. Advanced: Access the Smart Wallet Client
+
+ ```tsx
+ import { useAlchemyClient } from "@account-kit/privy-integration/react-native";
+
+ function Advanced() {
+ const { getClient } = useAlchemyClient();
+
+ const doOp = async () => {
+ const { client, account } = await getClient();
+ await client.sendCalls({
+ from: account.address,
+ calls: [{ to: "0x...", data: "0x" }],
+ capabilities: { eip7702Auth: true, paymasterService: { policyId: "your-policy-id" } },
+ });
+ };
+
+ return ;
+ }
+ ```
+
+ ### Authorization Modes
+
+ The integration supports two modes via `accountAuthMode`:
+
+ * **`'eip7702'` (default, recommended)**: Uses EIP-7702 to delegate the Privy wallet to a smart account. No deployment needed, funds stay at the wallet address.
+ * **`'owner'`**: Uses a traditional smart account with Privy wallet as owner. The smart account has a separate address. Use for environments without EIP-7702 support.
+
+ ```tsx
+ // Default (EIP-7702) - recommended
+
+
+
+
+ // Traditional smart account mode
+
+
+
+ ```
+
+ **Getting the Smart Account Address (owner mode):**
+
+ When using `owner` mode, access the smart account address (different from the Privy signer address):
+
+ ```tsx
+ import { useAlchemyClient } from "@account-kit/privy-integration/react-native";
+
+ function MyComponent() {
+ const { getClient } = useAlchemyClient();
+
+ const getAddress = async () => {
+ const { account } = await getClient();
+ console.log("Smart account:", account.address);
+ // Different from Privy signer address in owner mode
+ };
+ }
+ ```
+
+ ### Multiple Wallet Support
+
+ If your users have multiple Privy wallets, specify which one to use:
+
+ ```tsx
+
+
+
+ ```
+
+ ### Notes
+
+ * **Platform**: Uses `@privy-io/expo` for React Native authentication
+ * **EVM Only**: React Native integration currently supports EVM chains (Solana support coming soon)
+ * **EIP-7702**: Delegates your wallet to a smart account at send time. Alternatively, use `accountAuthMode="owner"` for a traditional smart account.
+ * The hooks API mirrors the React web version for easy cross-platform development.
+
+
+
+ ## Setup
+
+ Use the `@privy-io/node` package with Smart Wallets infrastructure in a server environment.
+
+ ### Installation
+
+ ```bash
+ npm install @privy-io/node @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
+ # or
+ yarn add @privy-io/node @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
+ # or
+ pnpm add @privy-io/node @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
+ ```
+
+ ### Prerequisites: Get your keys
+
+ * API Key:
+ * Go to your service dashboard ([link](https://dashboard.alchemy.com/))
+ * Create or select an app and copy the API key
+ * Gas sponsorship Policy ID (Gas Manager):
+ * Create a gas sponsorship policy in your dashboard ([link](https://dashboard.alchemy.com/services/gas-manager/configuration)) and copy its Policy ID
+ * App ID & Secret:
+ * Go to your authentication provider dashboard ([link](https://dashboard.privy.io/))
+ * Create or select an app and copy the App ID and App Secret
+
+
+ Both the Smart Wallets configuration and the gas sponsorship policy must be
+ linked to the application associated with your API key for sponsorship to work.
+
+
+ ### Create Smart Wallet Client from Privy
+
+ Create a helper function that converts a Privy wallet into an Alchemy Smart Wallet client:
+
+ ```ts twoslash
+ import { PrivyClient } from "@privy-io/node";
+ import { createViemAccount } from "@privy-io/node/viem";
+ import { createWalletClient } from "viem";
+ import { WalletClientSigner } from "@aa-sdk/core";
+ import { baseSepolia, alchemy } from "@account-kit/infra";
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+ const ALCHEMY_API_KEY = "your-alchemy-api-key";
+ const ALCHEMY_GAS_POLICY_ID = "your-gas-policy-id";
+
+ async function createPrivySmartWalletClient({
+ privyClient,
+ walletId,
+ walletAddress,
+ }) {
+ // Create a viem account from Privy's wallet
+ const viemAccount = createViemAccount(privyClient, {
+ walletId,
+ address: walletAddress,
+ });
+
+ // Create a viem wallet client
+ const walletClient = createWalletClient({
+ account: viemAccount,
+ chain: baseSepolia,
+ transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
+ });
+
+ // Wrap it in a WalletClientSigner for Account Kit
+ const signer = new WalletClientSigner(walletClient, "privy-wallet");
+
+ // Create and return the Smart Wallet client
+ return createSmartWalletClient({
+ chain: baseSepolia,
+ transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
+ signer,
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ });
+ }
+ ```
+
+ ### Send Transactions with EIP-7702
+
+ Send gasless transactions using EIP-7702 to upgrade the Privy wallet to a smart wallet:
+
+ ```ts twoslash
+ import { PrivyClient } from "@privy-io/node";
+
+ const PRIVY_APP_ID = "your-privy-app-id";
+ const PRIVY_APP_SECRET = "your-privy-app-secret";
+
+ async function sendTransaction() {
+ // Initialize Privy client
+ const privyClient = new PrivyClient({
+ appId: PRIVY_APP_ID,
+ appSecret: PRIVY_APP_SECRET,
+ });
+
+ // Create or get a Privy wallet
+ const wallet = await privyClient.wallets().create({
+ chain_type: "ethereum",
+ });
+
+ console.log("Wallet address:", wallet.address);
+
+ // Create Smart Wallet client from Privy wallet
+ const client = await createPrivySmartWalletClient({
+ privyClient,
+ walletId: wallet.id,
+ walletAddress: wallet.address,
+ });
+
+ // Send a gasless transaction with EIP-7702
+ const result = await client.sendCalls({
+ from: wallet.address,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x0",
+ data: "0x",
+ },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ },
+ },
+ });
+
+ console.log("Transaction sent:", result.preparedCallIds[0]);
+ }
+ ```
+
+ ### Wait for Transaction Confirmation
+
+ Wait for the transaction to be confirmed onchain:
+
+ ```ts twoslash
+ async function sendAndWaitForTransaction() {
+ // ... (setup code from above)
+
+ const result = await client.sendCalls({
+ from: wallet.address,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x0",
+ data: "0x",
+ },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ },
+ },
+ });
+
+ console.log("Waiting for confirmation...");
+
+ // Wait for the transaction to be confirmed
+ const txStatus = await client.waitForCallsStatus({
+ id: result.preparedCallIds[0],
+ timeout: 60_000, // 60 seconds
+ });
+
+ const txnHash = txStatus.receipts?.[0]?.transactionHash;
+
+ console.log("Transaction confirmed!");
+ console.log("Transaction hash:", txnHash);
+ }
+ ```
+
+ ### Batch Multiple Transactions
+
+ Send multiple transactions in a single batch:
+
+ ```ts twoslash
+ const result = await client.sendCalls({
+ from: wallet.address,
+ calls: [
+ { to: "0x...", data: "0x...", value: "0x0" },
+ { to: "0x...", data: "0x...", value: "0x0" },
+ { to: "0x...", data: "0x...", value: "0x0" },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ },
+ },
+ });
+ ```
+
+ ### Notes
+
+ * **EIP-7702**: The examples above use EIP-7702 to upgrade the wallet to a smart wallet at transaction time without migration or separate deployment
+ * **Gas Sponsorship**: Use `paymasterService` with your policy ID for gasless transactions
+ * **Traditional Smart Accounts**: To use traditional smart accounts instead of EIP-7702, omit the `eip7702Auth` capability and request an account with `await client.requestAccount({ creationHint: { accountType: "sma-b" } })`
+ * **Server-Side**: This example uses Privy's `@privy-io/node` package which is designed for server environments. However, the `@account-kit` packages (`@account-kit/wallet-client`, `@account-kit/infra`, etc.) can be used in any JavaScript environment including browsers, React Native, and Node.js
+
+
diff --git a/fern/wallets/pages/third-party/signers/turnkey.mdx b/fern/wallets/pages/third-party/signers/turnkey.mdx
new file mode 100644
index 000000000..6130d3710
--- /dev/null
+++ b/fern/wallets/pages/third-party/signers/turnkey.mdx
@@ -0,0 +1,240 @@
+---
+title: Turnkey
+description: Use Turnkey with Smart Wallets for EIP-7702, sponsorship, and batching
+slug: wallets/third-party/signers/turnkey
+---
+
+Upgrade existing Turnkey wallets to Smart Wallets to enable gasless transactions, batching, and more in under 10 minutes. Keep Turnkey for key management, no wallet migration needed. Add battle-tested transaction infrastructure using EIP-7702 to upgrade EOAs to Smart Wallets:
+
+* [#1 gas abstraction infrastructure](https://www.bundlebear.com/erc4337-bundlers/all) on the market
+* [370M+](https://www.bundlebear.com/erc4337-paymasters/all) sponsored transactions
+* 99.9% SLAs
+* Trusted by Worldcoin, JP Morgan, Gensyn, and more
+
+
+ Don't have a wallet set up yet? Start with the
+ Smart Wallet Quickstart to create one in
+ minutes.
+
+
+
+
+ ## Setup
+
+ Use Turnkey's `@turnkey/sdk-server` package with Smart Wallets infrastructure in a server environment.
+
+ ### Installation
+
+ ```bash
+ npm install @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
+ # or
+ yarn add @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
+ # or
+ pnpm add @turnkey/sdk-server @turnkey/viem @account-kit/wallet-client @account-kit/infra @aa-sdk/core viem
+ ```
+
+ ### Prerequisites: Get your keys
+
+ * Alchemy API Key:
+ * Go to the [dashboard](https://dashboard.alchemy.com/)
+ * Create or select an app and copy the API key
+ * Gas sponsorship Policy ID:
+ * Create a gas sponsorship policy in the [dashboard](https://dashboard.alchemy.com/services/gas-manager/configuration) and copy its Policy ID
+ * Turnkey Credentials:
+ * Go to the [Turnkey Dashboard](https://app.turnkey.com/)
+ * Create or select an organization and copy the Organization ID
+ * Create an API key pair and copy the API Public Key and Private Key
+ * Create or import a private key and copy the Private Key ID (UUID)
+ * Note the Ethereum address associated with that private key
+
+
+ Both the Smart Wallets configuration and the gas sponsorship policy must be
+ linked to the application behind your API key for sponsorship to work.
+
+
+ ### Create Smart Wallet Client from Turnkey
+
+ Create a helper function that converts a Turnkey wallet into an Alchemy Smart Wallet client:
+
+ ```ts twoslash
+ import { WalletClientSigner } from "@aa-sdk/core";
+ import { alchemy, baseSepolia } from "@account-kit/infra";
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+ import { Turnkey } from "@turnkey/sdk-server";
+ import { createAccount } from "@turnkey/viem";
+ import { createWalletClient } from "viem";
+
+ const ALCHEMY_API_KEY = "your-alchemy-api-key";
+ const ALCHEMY_GAS_POLICY_ID = "your-gas-policy-id";
+ const TURNKEY_ORGANIZATION_ID = "your-turnkey-org-id";
+ const TURNKEY_API_PRIVATE_KEY = "your-turnkey-api-private-key";
+ const TURNKEY_API_PUBLIC_KEY = "your-turnkey-api-public-key";
+
+ async function createTurnkeySmartWalletClient({
+ privateKeyId,
+ walletAddress,
+ }) {
+ // Initialize Turnkey SDK
+ const turnkey = new Turnkey({
+ defaultOrganizationId: TURNKEY_ORGANIZATION_ID,
+ apiBaseUrl: "https://api.turnkey.com",
+ apiPrivateKey: TURNKEY_API_PRIVATE_KEY,
+ apiPublicKey: TURNKEY_API_PUBLIC_KEY,
+ });
+
+ // Create a viem account using Turnkey's signing capabilities
+ const baseTurnkeyAccount = await createAccount({
+ client: turnkey.apiClient(),
+ organizationId: TURNKEY_ORGANIZATION_ID,
+ signWith: privateKeyId,
+ ethereumAddress: walletAddress,
+ });
+
+ // Wrap the account to fix the double 0x prefix bug in @turnkey/viem 0.14.8
+ const turnkeyAccount = {
+ ...baseTurnkeyAccount,
+ signAuthorization: async (authorization) => {
+ if (!baseTurnkeyAccount.signAuthorization) {
+ throw new Error("signAuthorization not available on Turnkey account");
+ }
+ const result = await baseTurnkeyAccount.signAuthorization(authorization);
+
+ // Strip the extra 0x prefix if present
+ return {
+ ...result,
+ r: result.r.replace(/^0x0x/, "0x"),
+ s: result.s.replace(/^0x0x/, "0x"),
+ };
+ },
+ };
+
+ // Create wallet client with Turnkey account
+ const walletClient = createWalletClient({
+ account: turnkeyAccount,
+ chain: baseSepolia,
+ transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
+ });
+
+ // Create signer from wallet client
+ const signer = new WalletClientSigner(walletClient, "turnkey-custom");
+
+ // Create and return the Smart Wallet client
+ return createSmartWalletClient({
+ chain: baseSepolia,
+ transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
+ signer,
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ });
+ }
+ ```
+
+ ### Send Transactions with EIP-7702
+
+ Send gasless transactions using EIP-7702 to upgrade the Turnkey wallet to a smart wallet:
+
+ ```ts twoslash
+ const TURNKEY_PRIVATE_KEY_ID = "your-private-key-uuid";
+ const TURNKEY_WALLET_ADDRESS = "0x...";
+
+ async function sendTransaction() {
+ console.log("Wallet address:", TURNKEY_WALLET_ADDRESS);
+
+ // Create Smart Wallet client from Turnkey wallet
+ const client = await createTurnkeySmartWalletClient({
+ privateKeyId: TURNKEY_PRIVATE_KEY_ID,
+ walletAddress: TURNKEY_WALLET_ADDRESS,
+ });
+
+ // Send a gasless transaction with EIP-7702
+ const result = await client.sendCalls({
+ from: TURNKEY_WALLET_ADDRESS,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x0",
+ data: "0x",
+ },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ },
+ },
+ });
+
+ console.log("Transaction sent:", result.preparedCallIds[0]);
+ }
+ ```
+
+ ### Wait for Transaction Confirmation
+
+ Wait for the transaction to be confirmed onchain:
+
+ ```ts twoslash
+ async function sendAndWaitForTransaction() {
+ const client = await createTurnkeySmartWalletClient({
+ privateKeyId: TURNKEY_PRIVATE_KEY_ID,
+ walletAddress: TURNKEY_WALLET_ADDRESS,
+ });
+
+ const result = await client.sendCalls({
+ from: TURNKEY_WALLET_ADDRESS,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x0",
+ data: "0x",
+ },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ },
+ },
+ });
+
+ console.log("Waiting for confirmation...");
+
+ // Wait for the transaction to be confirmed
+ const txStatus = await client.waitForCallsStatus({
+ id: result.preparedCallIds[0],
+ timeout: 60_000, // 60 seconds
+ });
+
+ const txnHash = txStatus.receipts?.[0]?.transactionHash;
+
+ console.log("Transaction confirmed!");
+ console.log("Transaction hash:", txnHash);
+ }
+ ```
+
+ ### Batch Multiple Transactions
+
+ Send multiple transactions in a single batch:
+
+ ```ts twoslash
+ const result = await client.sendCalls({
+ from: TURNKEY_WALLET_ADDRESS,
+ calls: [
+ { to: "0x...", data: "0x...", value: "0x0" },
+ { to: "0x...", data: "0x...", value: "0x0" },
+ { to: "0x...", data: "0x...", value: "0x0" },
+ ],
+ capabilities: {
+ eip7702Auth: true,
+ paymasterService: {
+ policyId: ALCHEMY_GAS_POLICY_ID,
+ },
+ },
+ });
+ ```
+
+ ### Notes
+
+ * **EIP-7702**: Upgrades the Turnkey wallet to a smart wallet at transaction time without migration
+ * **Gas Sponsorship**: Use `paymasterService` with your policy ID for gasless transactions
+ * **Server-Side**: This example uses Turnkey's `@turnkey/sdk-server` package which is designed for server environments. However, the `@account-kit` packages (`@account-kit/wallet-client`, `@account-kit/infra`, etc.) can be used in any JavaScript environment including browsers, React Native, and Node.js
+
+
diff --git a/fern/wallets/pages/third-party/smart-contracts.mdx b/fern/wallets/pages/third-party/smart-contracts.mdx
new file mode 100644
index 000000000..54d464c36
--- /dev/null
+++ b/fern/wallets/pages/third-party/smart-contracts.mdx
@@ -0,0 +1,885 @@
+---
+title: 3rd Party Smart Contracts
+description: Learn how to use Smart Contract Accounts not included in Smart Wallets
+slug: wallets/third-party/smart-contracts
+---
+
+You are not limited to the accounts defined in `@account-kit/smart-contracts`.
+If you'd like to bring your own smart account, you have a couple options:
+
+1. Use a 3rd party library that exports an `aa-sdk` compatible `SmartContractAccount` interface.
+2. Implement your own `SmartContractAccount` interface using `toSmartContractAccount`.
+
+## Third Party SDKs
+
+
+ If you've built an SDK or Guide that leverages any of the methods above to use as a Smart Contract within Smart Wallets, we're happy to include you in this list!
+
+ Please open a PR to add a link to your content in this section.
+
+
+## Using `toSmartContractAccount`
+
+The `SmartAccountClient` can be used with any smart account because it only relies on the `SmartContractAccount` interface. This means you can use your own smart account implementation with Smart Wallets.
+
+```ts my-account.ts twoslash
+import { getEntryPoint, toSmartContractAccount } from "@aa-sdk/core";
+import { http, type SignableMessage, type Hash } from "viem";
+import { sepolia } from "viem/chains";
+
+const myAccount = await toSmartContractAccount({
+ /// REQUIRED PARAMS ///
+ source: "MyAccount",
+ transport: http("RPC_URL"),
+ chain: sepolia,
+ // The EntryPointDef that your account is compatible with
+ entryPoint: getEntryPoint(sepolia),
+ // This should return a concatenation of your `factoryAddress` and the `callData` for your factory's create account method
+ getAccountInitCode: async (): Promise => "0x{factoryAddress}{callData}",
+ // an invalid signature that doesn't cause your account to revert during validation
+ getDummySignature: async (): Promise => "0x1234...",
+ // given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
+ encodeExecute: async (uo): Promise => "0x....",
+ signMessage: async ({ message }): Promise => "0x...",
+ signTypedData: async (typedData): Promise => "0x000",
+
+ /// OPTIONAL PARAMS ///
+ // if you already know your account's address, pass that in here to avoid generating a new counterfactual
+ accountAddress: "0x...",
+ // if your account supports batching, this should take an array of UOs and return the calldata for calling your contract's batchExecute method
+ encodeBatchExecute: async (uos): Promise => "0x...",
+ // if your contract expects a different signing scheme than the default signMessage scheme, you can override that here
+ signUserOperationHash: async (hash): Promise => "0x...",
+ // allows you to define the calldata for upgrading your account
+ encodeUpgradeToAndCall: async (params): Promise => "0x...",
+});
+```
+
+To use your account, you will need to pass it into a `SmartAccountClient`.
+
+
+ ```ts example.ts
+ import { createAlchemySmartAccountClient, alchemy } from "@account-kit/infra";
+ import { sepolia } from "viem/chains";
+ import { myAccount } from "./my-account";
+
+ const client = createAlchemySmartAccountClient({
+ // created above
+ account: myAccount,
+ chain: sepolia,
+ transport: alchemy({
+ apiKey: "YOUR_API_KEY",
+ }),
+ });
+ ```
+
+ ```ts my-account.ts filename="my-account.ts"
+ import { getEntryPoint, toSmartContractAccount } from "@aa-sdk/core";
+ import { http, type SignableMessage, type Hash } from "viem";
+ import { sepolia } from "viem/chains";
+
+ export const myAccount = await toSmartContractAccount({
+ /// REQUIRED PARAMS ///
+ source: "MyAccount",
+ transport: http("RPC_URL"),
+ chain: sepolia,
+ // The EntryPointDef that your account is compatible with
+ entryPoint: getEntryPoint(sepolia),
+ // This should return a concatenation of your `factoryAddress` and the `callData` for your factory's create account method
+ getAccountInitCode: async (): Promise => "0x{factoryAddress}{callData}",
+ // an invalid signature that doesn't cause your account to revert during validation
+ getDummySignature: async (): Promise => "0x1234...",
+ // given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
+ encodeExecute: async (uo): Promise => "0x....",
+ signMessage: async ({ message }): Promise => "0x...",
+ signTypedData: async (typedData): Promise => "0x000",
+
+ /// OPTIONAL PARAMS ///
+ // if you already know your account's address, pass that in here to avoid generating a new counterfactual
+ accountAddress: "0x...",
+ // if your account supports batching, this should take an array of UOs and return the calldata for calling your contract's batchExecute method
+ encodeBatchExecute: async (uos): Promise => "0x...",
+ // if your contract expects a different signing scheme than the default signMessage scheme, you can override that here
+ signUserOperationHash: async (hash): Promise => "0x...",
+ // allows you to define the calldata for upgrading your account
+ encodeUpgradeToAndCall: async (params): Promise => "0x...",
+ });
+ ```
+
+
+### `LightSmartContractAccount` as an Example
+
+We have built an extension of the eth-infinitism `SimpleAccount` called [LightAccount.sol](https://github.com/alchemyplatform/light-account/blob/main/src/LightAccount.sol). You can learn more about Light Account in the [Light Account documentation](/wallets/smart-contracts/other-accounts/light-account/).
+
+We provide an implementation of `SmartContractAccount` that works with `LightAccount.sol`, which can be used as an example of how to implement your own Smart Contract Account:
+
+
+ ````ts
+ import {
+ createBundlerClient,
+ getEntryPoint,
+ type Address,
+ type EntryPointDef,
+ type SmartAccountSigner,
+ } from "@aa-sdk/core";
+ import {
+ concatHex,
+ encodeFunctionData,
+ type Chain,
+ type Hex,
+ type Transport,
+ } from "viem";
+ import { LightAccountAbi_v1 } from "../abis/LightAccountAbi_v1.js";
+ import { LightAccountAbi_v2 } from "../abis/LightAccountAbi_v2.js";
+ import { LightAccountFactoryAbi_v1 } from "../abis/LightAccountFactoryAbi_v1.js";
+ import { LightAccountFactoryAbi_v2 } from "../abis/LightAccountFactoryAbi_v2.js";
+ import type {
+ LightAccountEntryPointVersion,
+ LightAccountVersion,
+ } from "../types.js";
+ import {
+ AccountVersionRegistry,
+ LightAccountUnsupported1271Factories,
+ defaultLightAccountVersion,
+ getDefaultLightAccountFactoryAddress,
+ } from "../utils.js";
+ import {
+ createLightAccountBase,
+ type CreateLightAccountBaseParams,
+ type LightAccountBase,
+ } from "./base.js";
+ import { predictLightAccountAddress } from "./predictAddress.js";
+
+ export type LightAccount<
+ TSigner extends SmartAccountSigner = SmartAccountSigner,
+ TLightAccountVersion extends
+ LightAccountVersion<"LightAccount"> = LightAccountVersion<"LightAccount">,
+ > = LightAccountBase & {
+ encodeTransferOwnership: (newOwner: Address) => Hex;
+ getOwnerAddress: () => Promise;
+ };
+
+ export type CreateLightAccountParams<
+ TTransport extends Transport = Transport,
+ TSigner extends SmartAccountSigner = SmartAccountSigner,
+ TLightAccountVersion extends
+ LightAccountVersion<"LightAccount"> = LightAccountVersion<"LightAccount">,
+ > = Omit<
+ CreateLightAccountBaseParams<
+ "LightAccount",
+ TLightAccountVersion,
+ TTransport,
+ TSigner
+ >,
+ | "getAccountInitCode"
+ | "entryPoint"
+ | "version"
+ | "abi"
+ | "accountAddress"
+ | "type"
+ > & {
+ salt?: bigint;
+ initCode?: Hex;
+ accountAddress?: Address;
+ factoryAddress?: Address;
+ version?: TLightAccountVersion;
+ entryPoint?: EntryPointDef<
+ LightAccountEntryPointVersion<"LightAccount", TLightAccountVersion>,
+ Chain
+ >;
+ };
+
+ export async function createLightAccount<
+ TTransport extends Transport = Transport,
+ TSigner extends SmartAccountSigner = SmartAccountSigner,
+ TLightAccountVersion extends LightAccountVersion<"LightAccount"> = "v2.0.0",
+ >(
+ config: CreateLightAccountParams,
+ ): Promise>;
+
+ /**
+ * Creates a light account based on the provided parameters such as transport, chain, signer, init code, and more. Ensures that an account is configured and returned with various capabilities, such as transferring ownership and retrieving the owner's address.
+ *
+ * @example
+ * ```ts
+ * import { createLightAccount } from "@account-kit/smart-contracts";
+ * import { LocalAccountSigner } from "@aa-sdk/core";
+ * import { sepolia } from "viem/chains";
+ * import { http, generatePrivateKey } from "viem"
+ *
+ * const account = await createLightAccount({
+ * chain: sepolia,
+ * transport: http("RPC_URL"),
+ * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey())
+ * });
+ * ```
+ *
+ * @param {CreateLightAccountParams} config The parameters for creating a light account
+ * @returns {Promise} A promise that resolves to a `LightAccount` object containing the created account information and methods
+ */
+ export async function createLightAccount({
+ transport,
+ chain,
+ signer,
+ initCode,
+ version = defaultLightAccountVersion(),
+ entryPoint = getEntryPoint(chain, {
+ version: AccountVersionRegistry["LightAccount"][version]
+ .entryPointVersion as any,
+ }),
+ accountAddress,
+ factoryAddress = getDefaultLightAccountFactoryAddress(chain, version),
+ salt: salt_ = 0n,
+ }: CreateLightAccountParams): Promise {
+ const client = createBundlerClient({
+ transport,
+ chain,
+ });
+
+ const accountAbi =
+ version === "v2.0.0" ? LightAccountAbi_v2 : LightAccountAbi_v1;
+ const factoryAbi =
+ version === "v2.0.0"
+ ? LightAccountFactoryAbi_v1
+ : LightAccountFactoryAbi_v2;
+
+ const signerAddress = await signer.getAddress();
+
+ const salt = LightAccountUnsupported1271Factories.has(
+ factoryAddress.toLowerCase() as Address,
+ )
+ ? 0n
+ : salt_;
+
+ const getAccountInitCode = async () => {
+ if (initCode) return initCode;
+
+ return concatHex([
+ factoryAddress,
+ encodeFunctionData({
+ abi: factoryAbi,
+ functionName: "createAccount",
+ args: [signerAddress, salt],
+ }),
+ ]);
+ };
+
+ const address =
+ accountAddress ??
+ predictLightAccountAddress({
+ factoryAddress,
+ salt,
+ ownerAddress: signerAddress,
+ version,
+ });
+
+ const account = await createLightAccountBase<
+ "LightAccount",
+ LightAccountVersion<"LightAccount">,
+ Transport,
+ SmartAccountSigner
+ >({
+ transport,
+ chain,
+ signer,
+ abi: accountAbi,
+ type: "LightAccount",
+ version,
+ entryPoint,
+ accountAddress: address,
+ getAccountInitCode,
+ });
+
+ return {
+ ...account,
+
+ encodeTransferOwnership: (newOwner: Address) => {
+ return encodeFunctionData({
+ abi: accountAbi,
+ functionName: "transferOwnership",
+ args: [newOwner],
+ });
+ },
+ async getOwnerAddress(): Promise {
+ const callResult = await client.readContract({
+ address,
+ abi: accountAbi,
+ functionName: "owner",
+ });
+
+ if (callResult == null) {
+ throw new Error("could not get on-chain owner");
+ }
+
+ return callResult;
+ },
+ };
+ }
+ ````
+
+
+### The `toSmartContractAccount` Method
+
+For your reference, this is the definition of the `toSmartContractAccount` interface as pulled from the source code:
+
+
+ ````ts
+ import {
+ getContract,
+ hexToBytes,
+ type Address,
+ type Chain,
+ type CustomSource,
+ type Hex,
+ type LocalAccount,
+ type PublicClient,
+ type SignableMessage,
+ type Transport,
+ type TypedData,
+ type TypedDataDefinition,
+ } from "viem";
+ import { toAccount } from "viem/accounts";
+ import { createBundlerClient } from "../client/bundlerClient.js";
+ import type {
+ EntryPointDef,
+ EntryPointRegistryBase,
+ EntryPointVersion,
+ } from "../entrypoint/types.js";
+ import {
+ BatchExecutionNotSupportedError,
+ FailedToGetStorageSlotError,
+ GetCounterFactualAddressError,
+ SignTransactionNotSupportedError,
+ UpgradesNotSupportedError,
+ } from "../errors/account.js";
+ import { InvalidRpcUrlError } from "../errors/client.js";
+ import { InvalidEntryPointError } from "../errors/entrypoint.js";
+ import { Logger } from "../logger.js";
+ import type { SmartAccountSigner } from "../signer/types.js";
+ import { wrapSignatureWith6492 } from "../signer/utils.js";
+ import type { NullAddress } from "../types.js";
+ import type { IsUndefined, Never } from "../utils/types.js";
+
+ export type AccountOp = {
+ target: Address;
+ value?: bigint;
+ data: Hex | "0x";
+ };
+
+ export enum DeploymentState {
+ UNDEFINED = "0x0",
+ NOT_DEPLOYED = "0x1",
+ DEPLOYED = "0x2",
+ }
+
+ export type SignatureRequest =
+ | {
+ type: "personal_sign";
+ data: SignableMessage;
+ }
+ | {
+ type: "eth_signTypedData_v4";
+ data: TypedDataDefinition;
+ };
+
+ export type SigningMethods = {
+ prepareSign: (request: SignatureRequest) => Promise;
+ formatSign: (signature: Hex) => Promise;
+ };
+
+ export type GetEntryPointFromAccount<
+ TAccount extends SmartContractAccount | undefined,
+ TAccountOverride extends SmartContractAccount = SmartContractAccount,
+ > =
+ GetAccountParameter extends SmartContractAccount<
+ string,
+ infer TEntryPointVersion
+ >
+ ? TEntryPointVersion
+ : EntryPointVersion;
+
+ export type GetAccountParameter<
+ TAccount extends SmartContractAccount | undefined =
+ | SmartContractAccount
+ | undefined,
+ TAccountOverride extends SmartContractAccount = SmartContractAccount,
+ > =
+ IsUndefined extends true
+ ? { account: TAccountOverride }
+ : { account?: TAccountOverride };
+
+ export type UpgradeToAndCallParams = {
+ upgradeToAddress: Address;
+ upgradeToInitData: Hex;
+ };
+
+ export type SmartContractAccountWithSigner<
+ Name extends string = string,
+ TSigner extends SmartAccountSigner = SmartAccountSigner,
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ > = SmartContractAccount & {
+ getSigner: () => TSigner;
+ };
+
+ /**
+ * Determines if the given SmartContractAccount has a signer associated with it.
+ *
+ * @example
+ * ```ts
+ * import { toSmartContractAccount } from "@aa-sdk/core";
+ *
+ * const account = await toSmartContractAccount(...);
+ *
+ * console.log(isSmartAccountWithSigner(account)); // false: the base account does not have a publicly accessible signer
+ * ```
+ *
+ * @param {SmartContractAccount} account The account to check.
+ * @returns {boolean} true if the account has a signer, otherwise false.
+ */
+ export const isSmartAccountWithSigner = (
+ account: SmartContractAccount,
+ ): account is SmartContractAccountWithSigner => {
+ return "getSigner" in account;
+ };
+
+ export type SmartContractAccount<
+ Name extends string = string,
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ > = LocalAccount & {
+ source: Name;
+ getDummySignature: () => Hex | Promise;
+ encodeExecute: (tx: AccountOp) => Promise;
+ encodeBatchExecute: (txs: AccountOp[]) => Promise;
+ signUserOperationHash: (uoHash: Hex) => Promise;
+ signMessageWith6492: (params: { message: SignableMessage }) => Promise;
+ signTypedDataWith6492: <
+ const typedData extends TypedData | Record,
+ primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
+ >(
+ typedDataDefinition: TypedDataDefinition,
+ ) => Promise;
+ encodeUpgradeToAndCall: (params: UpgradeToAndCallParams) => Promise;
+ getAccountNonce(nonceKey?: bigint): Promise;
+ getInitCode: () => Promise;
+ isAccountDeployed: () => Promise;
+ getFactoryAddress: () => Promise;
+ getFactoryData: () => Promise;
+ getEntryPoint: () => EntryPointDef;
+ getImplementationAddress: () => Promise;
+ } & SigningMethods;
+
+ export interface AccountEntryPointRegistry
+ extends EntryPointRegistryBase<
+ SmartContractAccount
+ > {
+ "0.6.0": SmartContractAccount;
+ "0.7.0": SmartContractAccount;
+ }
+
+ export type ToSmartContractAccountParams<
+ Name extends string = string,
+ TTransport extends Transport = Transport,
+ TChain extends Chain = Chain,
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ > = {
+ source: Name;
+ transport: TTransport;
+ chain: TChain;
+ entryPoint: EntryPointDef;
+ accountAddress?: Address;
+ getAccountInitCode: () => Promise;
+ getDummySignature: () => Hex | Promise;
+ encodeExecute: (tx: AccountOp) => Promise;
+ encodeBatchExecute?: (txs: AccountOp[]) => Promise;
+ getNonce?: (nonceKey?: bigint) => Promise;
+ // if not provided, will default to just using signMessage over the Hex
+ signUserOperationHash?: (uoHash: Hex) => Promise;
+ encodeUpgradeToAndCall?: (params: UpgradeToAndCallParams) => Promise;
+ getImplementationAddress?: () => Promise;
+ } & Omit &
+ (SigningMethods | Never);
+
+ /**
+ * Parses the factory address and factory calldata from the provided account initialization code (initCode).
+ *
+ * @example
+ * ```ts
+ * import { parseFactoryAddressFromAccountInitCode } from "@aa-sdk/core";
+ *
+ * const [address, calldata] = parseFactoryAddressFromAccountInitCode("0xAddressCalldata");
+ * ```
+ *
+ * @param {Hex} initCode The initialization code from which to parse the factory address and calldata
+ * @returns {[Address, Hex]} A tuple containing the parsed factory address and factory calldata
+ */
+ export const parseFactoryAddressFromAccountInitCode = (
+ initCode: Hex,
+ ): [Address, Hex] => {
+ const factoryAddress: Address = `0x${initCode.substring(2, 42)}`;
+ const factoryCalldata: Hex = `0x${initCode.substring(42)}`;
+ return [factoryAddress, factoryCalldata];
+ };
+
+ export type GetAccountAddressParams = {
+ client: PublicClient;
+ entryPoint: EntryPointDef;
+ accountAddress?: Address;
+ getAccountInitCode: () => Promise;
+ };
+
+ /**
+ * Retrieves the account address. Uses a provided `accountAddress` if available; otherwise, it computes the address using the entry point contract and the initial code.
+ *
+ * @example
+ * ```ts
+ * import { getEntryPoint, getAccountAddress } from "@aa-sdk/core";
+ *
+ * const accountAddress = await getAccountAddress({
+ * client,
+ * entryPoint: getEntryPoint(chain),
+ * getAccountInitCode: async () => "0x{factoryAddress}{factoryCallData}",
+ * });
+ * ```
+ *
+ * @param {GetAccountAddressParams} params The configuration object
+ * @param {PublicClient} params.client A public client instance to interact with the blockchain
+ * @param {EntryPointDef} params.entryPoint The entry point definition which includes the address and ABI
+ * @param {Address} params.accountAddress Optional existing account address
+ * @param {() => Promise} params.getAccountInitCode A function that returns a Promise resolving to a Hex string representing the initial code of the account
+ * @returns {Promise} A promise that resolves to the account address
+ */
+ export const getAccountAddress = async ({
+ client,
+ entryPoint,
+ accountAddress,
+ getAccountInitCode,
+ }: GetAccountAddressParams) => {
+ if (accountAddress) return accountAddress;
+
+ const entryPointContract = getContract({
+ address: entryPoint.address,
+ abi: entryPoint.abi,
+ client,
+ });
+
+ const initCode = await getAccountInitCode();
+ Logger.verbose("[BaseSmartContractAccount](getAddress) initCode: ", initCode);
+
+ try {
+ await entryPointContract.simulate.getSenderAddress([initCode]);
+ } catch (err: any) {
+ Logger.verbose(
+ "[BaseSmartContractAccount](getAddress) getSenderAddress err: ",
+ err,
+ );
+ if (err.cause?.data?.errorName === "SenderAddressResult") {
+ Logger.verbose(
+ "[BaseSmartContractAccount](getAddress) entryPoint.getSenderAddress result:",
+ err.cause.data.args[0],
+ );
+
+ return err.cause.data.args[0] as Address;
+ }
+
+ if (err.details === "Invalid URL") {
+ throw new InvalidRpcUrlError();
+ }
+ }
+
+ throw new GetCounterFactualAddressError();
+ };
+
+ export async function toSmartContractAccount<
+ Name extends string = string,
+ TTransport extends Transport = Transport,
+ TChain extends Chain = Chain,
+ TEntryPointVersion extends EntryPointVersion = EntryPointVersion,
+ >({
+ transport,
+ chain,
+ entryPoint,
+ source,
+ accountAddress,
+ getAccountInitCode,
+ getNonce,
+ signMessage,
+ signTypedData,
+ encodeBatchExecute,
+ encodeExecute,
+ getDummySignature,
+ signUserOperationHash,
+ encodeUpgradeToAndCall,
+ }: ToSmartContractAccountParams<
+ Name,
+ TTransport,
+ TChain,
+ TEntryPointVersion
+ >): Promise>;
+
+ /**
+ * Converts an account to a smart contract account and sets up various account-related methods using the provided parameters like transport, chain, entry point, and other utilities.
+ *
+ * @example
+ * ```ts
+ * import { http, type SignableMessage } from "viem";
+ * import { sepolia } from "viem/chains";
+ *
+ * const myAccount = await toSmartContractAccount({
+ * /// REQUIRED PARAMS ///
+ * source: "MyAccount",
+ * transport: http("RPC_URL"),
+ * chain: sepolia,
+ * // The EntryPointDef that your account is com"patible with
+ * entryPoint: getEntryPoint(sepolia, { version: "0.6.0" }),
+ * // This should return a concatenation of your `factoryAddress` and the `callData` for your factory's create account method
+ * getAccountInitCode: async () => "0x{factoryAddress}{callData}",
+ * // an invalid signature that doesn't cause your account to revert during validation
+ * getDummySignature: () => "0x1234...",
+ * // given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
+ * encodeExecute: async (uo) => "0xcalldata",
+ * signMessage: async ({ message }: { message: SignableMessage }) => "0x...",
+ * signTypedData: async (typedData) => "0x000",
+ *
+ * /// OPTIONAL PARAMS ///
+ * // if you already know your account's address, pass that in here to avoid generating a new counterfactual
+ * accountAddress: "0xaddressoverride",
+ * // if your account supports batching, this should take an array of UOs and return the calldata for calling your contract's batchExecute method
+ * encodeBatchExecute: async (uos) => "0x...",
+ * // if your contract expects a different signing scheme than the default signMessage scheme, you can override that here
+ * signUserOperationHash: async (hash) => "0x...",
+ * // allows you to define the calldata for upgrading your account
+ * encodeUpgradeToAndCall: async (params) => "0x...",
+ * });
+ * ```
+ *
+ * @param {ToSmartContractAccountParams} params the parameters required for converting to a smart contract account
+ * @param {Transport} params.transport the transport mechanism used for communication
+ * @param {Chain} params.chain the blockchain chain used in the account
+ * @param {EntryPoint} params.entryPoint the entry point of the smart contract
+ * @param {string} params.source the source identifier for the account
+ * @param {Address} [params.accountAddress] the address of the account
+ * @param {() => Promise} params.getAccountInitCode a function to get the initial state code of the account
+ * @param {(message: { message: SignableMessage }) => Promise} params.signMessage a function to sign a message
+ * @param {(typedDataDefinition: TypedDataDefinition) => Promise} params.signTypedData a function to sign typed data
+ * @param {(transactions: Transaction[]) => Hex} [params.encodeBatchExecute] a function to encode batch transactions
+ * @param {(tx: Transaction) => Hex} params.encodeExecute a function to encode a single transaction
+ * @param {() => Promise} params.getDummySignature a function to get a dummy signature
+ * @param {(uoHash: Hex) => Promise} [params.signUserOperationHash] a function to sign user operations
+ * @param {(implementationAddress: Address, implementationCallData: Hex) => Hex} [params.encodeUpgradeToAndCall] a function to encode upgrade call
+ * @returns {Promise} a promise that resolves to a SmartContractAccount object with methods and properties for interacting with the smart contract account
+ */
+ export async function toSmartContractAccount(
+ params: ToSmartContractAccountParams,
+ ): Promise {
+ const {
+ transport,
+ chain,
+ entryPoint,
+ source,
+ accountAddress,
+ getAccountInitCode,
+ signMessage,
+ signTypedData,
+ encodeExecute,
+ encodeBatchExecute,
+ getNonce,
+ getDummySignature,
+ signUserOperationHash,
+ encodeUpgradeToAndCall,
+ getImplementationAddress,
+ prepareSign: prepareSign_,
+ formatSign: formatSign_,
+ } = params;
+
+ const client = createBundlerClient({
+ // we set the retry count to 0 so that viem doesn't retry during
+ // getting the address. That call always reverts and without this
+ // viem will retry 3 times, making this call very slow
+ transport: (opts) => transport({ ...opts, chain, retryCount: 0 }),
+ chain,
+ });
+
+ const entryPointContract = getContract({
+ address: entryPoint.address,
+ abi: entryPoint.abi,
+ client,
+ });
+
+ const accountAddress_ = await getAccountAddress({
+ client,
+ entryPoint: entryPoint,
+ accountAddress,
+ getAccountInitCode,
+ });
+
+ let deploymentState = DeploymentState.UNDEFINED;
+
+ const getInitCode = async () => {
+ if (deploymentState === DeploymentState.DEPLOYED) {
+ return "0x";
+ }
+ const contractCode = await client.getCode({
+ address: accountAddress_,
+ });
+
+ if ((contractCode?.length ?? 0) > 2) {
+ deploymentState = DeploymentState.DEPLOYED;
+ return "0x";
+ } else {
+ deploymentState = DeploymentState.NOT_DEPLOYED;
+ }
+
+ return getAccountInitCode();
+ };
+
+ const signUserOperationHash_ =
+ signUserOperationHash ??
+ (async (uoHash: Hex) => {
+ return signMessage({ message: { raw: hexToBytes(uoHash) } });
+ });
+
+ const getFactoryAddress = async (): Promise =>
+ parseFactoryAddressFromAccountInitCode(await getAccountInitCode())[0];
+
+ const getFactoryData = async (): Promise =>
+ parseFactoryAddressFromAccountInitCode(await getAccountInitCode())[1];
+
+ const encodeUpgradeToAndCall_ =
+ encodeUpgradeToAndCall ??
+ (() => {
+ throw new UpgradesNotSupportedError(source);
+ });
+
+ const isAccountDeployed = async () => {
+ const initCode = await getInitCode();
+ return initCode === "0x";
+ };
+
+ const getNonce_ =
+ getNonce ??
+ (async (nonceKey = 0n): Promise => {
+ return entryPointContract.read.getNonce([
+ accountAddress_,
+ nonceKey,
+ ]) as Promise;
+ });
+
+ const account = toAccount({
+ address: accountAddress_,
+ signMessage,
+ signTypedData,
+ signTransaction: () => {
+ throw new SignTransactionNotSupportedError();
+ },
+ });
+
+ const create6492Signature = async (isDeployed: boolean, signature: Hex) => {
+ if (isDeployed) {
+ return signature;
+ }
+
+ const [factoryAddress, factoryCalldata] =
+ parseFactoryAddressFromAccountInitCode(await getAccountInitCode());
+
+ return wrapSignatureWith6492({
+ factoryAddress,
+ factoryCalldata,
+ signature,
+ });
+ };
+
+ const signMessageWith6492 = async (message: { message: SignableMessage }) => {
+ const [isDeployed, signature] = await Promise.all([
+ isAccountDeployed(),
+ account.signMessage(message),
+ ]);
+
+ return create6492Signature(isDeployed, signature);
+ };
+
+ const signTypedDataWith6492 = async <
+ const typedData extends TypedData | Record,
+ primaryType extends keyof typedData | "EIP712Domain" = keyof typedData,
+ >(
+ typedDataDefinition: TypedDataDefinition,
+ ): Promise => {
+ const [isDeployed, signature] = await Promise.all([
+ isAccountDeployed(),
+ account.signTypedData(typedDataDefinition),
+ ]);
+
+ return create6492Signature(isDeployed, signature);
+ };
+
+ const getImplementationAddress_ =
+ getImplementationAddress ??
+ (async () => {
+ const storage = await client.getStorageAt({
+ address: account.address,
+ // This is the default slot for the implementation address for Proxies
+ slot: "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ });
+
+ if (storage == null) {
+ throw new FailedToGetStorageSlotError(
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
+ "Proxy Implementation Address",
+ );
+ }
+
+ // The storage slot contains a full bytes32, but we want only the last 20 bytes.
+ // So, slice off the leading `0x` and the first 12 bytes (24 characters), leaving the last 20 bytes, then prefix with `0x`.
+ return `0x${storage.slice(26)}`;
+ });
+
+ if (entryPoint.version !== "0.6.0" && entryPoint.version !== "0.7.0") {
+ throw new InvalidEntryPointError(chain, entryPoint.version);
+ }
+
+ if ((prepareSign_ && !formatSign_) || (!prepareSign_ && formatSign_)) {
+ throw new Error(
+ "Must implement both prepareSign and formatSign or neither",
+ );
+ }
+
+ const prepareSign =
+ prepareSign_ ??
+ (() => {
+ throw new Error("prepareSign not implemented");
+ });
+
+ const formatSign =
+ formatSign_ ??
+ (() => {
+ throw new Error("formatSign not implemented");
+ });
+
+ return {
+ ...account,
+ source,
+ // TODO: I think this should probably be signUserOperation instead
+ // and allow for generating the UO hash based on the EP version
+ signUserOperationHash: signUserOperationHash_,
+ getFactoryAddress,
+ getFactoryData,
+ encodeBatchExecute:
+ encodeBatchExecute ??
+ (() => {
+ throw new BatchExecutionNotSupportedError(source);
+ }),
+ encodeExecute,
+ getDummySignature,
+ getInitCode,
+ encodeUpgradeToAndCall: encodeUpgradeToAndCall_,
+ getEntryPoint: () => entryPoint,
+ isAccountDeployed,
+ getAccountNonce: getNonce_,
+ signMessageWith6492,
+ signTypedDataWith6492,
+ getImplementationAddress: getImplementationAddress_,
+ prepareSign,
+ formatSign,
+ };
+ }
+ ````
+
diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx
new file mode 100644
index 000000000..508b3e7b6
--- /dev/null
+++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/api.mdx
@@ -0,0 +1,222 @@
+You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`.
+
+
+
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "{SIGNER_ADDRESS}"
+ }
+ ]
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "accountAddress": "ACCOUNT_ADDRESS",
+ "id": "ACCOUNT_ID"
+ }
+}
+```
+
+For other potential responses, [check out the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account)
+
+
+
+
+
+
+ **Important**: Cross-chain swaps do not support `postCalls`. You cannot batch
+ additional actions after a cross-chain swap.
+
+
+Request a cross-chain swap quote by specifying both the source chain (`chainId`) and destination chain (`toChainId`). In addition, just like in [single-chain swaps](/wallets/transactions/swap-tokens) you can specify either a `minimumToAmount` or a `fromAmount`.
+
+
+ If you're using an EOA or just want the raw array of calls returned, pass the
+ optional parameter `"returnRawCalls": true`, this will return a `calls` array.
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_requestQuote_v0",
+ "params": [
+ {
+ "from": "{ACCOUNT_ADDRESS_FROM_STEP_1}",
+ "chainId": "{SOURCE_CHAIN_ID}",
+ "toChainId": "{DESTINATION_CHAIN_ID}",
+ "fromToken": "{FROM_TOKEN}",
+ "toToken": "{TO_TOKEN}",
+ "fromAmount": "{FROM_AMOUNT_HEXADECIMAL}",
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "{PAYMASTER_POLICY_ID}"
+ }
+ }
+ }
+ ]
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 0,
+ "result": {
+ "rawCalls": false,
+ "chainId": "...",
+ "callId": "0x...",
+ "quote": {
+ "expiry": "EXPIRY",
+ "minimumToAmount": "MINIMUM_TO_AMOUNT",
+ "fromAmount": "FROM_AMOUNT"
+ },
+ "type": "user-operation-v070",
+ "data": "USER_OPERATION_DATA",
+ "signatureRequest": {
+ "type": "personal_sign",
+ "data": {
+ "raw": "..."
+ },
+ "rawPayload": "..."
+ },
+ "feePayment": {
+ "sponsored": true,
+ "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+ "maxAmount": "..."
+ }
+ }
+}
+```
+
+Note the `callId` in the response! You'll use this to track the cross-chain swap status. Also note the `signatureRequest` - this is what you need to sign, and the returned `data` field is what you'll need to send the transaction.
+
+
+
+
+
+To sign the signature request, sign the `raw` field (note, this is not a string! You need to pass it to your signer as raw bytes, generally like so: `{ raw: "0x..." }`) with your signer of choice.
+
+This should use the `personal_sign` RPC method, as noted by the `type` in the `signatureRequest`.
+
+Alternatively, you can sign the raw payload with a simple `eth_sign` but this RPC method is not favored due to security concerns.
+
+
+
+
+
+Pass the `callId` from Step 2 to `sendPreparedCalls`. This makes the response return the same `callId` with additional cross-chain status tracking information:
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_sendPreparedCalls",
+ "params": [
+ {
+ "callId": "{CALL_ID_FROM_STEP_2}",
+ "type": "user-operation-v070",
+ "data": "{DATA_FROM_STEP_2}",
+ "chainId": "{SOURCE_CHAIN_ID}",
+ "signature": {
+ "type": "secp256k1",
+ "data": "{SIGNATURE_FROM_STEP_3}"
+ }
+ }
+ ],
+ "id": 1
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": "1",
+ "result": {
+ "preparedCallIds": ["PREPARED_CALL_ID"]
+ }
+}
+```
+
+The response returns the same `callId` you passed in, which you'll use to track the cross-chain swap status in the next step.
+
+For other potential responses, [check out the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls)
+
+
+
+
+
+Use the `wallet_getCallsStatus` endpoint to check the status of your cross-chain swap. Cross-chain swaps may take longer than single-chain swaps due to cross-chain messaging.
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_getCallsStatus",
+ "params": [
+ [
+ "{CALL_ID_FROM_STEP_2_OR_STEP_4}"
+ ]
+ ],
+ "id": 1
+ }'
+```
+
+This returns:
+
+```json
+{
+ "id": "1",
+ "jsonrpc": "2.0",
+ "result": {
+ "id": "CALL_ID",
+ "chainId": "SOURCE_CHAIN_ID",
+ "atomic": true,
+ "status": 200,
+ "receipts": [...]
+ }
+}
+```
+
+Cross-chain swaps have additional status codes to reflect the cross-chain nature of the transaction:
+
+| Code | Title |
+| ---- | ----------------------- |
+| 100 | Pending |
+| 120 | Cross-Chain In Progress |
+| 200 | Confirmed |
+| 400 | Offchain Failure |
+| 410 | Cross-chain Refund |
+| 500 | Onchain Failure |
+| 600 | Partial Onchain Failure |
+
+To get your transaction hash, you can access `result.receipts[0].transactionHash`.
+
+For more details, check out [the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status)
+
+
+
+
diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx
new file mode 100644
index 000000000..9b64d2af2
--- /dev/null
+++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/client.mdx
@@ -0,0 +1,106 @@
+Required SDK version: ^v4.70.0
+
+
+ **Important**: Cross-chain swaps do not support `postCalls`. You cannot batch
+ additional actions after a cross-chain swap completes.
+
+
+You'll need the following env variables:
+
+* `ALCHEMY_API_KEY`: An [Alchemy API Key](https://dashboard.alchemy.com/apps)
+* `ALCHEMY_POLICY_ID`: A [Gas Manager](https://dashboard.alchemy.com/gas-manager/policy/create) policy ID
+* `PRIVATE_KEY`: A private key for a signer
+
+
+ ```ts title="requestCrossChainQuote.ts"
+ import { swapActions } from "@account-kit/wallet-client/experimental";
+ import { client } from "./client";
+
+ const account = await client.requestAccount();
+
+ // Add the swap actions to the client
+ const swapClient = client.extend(swapActions);
+
+ // Request the cross-chain swap quote
+ // Note: toChainId specifies the destination chain for the swap
+ const { quote, callId, ...calls } = await swapClient.requestQuoteV0({
+ from: account.address,
+ toChainId: "0x...", // Destination chain ID
+ fromToken: "0x...",
+ toToken: "0x...",
+ minimumToAmount: "0x...",
+ });
+
+ // Display the swap quote, including the minimum amount to receive and the expiry
+ console.log(quote);
+ console.log(`Cross-chain swap callId: ${callId}`);
+
+ // Assert that the calls are not raw calls.
+ // This will always be the case when requestQuoteV0 is used without the `returnRawCalls` option,
+ // the assertion is just needed for Typescript to recognize the result type.
+ if (calls.rawCalls) {
+ throw new Error("Expected user operation calls");
+ }
+
+ // Sign the quote, getting back prepared and signed calls
+ // The callId is automatically included in the signed calls
+ const signedCalls = await swapClient.signPreparedCalls(calls);
+
+ // Send the prepared calls
+ // The callId is passed through automatically
+ const { preparedCallIds } = await swapClient.sendPreparedCalls(signedCalls);
+
+ // Wait for the call to resolve
+ // Cross-chain swaps may take longer due to cross-chain messaging
+ const callStatusResult = await swapClient.waitForCallsStatus({
+ id: preparedCallIds[0]!,
+ });
+
+ // Filter through success or failure cases
+ // Cross-chain swaps have additional status codes:
+ // - 120: Cross-Chain In Progress
+ // - 410: Cross-chain Refund
+ if (
+ callStatusResult.status !== "success" ||
+ !callStatusResult.receipts ||
+ !callStatusResult.receipts[0]
+ ) {
+ throw new Error(
+ `Cross-chain swap failed with status ${callStatusResult.status}, full receipt:\n ${JSON.stringify(callStatusResult, null, 2)}`,
+ );
+ }
+
+ console.log("Cross-chain swap confirmed!");
+ console.log(
+ `Transaction hash: ${callStatusResult.receipts[0].transactionHash}`,
+ );
+ ```
+
+ ```ts title="client.ts"
+ import "dotenv/config";
+ import type { Hex } from "viem";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { alchemy, sepolia } from "@account-kit/infra";
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+ const clientParams = {
+ transport: alchemy({
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(
+ process.env.PRIVATE_KEY! as Hex,
+ ),
+ policyId: process.env.ALCHEMY_POLICY_ID!, // Optional: If you're using a gas manager policy
+ };
+
+ const clientWithoutAccount = createSmartWalletClient(clientParams);
+
+ const account = await clientWithoutAccount.requestAccount();
+
+ export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+ });
+ ```
+
diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx
new file mode 100644
index 000000000..dfe701964
--- /dev/null
+++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/index.mdx
@@ -0,0 +1,108 @@
+---
+title: Cross-chain swaps (Alpha)
+slug: wallets/transactions/cross-chain-swap-tokens
+---
+
+Cross-chain swaps let you convert tokens across different blockchain networks in a single transaction. They're built natively into Smart Wallets and you can integrate in minutes.
+
+Cross-chain swaps work just like any other Smart Wallet transaction, so you can sponsor gas to do gasless swaps, or pay for gas in an ERC-20 token.
+
+
+ Cross-chain swaps are in alpha. Note that there may be changes in the future
+ to simplify the endpoint/sdk. We will let you know if/when that happens.
+
+
+# The Cross-chain Swap flow
+
+## **Flow**
+
+1. Request a cross-chain swap quote
+2. Sign the prepared swap calls
+3. Send prepared calls
+4. Wait for cross-chain confirmation
+
+## **Swap options**
+
+
+ **Important**: Cross-chain swaps do not support `postCalls`. You cannot batch
+ additional actions after a cross-chain swap completes (for now).
+
+
+When requesting a cross-chain swap quote, you can specify either a `fromAmount` , or a `minimumToAmount`.
+
+```tsx
+// Mode 1: Swap exact input amount
+{
+ fromAmount: "0x2710";
+} // Swap exactly 0.01 USDC (10000 in hex, 6 decimals)
+
+// Mode 2: Get minimum output amount
+{
+ minimumToAmount: "0x5AF3107A4000";
+} // Get at least 0.0001 ETH (18 decimals). The amount you need to spend is calculated to get at least your desired ETH amount.
+```
+
+## Prerequisites
+
+Before you begin, ensure you have:
+
+* An [Alchemy API Key](https://dashboard.alchemy.com/apps)
+* If you're sponsoring gas, then a [Gas Manager](https://dashboard.alchemy.com/gas-manager/policy/create) policy
+* A small amount of tokens for testing (~$1 worth is enough!)
+ * **Important**: You'll need to send these tokens to your smart wallet address to be able to swap!
+* A signer to own the account and sign messages
+
+
+ Note that Cross-chain Swaps are currently supported via direct APIs and the
+ SDK. React support coming soon!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# FAQs
+
+## What chains are supported for cross-chain swaps?
+
+Chains supported (for now) are: Arbitrum, Arbitrum Nova, Base, Berachain, Boba Network, BSC/BNB, Celo, Ethereum, Hyperliquid, Ink, Optimism, Plasma, Polygon, Shape, Soneium, Story, Unichain, World Chain, and Zora mainnets.
+
+## Can I batch additional calls after a cross-chain swap?
+
+No, `postCalls` are not supported for cross-chain swaps (for now). You can only perform the swap itself across chains.
+
+## How long do cross-chain swaps take?
+
+Cross-chain swaps typically take longer than single-chain swaps due to the need for cross-chain messaging and confirmation. The exact time depends on the source and destination chains involved in the swap.
+
+## How do you encode values?
+
+Values are simply passed as hexadecimal strings. The Swap API doesn't add complexity to consider decimals, so 0x01 is always the smallest amount of a given asset.
+1 ETH, or DAI (18 decimals) is `0xDE0B6B3A7640000`
+1 USDC (6 decimals) is `0xF4240`
+This removes any ambiguity— if it's numerical, it's a hex.
+
+## What is the expiry?
+
+The expiry is an informational indicator of when you can expect to be able to process the swap request. If you're at/near the expiry, it might be a good time to request a new quote.
+
+## What are the different status codes for cross-chain swaps?
+
+Cross-chain swaps may have additional status codes beyond standard transaction statuses to reflect the cross-chain nature of the transaction. These are:
+
+* 120: Cross-chain in progress
+* 410: Cross-chain refund
+
+## When is a CallId returned from `wallet_requestQuote_v0`?
+
+Any time you’re requesting a cross-chain quote via `wallet_requestQuote_v0` , a `callId` is returned. This `callId` includes important data for cross-chain tracking. You can use this just like any other `callId` in `wallet_getCallsStatus`!
diff --git a/fern/wallets/pages/transactions/cross-chain-swap-tokens/react.mdx b/fern/wallets/pages/transactions/cross-chain-swap-tokens/react.mdx
new file mode 100644
index 000000000..f06a8080d
--- /dev/null
+++ b/fern/wallets/pages/transactions/cross-chain-swap-tokens/react.mdx
@@ -0,0 +1,172 @@
+Required SDK version: ^v4.70.0
+
+
+ **Important**: Cross-chain swaps do not support `postCalls`. You cannot batch
+ additional actions after a cross-chain swap completes.
+
+
+Use the `usePrepareSwap` hook with the `toChainId` parameter to request cross-chain swap quotes and the `useSignAndSendPreparedCalls` hook to execute token swaps across different chains.
+
+**Prerequisites**
+
+* [Smart Wallets installed and configured in your project](/wallets/react/quickstart)
+* An [authenticated user](/wallets/authentication/overview)
+* Tokens in your smart account to swap
+
+
+ ```tsx title="crossChainSwap.tsx"
+ import {
+ useSmartAccountClient,
+ usePrepareSwap,
+ useSignAndSendPreparedCalls,
+ useWaitForCallsStatus,
+ useUser,
+ } from "@account-kit/react";
+
+ // Chain IDs
+ const CHAIN_IDS = {
+ ARBITRUM: "0xa4b1",
+ BASE: "0x2105",
+ } as const;
+
+ // Token addresses
+ const TOKENS = {
+ NATIVE: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEee", // ETH
+ BASE_USDC: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
+ } as const;
+
+ export default function CrossChainSwap() {
+ const user = useUser();
+ const { client } = useSmartAccountClient({
+ accountParams: { mode: "7702" },
+ });
+
+ const { prepareSwapAsync, isPreparingSwap } = usePrepareSwap({
+ client,
+ });
+
+ const {
+ signAndSendPreparedCallsAsync,
+ isSigningAndSendingPreparedCalls,
+ signAndSendPreparedCallsResult,
+ } = useSignAndSendPreparedCalls({ client });
+
+ const {
+ data: statusResult,
+ isLoading: isWaitingForConfirmation,
+ error,
+ } = useWaitForCallsStatus({
+ client,
+ id: signAndSendPreparedCallsResult?.preparedCallIds[0],
+ });
+
+ const handleCrossChainSwap = async () => {
+ if (!client?.account.address) {
+ throw new Error("No account connected");
+ }
+
+ try {
+ // Step 1: Request cross-chain swap quote
+ const result = await prepareSwapAsync({
+ from: client.account.address,
+ fromToken: TOKENS.NATIVE,
+ toChainId: CHAIN_IDS.BASE, // Destination chain
+ toToken: TOKENS.BASE_USDC,
+ fromAmount: "0x5af3107a4000", // 0.0001 ETH
+ });
+
+ const { quote, ...calls } = result;
+ console.log("Cross-chain swap quote:", quote);
+
+ // Ensure we have prepared calls
+ if (calls.rawCalls) {
+ throw new Error("Expected prepared calls");
+ }
+
+ // Step 2: Sign and send the prepared calls
+ const callIds = await signAndSendPreparedCallsAsync(calls);
+
+ console.log("Cross-chain swap initiated");
+ console.log("Call ID:", callIds?.preparedCallIds[0]);
+ } catch (error) {
+ console.error("Cross-chain swap failed:", error);
+ }
+ };
+
+ if (!user) {
+ return
Please log in to use swap functionality
;
+ }
+
+ return (
+
+
+ {isPreparingSwap
+ ? "Requesting quote..."
+ : isSigningAndSendingPreparedCalls
+ ? "Signing and sending..."
+ : "Swap ETH Arbitrum → Base USDC"}
+
+
+ {signAndSendPreparedCallsResult && (
+
+ );
+ }
+ ```
+
+
+## How it works
+
+1. **Request quote**: `usePrepareSwap` requests a cross-chain swap quote with the `toChainId` parameter
+2. **Destructure result**: Extract `quote` for display and `calls` for signing
+3. **Sign and send**: `useSignAndSendPreparedCalls` signs and submits the transaction (callId automatically preserved)
+4. **Track status**: `useWaitForCallsStatus` monitors the transaction with cross-chain status codes
+
+Cross-chain swaps take longer than single-chain swaps due to cross-chain messaging and confirmation requirements.
+
+## Cross-chain status codes
+
+Cross-chain swaps have additional status codes to reflect the cross-chain nature:
+
+| Code | Status |
+| ------- | --------------------------- |
+| 100 | Pending |
+| **120** | **Cross-Chain In Progress** |
+| 200 | Confirmed |
+| 400 | Offchain Failure |
+| **410** | **Cross-chain Refund** |
+| 500 | Onchain Failure |
+| 600 | Partial Onchain Failure |
+
+## Swap options
+
+You can specify either an exact input amount or a minimum output amount:
+
+```tsx
+// Mode 1: Swap exact input amount
+await prepareSwapAsync({
+ from: address,
+ toChainId: "0x2105", // Base
+ fromToken: "0x...",
+ toToken: "0x...",
+ fromAmount: "0x2710", // Swap exactly 0.01 USDC
+});
+
+// Mode 2: Get minimum output amount
+await prepareSwapAsync({
+ from: address,
+ toChainId: "0x2105", // Base
+ fromToken: "0x...",
+ toToken: "0x...",
+ minimumToAmount: "0x5AF3107A4000", // Get at least 0.0001 ETH
+});
+```
diff --git a/fern/wallets/pages/transactions/overview.mdx b/fern/wallets/pages/transactions/overview.mdx
new file mode 100644
index 000000000..ca65304d2
--- /dev/null
+++ b/fern/wallets/pages/transactions/overview.mdx
@@ -0,0 +1,41 @@
+---
+title: Overview
+description: A comprehensive guide to sending transactions with Smart Wallets
+slug: wallets/transactions/overview
+---
+
+Smart Wallets make it easy to send transactions across EVM and Solana. This section covers everything you need to know about preparing, signing, sending, and tracking transactions.
+
+# Transaction lifecycle
+
+
+
+When a user sends a transaction, Smart Wallets handle every step. In this section, you’ll learn how to execute steps 2–5 to enable capabilities (ex: gas sponsorship) for your users:
+
+1. Authenticate: User verifies their identity and accesses your app (see [Authentication](/wallets/authentication/overview))
+2. Prepare: Prepare a transaction for submission
+3. Sign: User signs the transaction
+4. Send: Submit the transaction for inclusion onchain
+5. Track & display data: Track the status of the transaction and display it to the user
+
+# Everything you need for onchain applications
+
+| Capability | Description |
+| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
+| [Send transactions](/wallets/transactions/send-transactions) | Execute a single transaction |
+| [EIP-7702](/wallets/transactions/using-eip-7702) | Upgrade embedded EOA users to smart wallets |
+| [Batch transactions](/wallets/transactions/send-batch-transactions) | Execute multiple transactions atomically in a single step (ex: approve & swap) |
+| [Sponsor gas](/docs/wallets/transactions/sponsor-gas) | Make gas disappear and say goodbye to “insufficient gas” |
+| [Pay gas with any token](/wallets/transactions/pay-gas-with-any-token) | Pay gas with stablecoins or the sell-side token |
+| [Swap tokens](/wallets/transactions/swap-tokens) | Swap across networks, seamlessly |
+| [Retry transactions](/wallets/transactions/retry-transactions) | Retry transactions stuck in mempool |
+| [Send parallel transactions](/wallets/transactions/send-parallel-transactions) | Send multiple transactions in parallel |
+| [Sponsor gas on Solana](/wallets/transactions/solana/sponsor-gas) | Sponsor fees & rent and say goodbye to “insufficient fees" |
+| [Track status](/wallets/transactions/send-transactions) | Track the status of the transaction |
+
+# SDK vs. APIs
+
+Use the SDK if you’re building with React, React Native, or other JavaScript frameworks. It provides ready-to-use hooks and utilities. Use the APIs directly if you’re building in other environments (ex: python, flutter) or want lower-level control.
diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx
new file mode 100644
index 000000000..01bb440e6
--- /dev/null
+++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-postop.mdx
@@ -0,0 +1,68 @@
+See the [`wallet_prepareCalls` API
+reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+for full descriptions of the parameters used in the following example.
+
+
+
+ If the user does not yet have a smart account, you must create one.
+```bash
+ ACCOUNT_ADDRESS=$(curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "'$SIGNER_ADDRESS'"
+ }
+ ]
+}
+' | jq -r '.result.accountAddress')
+```
+
+
+ Prepare calls using the `paymasterService` capability.
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "'$ALCHEMY_POLICY_ID'",
+ "erc20": {
+ "tokenAddress": "'$GAS_TOKEN'",
+ "postOpSettings": {
+ "autoApprove": {
+ "below": "'$APPROVE_BELOW'",
+ "amount": "'$APPROVE_AMOUNT'"
+ }
+ }
+ }
+ }
+ },
+ "calls": [
+ {
+ "to": "0x0000000000000000000000000000000000000000"
+ }
+ ],
+ "from": "'$ACCOUNT_ADDRESS'",
+ "chainId": "'$CHAIN_ID'"
+ }
+ ]
+}'
+```
+
+
+ Sign the returned signature request and send using `wallet_sendPreparedCalls`. See the [send transactions guide](/wallets/transactions/send-transactions) for more info.
+
+
diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx
new file mode 100644
index 000000000..89145ff5a
--- /dev/null
+++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/api-preop.mdx
@@ -0,0 +1,101 @@
+
+
+ If the user does not yet have a smart account, you must create one.
+
+ ```bash
+ ACCOUNT_ADDRESS=$(curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+ {
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "'$SIGNER_ADDRESS'"
+ }
+ ]
+ }
+ ' | jq -r '.result.accountAddress')
+ ```
+
+
+
+ Prepare calls using the `paymasterService` capability.
+
+ ```bash
+ curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+ {
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "'$ALCHEMY_POLICY_ID'",
+ "erc20": {
+ "tokenAddress": "'$GAS_TOKEN'",
+ "preOpSettings": {
+ "autoPermit": {
+ "below": "'$APPROVE_BELOW'",
+ "amount": "'$APPROVE_AMOUNT'"
+ }
+ }
+ }
+ }
+ },
+ "calls": [
+ {
+ "to": "0x0000000000000000000000000000000000000000"
+ }
+ ],
+ "from": "'$ACCOUNT_ADDRESS'",
+ "chainId": "'$CHAIN_ID'"
+ }
+ ]
+ }'
+ ```
+
+
+
+ If the response to step 2 is a type `paymaster-permit`, then the user must sign the signature request and return via a second call to `wallet_prepareCalls`. Else, skip to step 5.
+ The `data` field on the `paymaster-permit` response contains a [Permit typed message](https://eips.ethereum.org/EIPS/eip-2612). It is recommended that the user
+ checks the fields of this message prior to calculating its hash. The `signatureRequest` field on the `paymaster-permit` response is a required signature over the calculated
+ hash. This is typically an ERC-712 typed signature envelope with a field for the hash to sign over.
+
+
+
+ After signing, another call to `wallet_prepareCalls` is needed to encode the permit into the operation. As a convenience, the `paymaster-permit` response type returns a `modifiedRequest`
+ parameter that modifies the initial request with the permit details. The second request is the `modifiedRequest` with an additional `paymasterPermitSignature` field set to the
+ signature from step 3. The user can also choose to recreate the request and set the corresponding fields in the `paymasterService` capability to the details returned in the permit signature.
+ For example:
+
+ ```
+ {
+ "capabilities": {
+ "paymasterService": {
+ "erc20": {
+ "preOp": {
+ "permitDetails": {
+ "value": "0x...",
+ "deadline": "0x...",
+ }
+ }
+ }
+ }
+ }
+ ... // same request as step 2
+ "paymasterPermitSignature": "0x..."
+ }
+ ```
+
+
+
+ Sign and send the last prepared calls result as usual using `wallet_sendPreparedCalls`. See the [send transactions guide](/wallets/transactions/send-transactions) for more info.
+
+
diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx
new file mode 100644
index 000000000..42101a9de
--- /dev/null
+++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/client.mdx
@@ -0,0 +1,73 @@
+Required SDK version: ^v4.61.0
+
+See the [`sendCalls` SDK
+reference](/wallets/reference/account-kit/wallet-client/functions/sendCalls)
+for full descriptions of the parameters used in the following example.
+
+Use the `paymasterService` capability on the smart wallet client `sendCalls` or `prepareCalls` actions.
+
+
+
+```ts title="sendCalls.ts"
+import { client, config } from "./client.ts";
+
+const { preparedCallIds } = await client.sendCalls({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ erc20: {
+ tokenAddress: config.gasToken,
+ postOpSettings: {
+ autoApprove: {
+ below: config.approveBelow,
+ amount: config.approveAmount,
+ },
+ },
+ },
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+});
+```
+
+```ts title="client.ts"
+import "dotenv/config";
+import { type Address, type Hex, toHex } from "viem";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+export const config = {
+ policyId: process.env.ALCHEMY_POLICY_ID!,
+ gasToken: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" as Address, // USDC on sepolia
+ approveBelow: toHex(1000000n), // 1 USDC
+ approveAmount: toHex(10000000n), // 10 USDC
+};
+
+const clientParams = {
+ transport: alchemy({
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(
+ process.env.PRIVATE_KEY! as Hex,
+ ),
+};
+
+const clientWithoutAccount = createSmartWalletClient(clientParams);
+
+const account = await clientWithoutAccount.requestAccount();
+
+export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+});
+```
+
+
diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/index.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/index.mdx
new file mode 100644
index 000000000..471220a1d
--- /dev/null
+++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/index.mdx
@@ -0,0 +1,173 @@
+---
+title: Pay gas with any token
+description: Enable users to pay gas with tokens like USDC
+slug: wallets/transactions/pay-gas-with-any-token
+---
+
+Gas fees paid in the native token can feel foreign to users that primarily hold stablecoins or your app's own token.
+With Smart Wallets, you can allow users to pay for gas with any token, streamlining the user experience.
+
+## How it works
+
+When a user pays for gas with a token, the gas is fronted using the network's native gas token and the payment tokens are transferred from the user's wallet to a wallet you configure in the policy.
+The equivalent USD amount and the admin fee are then added to your's monthly invoice.
+
+Post-operation mode is recommended. This mode requires users to hold enough of the gas token in their wallet after their operation completes to pay the gas fee. If the balance is insufficient, the transaction reverts and you sponsor any gas used without token payment. If this doesn't work for your use case, see the [Token gas payment modes](#token-gas-payment-modes) section below for more options.
+
+## Prerequisites
+
+* API key from your [dashboard](https://dashboard.alchemy.com/apps)
+* [A "pay gas with any token" policy](https://dashboard.alchemy.com/gas-manager/policy/create).
+
+## Implementation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Advanced
+
+
+ Pay gas with any token also works with the prepare calls methods in the various frameworks. Usage of the capability will be the same as when using send calls. It is recommended to use prepare calls if you want to inspect the prepared call prior to prompting the user for signature.
+
+
+
+ See the [`usePrepareCalls` react
+ hook](/wallets/reference/account-kit/react/hooks/usePrepareCalls)
+
+
+
+ See the [`prepareCalls` SDK
+ reference](/wallets/reference/account-kit/wallet-client/functions/prepareCalls)
+
+
+
+ See the [`wallet_prepareCalls` API
+ reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+
+
+
+
+
+ The configured mode determines when the user's token payment occurs.
+
+ **\[Recommended] Post-Operation**
+
+ * No upfront allowance is required.
+ * The user signs an approval inside the same calls batch, and the paymaster pulls the token after the operation has executed.
+ * If that post-operation transfer fails, the entire batch is reverted and you (the developer) pay the gas fee.
+
+ **Pre-Operation:**
+
+ * The paymaster must have an allowance prior to the operation.
+ * This can be done either through a prior call to `approve()` or by using an [ERC-7597 Permit](https://eips.ethereum.org/EIPS/eip-7597) signature.
+ * If the required allowance isn't in place when the user operation is submitted, it will be rejected.
+
+ Post-operation mode is recommended for most use cases. This mode:
+
+ * Is the most gas efficient as it only requires a single transfer.
+ * Works with all ERC-20 tokens.
+ * Only ever requires a single signature from the user.
+
+ However, because tokens are deducted after execution, you may be required to pay for gas without receiving sufficient token payment.
+ You should ensure that users have enough token left over to pay for gas after the operation, otherwise they won't receive payment from users for gas and the operation will revert.
+ If the operation results in a static amount of the user’s token balance after execution and you can account for this before submitting the operation, use PostOp mode.
+
+ Examples of static amounts:
+
+ * Payments, purchases, and deposits
+ * Operations unrelated to the payment token
+
+ Examples of dynamic amounts:
+
+ * Swaps that include the payment token
+
+ If you sponsor operations that result in dynamic amounts of the payment token left over, consider using pre-operation mode. See an example implementation below.
+
+
+
+
+
+
+
+ To show users the gas cost in their chosen gas token prior to signing and sending their request,
+ use the `prepareCalls` hook/action/api over the `sendCalls` version
+ as `sendCalls` doesn't surface the fee payment information during sign and send.
+
+ The return type of `prepareCalls` contains a `feePayment` field containing fee information. Display this information to users prior to signing the operation. For example:
+
+ ```
+ "feePayment": {
+ "sponsored": false,
+ "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
+ "maxAmount": "0x1d33"
+ }
+ ```
+
+ Calling `prepareCalls` using the token paymaster capability counts against your policy's pending total. If you intend on making many
+ calls to surface the best price, set the `onlyEstimation` parameter for estimation, and then remove only when the user intends to sign the result.
+
+
+ Requests to `prepareCalls` count against your policy's pending total. Use
+ `onlyEstimation` if the intention is to query for the fee and not to sign the
+ operation. Note that `onlyEstimation` does not work with the `sendCalls`
+ requests and must be used with `prepareCalls`.
+
+
+
+
+ The Wallet APIs can be configured to automatically inject the required approvals into the operation when needed. The settings for this depend on the mode being used.
+
+ **Post-operation mode**
+
+ In post-operation mode, you can set the paymaster service capability to automatically inject approvals to the paymaster contract using the `erc20.postOp.autoApprove` field.
+ The approval will be batched with the provided calls to ensure it's in place before the transfer to the paymaster during post-operation.
+ To save gas, the approval is only injected if the sender's existing allowance on the paymaster contract is less than the `below` parameter. The injected approval will be set to
+ the `amount` parameter.
+
+ Set `below` high enough to reasonably cover the gas cost for your most expensive operations. Set `amount` to a value
+ you feel comfortable maintaining as an allowance on the paymaster contract. Setting a large `amount` is not recommended for security purposes.
+
+ **Pre-operation mode**
+
+
+ This is an advanced feature. Use post-operation mode for most use cases.
+
+
+ In pre-operation mode, you can set the paymaster service capability to automatically return a request for a Permit signature using the `erc20.preOp.autoPermit` field.
+ This permit will allow an allowance to be set on the paymaster contract from the sender prior to any token transfers needed during pre operation.
+
+ To be eligible for auto-injected permits, the payment gas token must:
+
+ 1. Be [ERC-7597](https://eips.ethereum.org/EIPS/eip-7597) compliant.
+ 2. Expose a `version()` function.
+ 3. Utilize the canonical `DOMAIN_SEPARATOR` outlined in [ERC-2612](https://eips.ethereum.org/EIPS/eip-2612).
+
+ If the user already has an allowance on the paymaster contract, the normal flow is used. If the user needs an
+ approval, the following steps inject the permit:
+
+ 1. The prepare request returns a signature request for the paymaster permit.
+ 2. The user signs the permit request and the signature request back in a second prepare request.
+ 3. The permit signature is injected into the paymaster contract calldata, and a normal operation is returned.
+ 4. The user proceeds as normal, signing and sending the operation.
+
+
+## Next steps
+
+Build more:
+
+* [Sponsor gas for users](/wallets/transactions/sponsor-gas)
+
+Troubleshooting:
+
+* [Gas manager errors](/reference/gas-manager-errors)
diff --git a/fern/wallets/pages/transactions/pay-gas-with-any-token/react.mdx b/fern/wallets/pages/transactions/pay-gas-with-any-token/react.mdx
new file mode 100644
index 000000000..9656f3a22
--- /dev/null
+++ b/fern/wallets/pages/transactions/pay-gas-with-any-token/react.mdx
@@ -0,0 +1,71 @@
+Required SDK version: ^v4.61.0
+
+Use the `useSendCalls` hook and the `paymasterService` capability parameter to send calls with
+an ERC20 token paymaster.
+
+**Prerequisites**
+
+* [Smart Wallets installed and configured in your project](/wallets/pages/react/setup.mdx).
+
+See the [`useSendCalls` SDK reference](/wallets/reference/account-kit/react/hooks/useSendCalls) for parameter descriptions.
+
+
+ ```tsx title="sendCalls.tsx" focus={5-7,14-27}
+ import { useSendCalls, useSmartAccountClient } from "@account-kit/react";
+ import { config } from "./config";
+
+ export default function SendCalls() {
+ const { client } = useSmartAccountClient({});
+ const { sendCallsAsync } = useSendCalls({ client });
+
+ const handleSend = async () => {
+ if (!client) {
+ throw new Error("Smart account client not connected");
+ }
+
+ try {
+ const { ids } = await sendCallsAsync({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ erc20: {
+ tokenAddress: config.gasToken,
+ postOpSettings: {
+ autoApprove: {
+ below: config.approveBelow,
+ amount: config.approveAmount,
+ },
+ },
+ },
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Transaction sent with ID:", ids[0]);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ return Click to Send;
+ }
+ ```
+
+ ```ts title="config.ts"
+ import { type Address, toHex } from "viem";
+
+ export const config = {
+ policyId: process.env.ALCHEMY_POLICY_ID!,
+ gasToken: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238" as Address, // USDC on Sepolia
+ approveBelow: toHex(1000000n), // 1 USDC
+ approveAmount: toHex(10000000n), // 10 USDC
+ };
+ ```
+
diff --git a/fern/wallets/pages/transactions/retry-transactions/api.mdx b/fern/wallets/pages/transactions/retry-transactions/api.mdx
new file mode 100644
index 000000000..6c6d6dd35
--- /dev/null
+++ b/fern/wallets/pages/transactions/retry-transactions/api.mdx
@@ -0,0 +1,264 @@
+You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`.
+
+
+
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "{SIGNER_ADDRESS}"
+ }
+ ]
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "accountAddress": "ACCOUNT_ADDRESS",
+ "id": "ACCOUNT_ID"
+ }
+}
+```
+
+For other potential responses, [check out the API reference!](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account)
+
+
+
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "calls": [
+ {
+ "to": "{TO_ADDRESS}",
+ "value": "{VALUE}",
+ "data": "{DATA}"
+ }
+ ],
+ "from": "{ACCOUNT_ADDRESS}",
+ "chainId": "{CHAIN_ID}",
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "{PAYMASTER_POLICY_ID}"
+ }
+ }
+ }
+ ]
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "type": "user-operation-v070",
+ "data": "USER_OPERATION_DATA",
+ "chainId": "CHAIN_ID",
+ "signatureRequest": {
+ "type": "personal_sign",
+ "data": {
+ "raw": "HASH_TO_SIGN"
+ },
+ "rawPayload": "RAW_PAYLOAD"
+ }
+ }
+}
+```
+
+Sign the `raw` field and send the transaction as usual. See the [sending transactions documentation](/wallets/transactions/send-transactions) for more details.
+
+
+
+
+
+After sending the transaction and getting a `PREPARED_CALL_ID`, monitor its status:
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_getCallsStatus",
+ "params": [
+ [
+ "{PREPARED_CALL_ID}"
+ ]
+ ],
+ "id": 1
+ }'
+```
+
+This returns:
+
+```json
+{
+ "id": "1",
+ "jsonrpc": "2.0",
+ "result": {
+ "id": "PREPARED_CALL_ID",
+ "chainId": "CHAIN_ID",
+ "atomic": true,
+ "status": 100,
+ "receipts": []
+ }
+}
+```
+
+If `status` remains 100 (Pending) for too long, proceed to retry.
+
+
+
+
+
+To replace the stuck transaction, re-prepare the same call. This will automatically use the same nonce as the pending transaction as long as you don't override the nonce!
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "calls": [
+ {
+ "to": "{SAME_TO_ADDRESS}",
+ "value": "{SAME_VALUE}",
+ "data": "{SAME_DATA}"
+ }
+ ],
+ "from": "{ACCOUNT_ADDRESS}",
+ "chainId": "{CHAIN_ID}",
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "{PAYMASTER_POLICY_ID}"
+ }
+ }
+ }
+ ]
+ }'
+```
+
+This will return a new signature request with updated gas prices:
+
+```bash
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "type": "user-operation-v070",
+ "data": "NEW_USER_OPERATION_DATA",
+ "chainId": "CHAIN_ID",
+ "signatureRequest": {
+ "type": "personal_sign",
+ "data": {
+ "raw": "NEW_HASH_TO_SIGN"
+ },
+ "rawPayload": "NEW_RAW_PAYLOAD"
+ }
+ }
+}
+```
+
+
+
+
+
+Sign the new signature request and send it:
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_sendPreparedCalls",
+ "params": [
+ {
+ "type": "user-operation-v070",
+ "data": "{NEW_USER_OPERATION_DATA}",
+ "chainId": "{CHAIN_ID}",
+ "signature": {
+ "type": "secp256k1",
+ "data": "{NEW_SIGNATURE}"
+ }
+ }
+ ],
+ "id": 1
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": "1",
+ "result": {
+ "preparedCallIds": ["NEW_PREPARED_CALL_ID"]
+ }
+}
+```
+
+The replacement transaction will use the same nonce as the stuck transaction, causing the original to be dropped from the mempool.
+
+
+
+
+
+Check the status of your replacement transaction:
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_getCallsStatus",
+ "params": [
+ [
+ "{NEW_PREPARED_CALL_ID}"
+ ]
+ ],
+ "id": 1
+ }'
+```
+
+Note that the status codes are:
+| Code | Title |
+|------|--------------------------|
+| 100 | Pending |
+| 200 | Confirmed |
+| 400 | Offchain Failure |
+| 500 | Onchain Failure |
+| 600 | Partial Onchain Failure |
+
+
+
+
+
+
+ To cancel a stuck transaction entirely, send a no-op replacement (same `from`
+ and `to` address with empty data).
+
diff --git a/fern/wallets/pages/transactions/retry-transactions/client.mdx b/fern/wallets/pages/transactions/retry-transactions/client.mdx
new file mode 100644
index 000000000..c8a1d80b6
--- /dev/null
+++ b/fern/wallets/pages/transactions/retry-transactions/client.mdx
@@ -0,0 +1,103 @@
+Required SDK version: ^v4.61.0
+
+See the [`sendCalls` SDK
+reference](/docs/wallets/reference/account-kit/wallet-client/functions/sendCalls)
+for full descriptions of the parameters used in the following example.
+
+When re-sending calls without a `nonceOverride`, the client automatically uses the same nonce as the pending transaction, replacing it.
+
+You'll need the following env variables:
+
+* `ALCHEMY_API_KEY`: An [Alchemy API Key](https://dashboard.alchemy.com/apps)
+* `ALCHEMY_POLICY_ID`: A [Gas Manager](https://dashboard.alchemy.com/gas-manager/policy/create) policy ID
+* `PRIVATE_KEY`: A private key for a signer
+
+
+ ```ts title="retryTransaction.ts"
+ import { client, config } from "./client.ts";
+
+ // Send initial transaction
+ const { preparedCallIds } = await client.sendCalls({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Initial transaction:", preparedCallIds[0]);
+
+ // Check status
+ const status = await client.getCallsStatus({
+ id: preparedCallIds[0]!,
+ });
+
+ console.log("Transaction status:", status.status);
+
+ // If still pending (status 100), send replacement
+ if (status.status === 100) {
+ console.log("Transaction pending, sending replacement...");
+
+ // Re-send without nonceOverride to replace stuck transaction
+ const { preparedCallIds: replacementIds } = await client.sendCalls({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Replacement transaction:", replacementIds[0]);
+
+ // Wait for replacement to confirm
+ const result = await client.waitForCallsStatus(replacementIds[0]!);
+
+ console.log("Replacement confirmed:", result);
+ }
+ ```
+
+ ```ts title="client.ts"
+ import "dotenv/config";
+ import { type Address, type Hex, toHex } from "viem";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { alchemy, sepolia } from "@account-kit/infra";
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+ export const config = {
+ policyId: process.env.ALCHEMY_POLICY_ID!,
+ };
+
+ const clientParams = {
+ transport: alchemy({
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(
+ process.env.PRIVATE_KEY! as Hex,
+ ),
+ };
+
+ const clientWithoutAccount = createSmartWalletClient(clientParams);
+
+ const account = await clientWithoutAccount.requestAccount();
+
+ export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+ });
+ ```
+
diff --git a/fern/wallets/pages/transactions/retry-transactions/index.mdx b/fern/wallets/pages/transactions/retry-transactions/index.mdx
new file mode 100644
index 000000000..b2d69c647
--- /dev/null
+++ b/fern/wallets/pages/transactions/retry-transactions/index.mdx
@@ -0,0 +1,48 @@
+---
+title: Retry Transactions
+slug: wallets/transactions/retry-transactions
+---
+
+Replace stuck transactions by preparing and sending calls again.
+
+Key use cases:
+
+* Replace transactions stuck due to low gas prices
+* Speed up pending transactions by increasing gas fees
+* Override transactions that have been pending for too long
+* Cancel stuck transactions by replacing with a no-op
+
+## The Retry flow
+
+1. Send the initial transaction
+2. Monitor the transaction status
+3. If the transaction is stuck/pending too long, re-prepare the same call
+4. Send the transaction with higher gas to replace the stuck transaction
+5. The original transaction gets dropped from mempool
+
+## Prerequisites
+
+* An [Alchemy API Key](https://dashboard.alchemy.com/apps)
+* A [Gas Manager](https://dashboard.alchemy.com/gas-manager/policy/create) policy
+* A signer to own the account and sign messages
+
+
+ If the original transaction is already being mined, the replacement
+ transaction may be dropped. In this case, you won't be able to retrieve data
+ using the replacement's call ID, and the original transaction will be
+ included!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/fern/wallets/pages/transactions/retry-transactions/react.mdx b/fern/wallets/pages/transactions/retry-transactions/react.mdx
new file mode 100644
index 000000000..55df1f857
--- /dev/null
+++ b/fern/wallets/pages/transactions/retry-transactions/react.mdx
@@ -0,0 +1,119 @@
+Required SDK version: ^v4.61.0
+
+Use the `useSendCalls` hook to send and retry transactions. When retrying, don't specify a `nonceOverride` - the client automatically uses the same nonce to replace the stuck transaction.
+
+You'll need both `ALCHEMY_API_KEY` and `ALCHEMY_POLICY_ID` environment variables set to follow along!
+
+
+
+```tsx title="retryTransaction.tsx"
+import { config } from "@/app/config";
+import {
+ useSendCalls,
+ useSmartAccountClient,
+ useSmartWalletClient,
+} from "@account-kit/react";
+import { useState } from "react";
+
+export default function RetryTransaction() {
+ const { client } = useSmartAccountClient({});
+ const { sendCallsAsync } = useSendCalls({ client });
+ const walletClient = useSmartWalletClient({
+ account: client?.getAddress(),
+ });
+ const [isStuck, setIsStuck] = useState(false);
+
+ const handleSend = async () => {
+ if (!client) {
+ throw new Error("Smart account client not connected");
+ }
+ if (!walletClient) {
+ throw new Error("Wallet client not connected");
+ }
+
+ try {
+ const { ids } = await sendCallsAsync({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Transaction sent:", ids[0]);
+
+ // Check status after a delay
+ setTimeout(async () => {
+ const callStatus = await walletClient.getCallsStatus(ids[0]!);
+ console.log(callStatus.status);
+ if (callStatus.status === 100) {
+ setIsStuck(true);
+ console.log("Transaction is still in mempool");
+ }
+ }, 3000); // Check after 3 seconds
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ const handleRetry = async () => {
+ if (!client) {
+ throw new Error("Smart account client not connected");
+ }
+ if (!walletClient) {
+ throw new Error("Wallet client not connected");
+ }
+
+ try {
+ // Re-send without nonceOverride to replace stuck transaction
+ const { ids } = await sendCallsAsync({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Replacement transaction sent:", ids[0]);
+ setIsStuck(false);
+
+ const result = await walletClient.waitForCallsStatus({ id: ids[0]! });
+ console.log("Replacement confirmed:", result);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ return (
+
;
+ }
+
+ return (
+
+ sendTransaction({
+ transfer: {
+ toAddress: "",
+ amount: 1000000,
+ },
+ })
+ }
+ >
+ Send Sponsored Transaction
+
+ );
+}
+```
+
+
diff --git a/fern/wallets/pages/transactions/solana/sponsor-gas-solana.mdx b/fern/wallets/pages/transactions/solana/sponsor-gas-solana.mdx
new file mode 100644
index 000000000..be03a876c
--- /dev/null
+++ b/fern/wallets/pages/transactions/solana/sponsor-gas-solana.mdx
@@ -0,0 +1,68 @@
+---
+title: Sponsor fees & rent on Solana
+description: How to sponsor fees & rent on Solana
+slug: wallets/transactions/solana/sponsor-gas
+---
+
+Fees and rent are a significant barrier to entry for new users of your app. Sponsor fees and rent to enable users to transact without holding SOL.
+
+* Works with any Solana wallet. [Check out Solana Smart Wallets](/wallets/react/solana-wallets/get-started)
+* No need to manage SOL, we sponsor fees & rent and put it on your bill
+
+## How it works
+
+When you request gas sponsorship for a transaction using a configured policy, the policy engine will determine if that transaction is eligible for sponsorship. If eligible, Gas Manager will pay for the fees and rent upfront when the user sends the transaction. Gas Manager will make a note of the sponsored cost and add it to your monthly bill.
+
+* Fees: the cost of executing transactions
+* Rent: the minimum payment to store data onchain
+ * Rent sponsorship is supported for `createAccount` and `createAssociatedTokenAccount`. If you need support for custom programs, contact [wallets@alchemy.com](mailto:wallets@alchemy.com).
+
+## Prerequisites
+
+* API key from your [dashboard](https://dashboard.alchemy.com/apps)
+* Smart Wallets for Solana [set up](/wallets/react/solana-wallets/get-started) in your project if you want to enable sign up/login for creation of wallets
+* A sponsorship policy to cover fees and/or rent: [create a policy](https://dashboard.alchemy.com/gas-manager/policy/create/)
+
+## Implementation
+
+
+
+ Required SDK version: ^v4.59.1
+
+ ## Global sponsorship
+
+ To apply a sponsorship policy to all transactions sent by the Solana wallet provider, you can configure fee and rent sponsorship by passing a `policyId` into your Solana config. Replace the API key and Policy Id with your keys from the [dashboard](https://dashboard.alchemy.com/services/gas-manager/configuration). This setup applies sponsorship to all Solana transactions sent from wallets created with this configuration (e.g. this will be applied to usage of the `useSolana…` hooks).
+
+
+
+ ### Per transaction sponsorship
+
+ Alternatively, to apply sponsorship conditionally for each transaction, you can pass a `policyId` to the `useSolanaTransaction` hook.
+
+
+
+ Congrats! You’re now creating smart wallets on Solana with social login and sending sponsored transactions to create a zero-friction experience from sign-up to transaction.
+
+
+
+ ### Prepare a Serialized Solana Transaction
+
+ Here’s an example of creating a serialized transfer transaction using javascript:
+
+
+
+ ### Request sponsorship for the Serialized Transaction
+
+ To sponsor fees and rent on Solana, 1) the `payerKey` field of the transaction needs to be set to the feePayer wallet that will pay for the gas, 2) the feePayer wallet needs to sign the transaction.
+
+ You can get the `feePayer` address and the feePayer signature through [`alchemy_requestFeePayer`](/reference/alchemy-requestfeepayer) using your gas policy id and the serialized transaction. Gas Manager will update the `feePayer` and add the signature to the `serializedTransaction` if and only the transaction satisfies the rules defined in your policy.
+
+
+
+ ### Sign and broadcast the Transaction
+
+ Here is an example of signing and broadcasting a transaction using javascript:
+
+
+
+
diff --git a/fern/wallets/pages/transactions/sponsor-gas/api.mdx b/fern/wallets/pages/transactions/sponsor-gas/api.mdx
new file mode 100644
index 000000000..b49e9037a
--- /dev/null
+++ b/fern/wallets/pages/transactions/sponsor-gas/api.mdx
@@ -0,0 +1,59 @@
+See the [`wallet_prepareCalls` API
+reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+for full descriptions of the parameters used in the following example.
+
+
+
+ If the user does not yet have a smart account, you must create one.
+```bash
+ACCOUNT_ADDRESS=$(curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "'$SIGNER_ADDRESS'"
+ }
+ ]
+}
+' | jq -r '.result.accountAddress')
+```
+
+
+ Prepare calls using the `paymasterService` capability.
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "'$ALCHEMY_POLICY_ID'"
+ }
+ },
+ "calls": [
+ {
+ "to": "0x0000000000000000000000000000000000000000"
+ }
+ ],
+ "from": "'$ACCOUNT_ADDRESS'",
+ "chainId": "'$CHAIN_ID_HEX'"
+ }
+ ]
+}'
+```
+
+
+ Sign the returned signature request and send using `wallet_sendPreparedCalls`.
+
+
diff --git a/fern/wallets/pages/transactions/sponsor-gas/client.mdx b/fern/wallets/pages/transactions/sponsor-gas/client.mdx
new file mode 100644
index 000000000..bb0b98325
--- /dev/null
+++ b/fern/wallets/pages/transactions/sponsor-gas/client.mdx
@@ -0,0 +1,67 @@
+Required SDK version: ^v4.61.0
+
+See the [`sendCalls` SDK
+reference](/wallets/reference/account-kit/wallet-client/functions/sendCalls)
+for full descriptions of the parameters used in the following example.
+
+Use the `paymasterService` capability on the smart wallet client `sendCalls` or `prepareCalls` actions.
+
+
+
+```ts title="sendCalls.ts"
+import { client, config } from "./client.ts";
+
+try {
+ const { preparedCallIds } = await client.sendCalls({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log(preparedCallIds);
+} catch (error) {
+ console.error(error);
+}
+```
+
+```ts title="client.ts"
+import "dotenv/config";
+import type { Hex } from "viem";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+export const config = {
+ policyId: process.env.ALCHEMY_POLICY_ID!,
+};
+
+const clientParams = {
+ transport: alchemy({
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(
+ process.env.PRIVATE_KEY! as Hex,
+ ),
+};
+
+const clientWithoutAccount = createSmartWalletClient(clientParams);
+
+const account = await clientWithoutAccount.requestAccount();
+
+export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+});
+```
+
+
diff --git a/fern/wallets/pages/transactions/sponsor-gas/index.mdx b/fern/wallets/pages/transactions/sponsor-gas/index.mdx
new file mode 100644
index 000000000..2788bdae2
--- /dev/null
+++ b/fern/wallets/pages/transactions/sponsor-gas/index.mdx
@@ -0,0 +1,77 @@
+---
+title: Sponsor gas
+description: Sponsor gas fees for your users
+slug: wallets/transactions/sponsor-gas
+---
+
+Gas fees are a significant barrier to entry for new users. With Gas Sponsorship, you can eliminate this friction by covering transaction costs for your users.
+
+## How it works
+
+When a user requests gas sponsorship using a configured policy, the policy engine will determine if that transaction is eligible for sponsorship.
+If eligible, when the user sends the transaction the Gas Manager will pay for the gas fee upfront. The Gas Manager will make a note of the sponsored cost
+and bill the sponsoring developer in fiat.
+
+## Prerequisites
+
+* API key from your [dashboard](https://dashboard.alchemy.com/apps)
+* [A gas sponsorship policy](https://dashboard.alchemy.com/gas-manager/policy/create).
+
+## Implementation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Advanced
+
+
+ Gas sponsorship also works with the prepare calls methods in the various frameworks. Usage of the capability will be the same as when using send calls. It is recommended to use prepare calls if you want to inspect the prepared call prior to prompting the user for signature.
+
+
+
+ See the [`usePrepareCalls` react
+ hook](/wallets/reference/account-kit/react/hooks/usePrepareCalls)
+
+
+
+ See the [`prepareCalls` SDK
+ reference](/wallets/reference/account-kit/wallet-client/functions/prepareCalls)
+
+
+
+ See the [`wallet_prepareCalls` API
+ reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+
+
+
+
+
+ Developers can configure multiple policy IDs for use in gas sponsorship. The
+ backend will choose the first policy ID that where the transaction is eligible
+ for sponsorship. This is done by passing an array of policy IDs instead of a
+ single policy ID to the sponsor gas capability. See the [`wallet_prepareCalls`
+ API
+ parameters](https://www.alchemy.com/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+ for reference to the `paymasterService.policyIds` parameter.
+
+
+## Next steps
+
+Build more:
+
+* [Pay gas with any token](/wallets/transactions/pay-gas-with-any-token)
+
+Troubleshooting:
+
+* [Gas manager errors](/reference/gas-manager-errors)
diff --git a/fern/wallets/pages/transactions/sponsor-gas/overview.mdx b/fern/wallets/pages/transactions/sponsor-gas/overview.mdx
new file mode 100644
index 000000000..cf3b921cf
--- /dev/null
+++ b/fern/wallets/pages/transactions/sponsor-gas/overview.mdx
@@ -0,0 +1,78 @@
+---
+title: Gasless transactions
+description: Sponsor gas fees for your users
+slug: wallets/transactions/sponsor-gas/overview
+---
+
+Gas fees are a significant barrier to entry for new users. With gas sponsorship, you can eliminate this friction by covering gas costs for your users.
+
+## Get Started
+
+Start sponsoring gas in minutes:
+
+1. **Create a policy** in your [Alchemy dashboard](https://dashboard.alchemy.com/apps/latest/services/gas-manager/configuration) and configure sponsorship rules
+2. **Send sponsored transactions** using your policy ID
+
+No need to manage crypto or pre-fund accounts. Alchemy fronts the gas for your users' transactions and you pay the equivalent USD amount.
+
+Get started [here](/wallets/transactions/sponsor-gas)!
+
+## Sponsorship options
+
+
+
+ Cover all gas fees. Best for onboarding and growth
+
+
+ Set spending limits and rules to control costs
+
+
+ Let users pay gas with USDC, USDT, or any token
+
+
+ Sponsor transaction fees on Solana
+
+
+
+### Easy management
+
+Track and optimize your sponsorship spending in real-time through the Alchemy dashboard and APIs.
+
+
+
+ No need to pre-fund wallets or manage tokens. We front the gas for you and
+ bill in USD.
+
+
+ Monitor spending as transactions happen with live dashboards
+
+
+ Get notified when approaching limits and pre-deposit to scale up instantly
+
+
+ Programmatically manage policies, retrieve stats, and monitor sponsorships
+ via APIs
+
+
+
+[Start Building →](/wallets/transactions/sponsor-gas)
diff --git a/fern/wallets/pages/transactions/sponsor-gas/react.mdx b/fern/wallets/pages/transactions/sponsor-gas/react.mdx
new file mode 100644
index 000000000..9c56f8049
--- /dev/null
+++ b/fern/wallets/pages/transactions/sponsor-gas/react.mdx
@@ -0,0 +1,65 @@
+Required SDK version: ^v4.61.0
+
+Use the `useSendCalls` hook to sponsor gas for a transaction.
+
+The gas sponsorship policy ID can be configured:
+
+1. In the `AlchemyAccountProvider` component configuration. Used by default if configured.
+2. In the `paymasterService` capabilities `policyId` parameter. Overrides the component configuration.
+
+**Prerequisites**
+
+* [Smart Wallets installed and configured in your project](/wallets/pages/react/setup.mdx).
+
+See the [`useSendCalls` SDK reference](/wallets/reference/account-kit/react/hooks/useSendCalls) for parameter descriptions.
+
+
+ ```tsx title="sendCalls.tsx" focus={5-6,14-21}
+ import { useSendCalls, useSmartAccountClient } from "@account-kit/react";
+ import { config } from "./config.ts";
+
+ export default function SendCalls() {
+ const { client } = useSmartAccountClient({});
+ const { sendCallsAsync } = useSendCalls({ client });
+
+ const handleSend = async () => {
+ if (!client) {
+ throw new Error("Smart account client not connected");
+ }
+
+ try {
+ const { ids } = await sendCallsAsync({
+ // OPTIONAL: configure here to override a policyId set
+ // in the `AlchemyAccountProvider` configuration.
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Transaction sent with ID:", ids[0]);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ return Click to Send;
+ }
+ ```
+
+ ```ts title="config.ts"
+ import { Address, toHex } from "viem";
+
+ export const config = {
+ policyId: process.env.ALCHEMY_POLICY_ID!,
+ };
+ ```
+
diff --git a/fern/wallets/pages/transactions/swap-tokens/api.mdx b/fern/wallets/pages/transactions/swap-tokens/api.mdx
new file mode 100644
index 000000000..37c61a02b
--- /dev/null
+++ b/fern/wallets/pages/transactions/swap-tokens/api.mdx
@@ -0,0 +1,214 @@
+You will need to fill in values wrapped in curly braces like `{SIGNER_ADDRESS}`.
+
+
+
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{apiKey} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_requestAccount",
+ "params": [
+ {
+ "signerAddress": "{SIGNER_ADDRESS}"
+ }
+ ]
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "accountAddress": "ACCOUNT_ADDRESS",
+ "id": "ACCOUNT_ID"
+ }
+}
+```
+
+For other potential responses, [check out the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-request-account)
+
+
+
+
+
+Note that `postCalls` are optional and allow you to batch an array of calls after the swap.
+
+
+ If you're using an EOA or just want the raw array of calls returned, pass the
+ optional parameter `"returnRawCalls": "true"`, this will return a `calls`
+ array.
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{API_KEY} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "method": "wallet_requestQuote_v0",
+ "params": [
+ {
+ "from": "{ACCOUNT_ADDRESS_FROM_STEP_1}",
+ "chainId": "{CHAIN_ID}",
+ "fromToken": "{FROM_TOKEN}",
+ "toToken": "{TO_TOKEN}",
+ "fromAmount": "{FROM_AMOUNT_HEXADECIMAL}",
+ "postCalls": [{
+ "to:" "{POSTCALL_TO_ADDRESS}",
+ "data": "{POSTCALL_DATA}",
+ "value": "{POSTCALL_VALUE}"
+ }],
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "{PAYMASTER_POLICY_ID}"
+ }
+ }
+ }
+ ]
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": 0,
+ "result": {
+ "rawCalls": false,
+ "chainId": "...",
+ "quote": {
+ "expiry": "EXPIRY",
+ "minimumToAmount": "MINIMUM_TO_AMOUNT",
+ "fromAmount": "FROM_AMOUNT"
+ },
+ "type": "user-operation-v070",
+ "data": "USER_OPERATION_DATA",
+ "signatureRequest": {
+ "type": "personal_sign",
+ "data": {
+ "raw": "..."
+ },
+ "rawPayload": "..."
+ },
+ "feePayment": {
+ "sponsored": true,
+ "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
+ "maxAmount": "..."
+ }
+ }
+}
+```
+
+Note the `signatureRequest`! This is what you now have to sign, and the returned `data` field is what you will need to send the transaction!
+
+
+
+
+ To sign the signature request, you should sign the `raw` field (note, this is not a string! You need to pass it to your signer as raw bytes, generally like so: `{ raw: "0x..." }`) with your signer of choice.
+
+ This should use the `personal_sign` RPC method, as noted by the `type` in the `signatureRequest`.
+
+ Alternatively, you can sign the raw payload with a simple `eth_sign` but this RPC method is not favored due to security concerns.
+
+
+
+
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{apiKey} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_sendPreparedCalls",
+ "params": [
+ {
+ "type": "user-operation-v070",
+ "data": "{DATA_FROM_STEP_2}",
+ "chainId": "{CHAIN_ID}",
+ "signature": {
+ "type": "secp256k1",
+ "data": "{SIGNATURE_FROM_STEP_3}"
+ }
+ }
+ ],
+ "id": 1
+ }'
+```
+
+This returns:
+
+```json
+{
+ "jsonrpc": "2.0",
+ "id": "1",
+ "result": {
+ "preparedCallIds": ["PREPARED_CALL_ID"]
+ }
+}
+```
+
+Note the `PREPARED_CALL_ID`! You need this to track call status in the next step.
+
+For other potential responses, [check out the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-send-prepared-calls)
+
+
+
+
+
+You can use the `wallet_getCallsStatus` endpoint to check up on the transaction's status.
+
+```bash
+curl -X POST https://api.g.alchemy.com/v2/{apiKey} \
+ -H "Content-Type: application/json" \
+ -d '{
+ "jsonrpc": "2.0",
+ "method": "wallet_getCallsStatus",
+ "params": [
+ [
+ "{PREPARED_CALL_ID_FROM_STEP_4}"
+ ]
+ ],
+ "id": 1
+ }'
+```
+
+This returns:
+
+```json
+{
+ "id": "1",
+ "jsonrpc": "2.0",
+ "result": {
+ "id": "PREPARED_CALL_ID",
+ "chainId": "CHAIN_ID",
+ "atomic": true,
+ "status": 200,
+ "receipts": [...]
+ }
+}
+```
+
+Note that the status codes match the following:
+| Code | Title |
+|------|--------------------------|
+| 100 | Pending |
+| 200 | Confirmed |
+| 400 | Offchain Failure |
+| 500 | Onchain Failure |
+| 600 | Partial Onchain Failure |
+
+To get your transaction hash, you can access `result.receipts[0].transactionHash`.
+
+For more details, check out [the API reference!](/docs/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-get-calls-status)
+
+
+
+
diff --git a/fern/wallets/pages/transactions/swap-tokens/client.mdx b/fern/wallets/pages/transactions/swap-tokens/client.mdx
new file mode 100644
index 000000000..a7cf1df2d
--- /dev/null
+++ b/fern/wallets/pages/transactions/swap-tokens/client.mdx
@@ -0,0 +1,92 @@
+Required SDK version: ^v4.65.0
+
+You'll need the following env variables:
+
+* `ALCHEMY_API_KEY`: An [Alchemy API Key](https://dashboard.alchemy.com/apps)
+* `ALCHEMY_POLICY_ID`: A [Gas Manager](https://dashboard.alchemy.com/gas-manager/policy/create) policy ID
+* `PRIVATE_KEY`: A private key for a signer
+
+
+ ```ts title="requestQuote.ts"
+ import { swapActions } from "@account-kit/wallet-client/experimental";
+ import { client } from "./client";
+
+ const account = await client.requestAccount();
+
+ // Add the swap actions to the client
+ const swapClient = client.extend(swapActions);
+
+ // Request the swap quote
+ const { quote, ...calls } = await swapClient.requestQuoteV0({
+ from: account.address,
+ fromToken: "0x...",
+ toToken: "0x...",
+ minimumToAmount: "0x...",
+ });
+
+ // Display the swap quote, including the minimum amount to receive and the expiry
+ console.log(quote);
+
+ // Assert that the calls are not raw calls.
+ // This will always be the case when requestQuoteV0 is used without the `returnRawCalls` option,
+ // the assertion is just needed for Typescript to recognize the result type.
+ if (calls.rawCalls) {
+ throw new Error("Expected user operation calls");
+ }
+
+ // Sign the quote, getting back prepared and signed calls
+ const signedCalls = await swapClient.signPreparedCalls(calls);
+
+ // Send the prepared calls
+ const { preparedCallIds } = await swapClient.sendPreparedCalls(signedCalls);
+
+ // Wait for the call to resolve
+ const callStatusResult = await swapClient.waitForCallsStatus({
+ id: preparedCallIds[0]!,
+ });
+
+ // Filter through success or failure cases
+ if (
+ callStatusResult.status !== "success" ||
+ !callStatusResult.receipts ||
+ !callStatusResult.receipts[0]
+ ) {
+ throw new Error(
+ `Transaction failed with status ${callStatusResult.status}, full receipt:\n ${JSON.stringify(callStatusResult, null, 2)}`,
+ );
+ }
+
+ console.log("Swap confirmed!");
+ console.log(
+ `Transaction hash: ${callStatusResult.receipts[0].transactionHash}`,
+ );
+ ```
+
+ ```ts title="client.ts"
+ import "dotenv/config";
+ import type { Hex } from "viem";
+ import { LocalAccountSigner } from "@aa-sdk/core";
+ import { alchemy, sepolia } from "@account-kit/infra";
+ import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+ const clientParams = {
+ transport: alchemy({
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ }),
+ chain: sepolia,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(
+ process.env.PRIVATE_KEY! as Hex,
+ ),
+ policyId: process.env.ALCHEMY_POLICY_ID!, // Optional: If you're using a gas manager policy
+ };
+
+ const clientWithoutAccount = createSmartWalletClient(clientParams);
+
+ const account = await clientWithoutAccount.requestAccount();
+
+ export const client = createSmartWalletClient({
+ ...clientParams,
+ account: account.address,
+ });
+ ```
+
diff --git a/fern/wallets/pages/transactions/swap-tokens/index.mdx b/fern/wallets/pages/transactions/swap-tokens/index.mdx
new file mode 100644
index 000000000..77d389d67
--- /dev/null
+++ b/fern/wallets/pages/transactions/swap-tokens/index.mdx
@@ -0,0 +1,91 @@
+---
+title: Same-chain swaps (Alpha)
+slug: wallets/transactions/swap-tokens
+---
+
+Swaps let you convert any token to any other token onchain. They're built natively into Smart Wallets and you can integrate in minutes.
+
+Smart Wallets also allow you to add actions after the swap completes. For example, you can deposit your newly swapped tokens into a DeFi protocol.
+
+Swaps work just like any other Smart Wallet transaction, so you can sponsor gas to do gasless swaps, or pay for gas in an ERC-20 token.
+
+[Cross-chain swaps are live! Send your first cross-chain swap now!](/docs/wallets/transactions/cross-chain-swap-tokens)
+
+
+ Swaps are in alpha. Note that there may be changes in the future to simplify
+ the endpoint/sdk. We will let you know if/when that happens.
+
+
+# The Swap flow
+
+## **Flow**
+
+1. Request a swap quote
+2. Sign the prepared swap calls (including any post swap action)
+3. Send prepared calls
+4. Wait for onchain confirmation
+
+## **Swap options**
+
+When requesting a swap quote, you can specify either an amount in, or a minimum amount out.
+
+```tsx
+// Mode 1: Swap exact input amount
+{
+ fromAmount: "0x2710";
+} // Swap exactly 0.01 USDC (10000 in hex, 6 decimals)
+
+// Mode 2: Get minimum output amount
+{
+ minimumToAmount: "0x5AF3107A4000";
+} // Get at least 0.0001 ETH (18 decimals). We calculate how much USDC you need to spend to get at least your desired ETH amount.
+```
+
+## Prerequisites
+
+Before you begin, ensure you have:
+
+* An [Alchemy API Key](https://dashboard.alchemy.com/apps)
+* If you're sponsoring gas, then a [Gas Manager](https://dashboard.alchemy.com/gas-manager/policy/create) policy
+* A small amount of USDC for testing (~$1 worth is enough!)
+ * **Important**: You'll need to send these tokens to your smart wallet address to be able to swap!
+* A signer to own the account and sign messages
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# FAQs
+
+## What chains are supported?
+
+Chains supported (for now) are: Ethereum, Arbitrum, Base, Berachain, BSC/BNB, Ink, Monad, Optimism, Polygon, Unichain and World Chain mainnets.
+
+## Does the Swap API support cross-chain swaps?
+
+Currently, the Swap API supports only single-chain swaps. Cross-chain swaps are coming soon!
+
+## How do you encode values?
+
+Values are simply passed as hexadecimal strings. The Swap API doesn't add complexity to consider decimals, so 0x01 is always the smallest amount of a given asset.
+1 ETH, or DAI (18 decimals) is `0xDE0B6B3A7640000`
+1 USDC (6 decimals) is `0xF4240`
+This removes any ambiguity— if it’s numerical, it’s a hex.
+
+## What is the expiry?
+
+The expiry is an informational indicator of when you can expect to be able to process the swap request. If you’re at/near the expiry, it might be a good time to request a new quote.
+
+## What if I’m using React?
+
+Use the `@account-kit/react` package to access a `smartWalletClient`. This package automatically uses the Signer for user authentication & signing. [Check out the docs for getting started with React to learn more](/wallets/react/quickstart).
diff --git a/fern/wallets/pages/transactions/swap-tokens/react.mdx b/fern/wallets/pages/transactions/swap-tokens/react.mdx
new file mode 100644
index 000000000..8699a3037
--- /dev/null
+++ b/fern/wallets/pages/transactions/swap-tokens/react.mdx
@@ -0,0 +1,149 @@
+Required SDK version: ^v4.70.0
+
+Use the `usePrepareSwap` hook to request swap quotes and the `useSignAndSendPreparedCalls` hook to execute token swaps on the same chain.
+
+**Prerequisites**
+
+* [Smart Wallets installed and configured in your project](/wallets/react/quickstart)
+* An [authenticated user](/wallets/authentication/overview)
+* Tokens in your smart account to swap
+
+
+ ```tsx title="swapTokens.tsx"
+ import {
+ useSmartAccountClient,
+ usePrepareSwap,
+ useSignAndSendPreparedCalls,
+ useWaitForCallsStatus,
+ useUser,
+ } from "@account-kit/react";
+
+ // Token addresses on Arbitrum
+ const TOKENS = {
+ NATIVE: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEee", // ETH
+ USDC: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
+ } as const;
+
+ export default function SwapTokens() {
+ const user = useUser();
+ const { client } = useSmartAccountClient({
+ accountParams: { mode: "7702" },
+ });
+
+ const { prepareSwapAsync, isPreparingSwap } = usePrepareSwap({
+ client,
+ });
+
+ const {
+ signAndSendPreparedCallsAsync,
+ isSigningAndSendingPreparedCalls,
+ signAndSendPreparedCallsResult,
+ } = useSignAndSendPreparedCalls({ client });
+
+ const {
+ data: statusResult,
+ isLoading: isWaitingForConfirmation,
+ error,
+ } = useWaitForCallsStatus({
+ client,
+ id: signAndSendPreparedCallsResult?.preparedCallIds[0],
+ });
+
+ const handleSwap = async () => {
+ if (!client?.account.address) {
+ throw new Error("No account connected");
+ }
+
+ try {
+ // Step 1: Request swap quote
+ const result = await prepareSwapAsync({
+ from: client.account.address,
+ fromToken: TOKENS.NATIVE,
+ toToken: TOKENS.USDC,
+ fromAmount: "0x5af3107a4000", // 0.0001 ETH
+ // postCalls: [{
+ // to: "0x...",
+ // data: "0x...",
+ // value: "0x0"
+ // }], // Optional: batch additional calls after the swap
+ });
+
+ const { quote, ...calls } = result;
+ console.log("Swap quote:", quote);
+
+ // Ensure we have user operation calls
+ if (calls.rawCalls) {
+ throw new Error("Expected user operation calls");
+ }
+
+ // Step 2: Sign and send the prepared calls
+ const callIds = await signAndSendPreparedCallsAsync(calls);
+
+ console.log("Transaction sent");
+ console.log("Call ID:", callIds?.preparedCallIds[0]);
+ } catch (error) {
+ console.error("Swap failed:", error);
+ }
+ };
+
+ if (!user) {
+ return
Please log in to use swap functionality
;
+ }
+
+ return (
+
+
+ {isPreparingSwap
+ ? "Requesting quote..."
+ : isSigningAndSendingPreparedCalls
+ ? "Signing and sending..."
+ : "Swap ETH to USDC"}
+
+
+ {signAndSendPreparedCallsResult && (
+
+ );
+ }
+ ```
+
+
+## How it works
+
+1. **Request quote**: `usePrepareSwap` requests a swap quote and prepares the transaction calls
+2. **Destructure result**: Extract `quote` for display and `calls` for signing
+3. **Sign and send**: `useSignAndSendPreparedCalls` signs and submits the transaction
+4. **Track status**: `useWaitForCallsStatus` monitors the transaction until confirmation
+
+The quote includes the minimum amount you'll receive and the expiry time for the quote.
+
+## Swap options
+
+You can specify either an exact input amount or a minimum output amount:
+
+```tsx
+// Mode 1: Swap exact input amount
+await prepareSwapAsync({
+ from: address,
+ fromToken: "0x...",
+ toToken: "0x...",
+ fromAmount: "0x2710", // Swap exactly 0.01 USDC (10000 in hex, 6 decimals)
+});
+
+// Mode 2: Get minimum output amount
+await prepareSwapAsync({
+ from: address,
+ fromToken: "0x...",
+ toToken: "0x...",
+ minimumToAmount: "0x5AF3107A4000", // Get at least 0.0001 ETH (18 decimals)
+});
+```
diff --git a/fern/wallets/pages/transactions/using-eip-7702/api.mdx b/fern/wallets/pages/transactions/using-eip-7702/api.mdx
new file mode 100644
index 000000000..ff6f42aae
--- /dev/null
+++ b/fern/wallets/pages/transactions/using-eip-7702/api.mdx
@@ -0,0 +1,41 @@
+See the [`wallet_prepareCalls` API
+reference](/wallets/api-reference/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+for full descriptions of the parameters used in the following example.
+
+
+
+ Prepare calls using the `eip7702Auth` capability.
+```bash
+curl --request POST \
+ --url https://api.g.alchemy.com/v2/$ALCHEMY_API_KEY \
+ --header 'accept: application/json' \
+ --data '
+{
+ "id": 1,
+ "jsonrpc": "2.0",
+ "method": "wallet_prepareCalls",
+ "params": [
+ {
+ "capabilities": {
+ "paymasterService": {
+ "policyId": "'$ALCHEMY_POLICY_ID'"
+ },
+ "eip7702Auth": true
+ },
+ "calls": [
+ {
+ "to": "0x0000000000000000000000000000000000000000"
+ }
+ ],
+ "from": "'$SIGNER_ADDRESS'",
+ "chainId": "'$CHAIN_ID_HEX'"
+ }
+ ]
+}'
+```
+
+
+ Sign the returned signature request and send using `wallet_sendPreparedCalls`.
+ See "Usage with prepare calls" for details on the authorization signature request.
+
+
diff --git a/fern/wallets/pages/transactions/using-eip-7702/client.mdx b/fern/wallets/pages/transactions/using-eip-7702/client.mdx
new file mode 100644
index 000000000..3ebaa59e4
--- /dev/null
+++ b/fern/wallets/pages/transactions/using-eip-7702/client.mdx
@@ -0,0 +1,71 @@
+Required SDK version: ^v4.61.0
+
+Use the `eip7702Auth` capability on the smart wallet client `sendCalls` action. `sendCalls` will automatically
+sign any required authorization requests to delegate the EOA to Modular Account v2.
+
+
+ If the signing EOA is delegated to a different smart contract, `sendCalls`
+ will request to re-delegate to Modular Account v2, overriding any previous
+ delegation.
+
+
+See the [`sendCalls` SDK
+reference](/wallets/reference/account-kit/wallet-client/functions/sendCalls)
+for full descriptions of the parameters used in the following example.
+
+
+
+```tsx title="sendCalls.ts"
+import { client, config } from "./client.ts";
+
+try {
+ const { preparedCallIds } = await client.sendCalls({
+ capabilities: {
+ paymasterService: {
+ policyId: config.policyId,
+ },
+ eip7702Auth: true,
+ },
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log(preparedCallIds);
+} catch (error) {
+ console.error(error);
+}
+```
+
+```ts title="client.ts"
+import "dotenv/config";
+import type { Hex } from "viem";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createSmartWalletClient } from "@account-kit/wallet-client";
+
+export const config = {
+ policyId: process.env.ALCHEMY_POLICY_ID!,
+};
+
+const signer = LocalAccountSigner.privateKeyToAccountSigner(
+ process.env.PRIVATE_KEY! as Hex,
+);
+const signerAddress = await signer.getAddress();
+
+export const client = createSmartWalletClient({
+ transport: alchemy({
+ apiKey: process.env.ALCHEMY_API_KEY!,
+ }),
+ chain: sepolia,
+ signer,
+ // hoist the signer address as the account when using 7702 mode
+ account: signerAddress,
+});
+```
+
+
diff --git a/fern/wallets/pages/transactions/using-eip-7702/index.mdx b/fern/wallets/pages/transactions/using-eip-7702/index.mdx
new file mode 100644
index 000000000..81e68ea0d
--- /dev/null
+++ b/fern/wallets/pages/transactions/using-eip-7702/index.mdx
@@ -0,0 +1,108 @@
+---
+title: Using EIP-7702
+description: Upgrade to Smart Wallets with EIP-7702
+slug: wallets/transactions/using-eip-7702
+---
+
+Upgrade your user's accounts to Smart Wallets using [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702).
+This gives users access to all of the capabilities of Smart Wallets including gas sponsorship, batching, and more.
+
+## How it works
+
+EIP-7702 enables EOAs (Externally Owned Accounts) to delegate control to smart wallets that can execute code directly from their addresses. Users simply sign an authorization
+payload to delegate, then their account can function as a smart wallet with access to all of the user's assets. Wallet APIs will prompt the user to sign this authorization
+when the delegation is missing on the chain they're interacting with.
+
+## Prerequisites
+
+* API key from your [dashboard](https://dashboard.alchemy.com/apps)
+
+## Implementation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Advanced
+
+
+ EIP-7702 is particularly useful when you have existing wallets that are being used as EOAs, but want access to smart wallet capabilities while allowing users
+ to keep their existing address and avoid transferring their assets. With EIP-7702, you can upgrade your users' EOAs directly to a Smart Wallet at their current address.
+
+ See a full list of recommendations [here](https://www.alchemy.com/blog/eip-7702-ethereum-pectra-hardfork#for-developers-that-havent-yet-deployed-their-application).
+
+
+
+ The EIP-7702 capability works with the prepare calls function in a similar way to its usage in send calls.
+ However, you are required to handle the authorization signature request in your code's logic.
+
+ When a delegation is required the response to prepare calls will return an array of signature requests:
+
+ 1. An authorization signature request
+ 2. A user operation signature request
+
+ Both of these requests need to be signed and included in the `sendPreparedCalls` request. `sendPreparedCalls` takes
+ an array of signed calls for this purpose. These signatures are combined into a single transaction that is relayed onchain.
+ Once the delegation is signed, as long as the user doesn't delegate to another account, subsequent requests will only
+ contain the user operation signature request.
+
+ Signing an authorization signature request requires logic specific to the wallet being used. Examples:
+
+ * [Viem](https://viem.sh/docs/eip7702/signAuthorization)
+ * [Cast](https://getfoundry.sh/cast/reference/wallet/sign-auth/)
+
+
+ Most external wallet implementations, including browser wallets, will block
+ requests to sign authorizations. This is done for security reasons. To use
+ EIP-7702 ensure that your user's wallets support signing authorizations.
+
+
+
+
+ See the [`usePrepareCalls` react
+ hook](/wallets/reference/account-kit/react/hooks/usePrepareCalls)
+
+
+
+ See the [`prepareCalls` SDK
+ reference](/wallets/reference/account-kit/wallet-client/functions/prepareCalls)
+
+
+
+ See the [`wallet_prepareCalls` API
+ reference](/wallets/api/smart-wallets/wallet-api-endpoints/wallet-api-endpoints/wallet-prepare-calls)
+
+
+
+
+
+ The `eip7702Auth` capability supports the interface defined in [ERC-7902](https://eips.ethereum.org/EIPS/eip-7902).
+ However, it currently only supports a single delegation address: `0x69007702764179f14F51cdce752f4f775d74E139`. All other addresses will be rejected.
+ It's recommended to use `eip7702Auth: true` to avoid the extra complexity.
+
+ Once your account is delegated, it will remain delegated until the delegation is replaced or removed. We currently only support relaying delegations
+ for Modular Account v2. If you wish to replace or remove this delegation, you'll need to relay the delegation yourself or use a third party service.
+ Viem has a guide for this [here](https://viem.sh/docs/eip7702/contract-writes).
+
+ To reset an account back to a pure EOA with no delegation, delegate the account to `0x0000000000000000000000000000000000000000` as defined in [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702#behavior).
+
+
+
+
+
+
+## Next steps
+
+Build more:
+
+* [Sponsor gas](/wallets/transactions/sponsor-gas)
diff --git a/fern/wallets/pages/transactions/using-eip-7702/react.mdx b/fern/wallets/pages/transactions/using-eip-7702/react.mdx
new file mode 100644
index 000000000..87de7f4d9
--- /dev/null
+++ b/fern/wallets/pages/transactions/using-eip-7702/react.mdx
@@ -0,0 +1,50 @@
+Required SDK version: ^v4.61.0
+
+To use Smart Wallets in EIP-7702 mode, you must specify the mode when constructing the smart account client with `useSmartAccountClient`.
+This will allow the authorized EOA to act as a smart wallet with the same address as the EOA. The `useSendCalls` hook will sign the required
+delegation payload the first time that a user interacts with a new chain. This payload is then relayed on chain with the user's first transaction.
+
+**Prerequisites:**
+
+* [Smart Wallets installed and configured in your project](/wallets/react/setup)
+
+See the [`useSendCalls` SDK reference](/wallets/reference/account-kit/react/hooks/useSendCalls) for parameter descriptions.
+
+
+ ```tsx title="sendCalls.tsx" focus={4-8,17-25}
+ import { useSendCalls, useSmartAccountClient } from "@account-kit/react";
+
+ export default function SendCalls() {
+ const { client } = useSmartAccountClient({
+ accountParams: {
+ mode: "7702",
+ },
+ });
+ const { sendCallsAsync } = useSendCalls({ client });
+
+ const handleSend = async () => {
+ if (!client) {
+ throw new Error("Smart account client not connected");
+ }
+
+ try {
+ const { ids } = await sendCallsAsync({
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ value: "0x00",
+ data: "0x",
+ },
+ ],
+ });
+
+ console.log("Transaction sent with ID:", ids[0]);
+ } catch (error) {
+ console.error(error);
+ }
+ };
+
+ return Click to Send;
+ }
+ ```
+
diff --git a/fern/wallets/pages/transactions/using-eip-7702/third-party.mdx b/fern/wallets/pages/transactions/using-eip-7702/third-party.mdx
new file mode 100644
index 000000000..6cc829b19
--- /dev/null
+++ b/fern/wallets/pages/transactions/using-eip-7702/third-party.mdx
@@ -0,0 +1,220 @@
+If you have an existing custom signer or another third-party embedded wallet provider, you can upgrade your embedded EOAs to smart wallets by connecting your existing signer.
+This will allow you to use EIP-7702 to get features like gas sponsorship, batching, and more.
+
+
+ **Requirement:** Your signer or embedded wallet provider must support signing
+ EIP-7702 authorizations in order to delegate to a smart account.
+
+
+To bring your own signer, you create a `SmartAccountSigner` that implements `signAuthorization`. See the details for the interface requirements [here](/wallets/resources/types#smartaccountsigner).
+
+For example, you can upgrade an existing Privy embedded EOA by extending a Wallet Client to use the `sign7702Authorization` action exposed by Privy.
+See full example code [here](https://github.com/alchemyplatform/alchemy-wallets-7702-thirdparty-example).
+
+The bulk of the logic happens in [`use-embedded-smart-wallet.ts`](https://github.com/alchemyplatform/alchemy-wallets-7702-thirdparty-example/blob/main/hooks/use-smart-embedded-wallet.ts).
+In this react hook, a Privy embedded wallet is wrapped in a `WalletClientSigner` that supports `signAuthorization`.
+This signer is then passed to `createSmartWalletClient` to construct a client for sending transactions.
+
+
+
+```ts twoslash title="use-smart-embedded-wallet.ts"
+import { Address, Authorization, createWalletClient, custom } from "viem";
+import { useSign7702Authorization } from "@privy-io/react-auth";
+import { AuthorizationRequest, WalletClientSigner } from "@aa-sdk/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { useEffect, useState } from "react";
+import {
+ createSmartWalletClient,
+ SmartWalletClient,
+} from "@account-kit/wallet-client";
+import { ConnectedWallet as PrivyWallet } from "@privy-io/react-auth";
+
+/\*_ Creates an Alchemy Smart Wallet client for an embedded Privy wallet using EIP-7702. _/;
+export const useSmartEmbeddedWallet = ({
+ embeddedWallet,
+}: {
+ embeddedWallet: PrivyWallet;
+}) => {
+ const { signAuthorization } = useSign7702Authorization();
+ const [client, setClient] = useState();
+
+ useEffect(() => {
+ const apiKey = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY;
+ if (!apiKey) {
+ throw new Error("Missing NEXT_PUBLIC_ALCHEMY_API_KEY");
+ }
+ (async () => {
+ const provider = await embeddedWallet.getEthereumProvider();
+
+ const baseSigner = new WalletClientSigner(
+ createWalletClient({
+ account: embeddedWallet.address as Address,
+ chain: sepolia,
+ transport: custom(provider),
+ }),
+ "privy",
+ );
+
+ const signer = {
+ ...baseSigner,
+ signAuthorization: async (
+ unsignedAuth: AuthorizationRequest,
+ ): Promise> => {
+ const signature = await signAuthorization({
+ ...unsignedAuth,
+ contractAddress:
+ unsignedAuth.address ?? unsignedAuth.contractAddress,
+ });
+
+ return {
+ ...unsignedAuth,
+ ...signature,
+ };
+ },
+ };
+
+ const client = createSmartWalletClient({
+ chain: sepolia,
+ transport: alchemy({
+ apiKey,
+ }),
+ signer,
+ policyId: process.env.NEXT_PUBLIC_ALCHEMY_POLICY_ID,
+ });
+
+ setClient(client);
+ })();
+ }, [embeddedWallet, signAuthorization]);
+
+ return { client };
+};
+```
+
+
+
+When using the `SmartWalletClient` you must set the `eip7702Auth` capability to `true`, as shown in [`smart-wallet-demo.tsx`](https://github.com/alchemyplatform/alchemy-wallets-7702-thirdparty-example/blob/main/components/smart-wallet-demo.tsx)
+
+
+
+```tsx twoslash title="smart-wallet-demo.tsx" focus={11, 27-30}
+import { ConnectedWallet as PrivyWallet } from "@privy-io/react-auth";
+import { useSmartEmbeddedWallet } from "../hooks/use-smart-embedded-wallet";
+import { useCallback, useState } from "react";
+import type { Address, Hex } from "viem";
+
+export const SmartWalletDemo = ({
+ embeddedWallet,
+}: {
+ embeddedWallet: PrivyWallet;
+}) => {
+ const { client } = useSmartEmbeddedWallet({ embeddedWallet });
+
+ const [status, setStatus] = useState<
+ | { status: "idle" | "error" | "sending" }
+ | { status: "success"; txHash: Hex }
+ >({ status: "idle" });
+
+ const delegateAndSend = useCallback(async () => {
+ if (!client) {
+ return;
+ }
+
+ setStatus({ status: "sending" });
+ try {
+ const {
+ preparedCallIds: [callId],
+ } = await client.sendCalls({
+ capabilities: {
+ eip7702Auth: true,
+ },
+ from: embeddedWallet.address as Address,
+ calls: [
+ {
+ to: "0x0000000000000000000000000000000000000000",
+ data: "0x",
+ },
+ ],
+ });
+ if (!callId) {
+ throw new Error("Missing call id");
+ }
+
+ const { receipts } = await client.waitForCallsStatus({ id: callId });
+ if (!receipts?.length) {
+ throw new Error("Missing transaction receipts");
+ }
+ const [receipt] = receipts;
+ if (receipt?.status !== "success") {
+ throw new Error("Transaction failed");
+ }
+ setStatus({ status: "success", txHash: receipt.transactionHash });
+ } catch (err) {
+ console.error("Transaction failed:", err);
+ setStatus({ status: "error" });
+ }
+ }, [client, embeddedWallet]);
+
+ return (
+
+ You've successfully upgraded your EOA to a smart account and sent
+ your first sponsored transaction.{" "}
+
+ Keep building
+
+ .
+
+
+ Transaction Hash:{" "}
+ {status.txHash}
+
+
+ )}
+ {status.status === "error" && (
+
+
+ Transaction Failed
+
+
+ There was an error sending your sponsored transaction. Please try
+ again.
+
+
+ )}
+
+ );
+};
+```
+
+
diff --git a/fern/wallets/pages/troubleshooting/server-side-rendering.mdx b/fern/wallets/pages/troubleshooting/server-side-rendering.mdx
new file mode 100644
index 000000000..ba61535af
--- /dev/null
+++ b/fern/wallets/pages/troubleshooting/server-side-rendering.mdx
@@ -0,0 +1,263 @@
+---
+title: Server-side Rendering
+description: Learn how to use Smart Wallets with server-side rendering.
+slug: wallets/troubleshooting/ssr
+---
+
+> ⚠️ **Common Issue**: If SSR is not set up correctly, you may see sessions resetting or users getting logged out unexpectedly. Make sure to follow this guide fully to preserve session state.
+
+When using Smart Wallets in a server-side rendered setting, you will see inconsistencies of the user state between the server and the client. This will lead to flashes of content when a user is logged in. To avoid this, the account state can be optimistically loaded on the server and passed to the client.
+
+## Update your config
+
+To enable server-side rendering support, you need to set `ssr: true` when creating a config.
+
+
+
+
+When using React, you should also make the config a function so that you can call it once per request, which allows for request-based isolation of the account state.
+
+```ts twoslash config.ts {9}
+import { createConfig } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+export const config = () =>
+ createConfig({
+ // required
+ transport: alchemy({ rpcUrl: "/api/rpc" }),
+ chain: sepolia,
+ ssr: true,
+ });
+```
+
+This setting will defer hydration of the account state to the client after the initial mount.
+
+## Persisting the Account State
+
+### Cookie Storage
+
+To consistently pass the state between the server and the client, you can pass in a cookie storage to the `config` object created above. The cookie storage allows the client state to be written serialized to a cookie which can be passed along to the server on each request. This allows the server to have access to certain parts of the account state when rendering, ensuring a consistent render between client and server (eg. user's address displayed in the top nav). Instances which can only be created on the client will still not be available on the server, however. This includes the signer or smart contract account instances.
+
+```ts twoslash config.ts {1,12-13}
+import { createConfig, cookieStorage } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { QueryClient } from "@tanstack/react-query";
+
+export const queryClient = new QueryClient();
+
+export const config = () =>
+ createConfig({
+ // required
+ transport: alchemy({ rpcUrl: "/api/rpc" }),
+ chain: sepolia,
+ ssr: true,
+ storage: cookieStorage,
+ });
+```
+
+Now, depending on your application, you can get the state from cookies and pass in the `initialState` to the `AlchemyAccountProvider` to hydrate the account state on the client.
+
+### Next.js App Directory
+
+If you are using NextJS App Directory, you can read the cookie state and pass it to the providers like so:
+
+
+
+```tsx app/layout.tsx
+// @noErrors
+import React from "react";
+import { cookieToInitialState } from "@account-kit/core";
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import { headers } from "next/headers";
+import { config } from "./config";
+import "./globals.css";
+import { Providers } from "./providers";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "Embedded Accounts Getting Started",
+ description: "Embedded Accounts Quickstart Guide",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ // This will allow us to persist state across page boundaries
+ const initialState = cookieToInitialState(
+ // the config here is just used to compute the initial state
+ config(),
+ headers().get("cookie") ?? undefined,
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+```
+
+```tsx app/providers.tsx
+// @noErrors
+"use client";
+
+import React, { useRef } from "react";
+import { AlchemyClientState } from "@account-kit/core";
+import {
+ AlchemyAccountProvider,
+ type AlchemyAccountsConfigWithUI,
+} from "@account-kit/react";
+import { QueryClientProvider } from "@tanstack/react-query";
+import { PropsWithChildren, Suspense } from "react";
+import { config, queryClient } from "./config";
+
+export const Providers = (
+ props: PropsWithChildren<{ initialState?: AlchemyClientState }>,
+) => {
+ const ref = useRef();
+ if (!ref.current) {
+ ref.current = config();
+ }
+
+ return (
+
+
+
+ {props.children}
+
+
+
+ );
+};
+```
+
+```tsx config.ts
+import { createConfig, cookieStorage } from "@account-kit/react";
+import { sepolia, alchemy } from "@account-kit/infra";
+import { QueryClient } from "@tanstack/react-query";
+
+export const queryClient = new QueryClient();
+
+// When using SSR, you need to be able to create a config per request
+// This is to avoid sharing state between requests (eg. signed in users)
+export const config = () =>
+ createConfig({
+ transport: alchemy({ rpcUrl: "/api/rpc" }),
+ chain: sepolia,
+ ssr: true,
+ storage: cookieStorage,
+ });
+```
+
+
+
+
+
+
+```ts
+import { createConfig } from "@account-kit/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+export const config = createConfig({
+ transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
+ chain: sepolia,
+ ssr: true,
+});
+```
+
+This setting will defer hydration of the account kit state until you can call the `hydrate` method on mount.
+
+## Hydrate the Account State
+
+Now that you've set up your config for SSR, you will have to manually hydrate the state on the client. This can be done by calling the `hydrate` method exported by Smart Wallets core.
+
+
+
+```ts hydrate.ts
+import { hydrate } from "@account-kit/core";
+import { config } from "./config";
+
+const { onMount } = hydrate(config);
+
+// when your component has mounted on the client, call the onMount method
+// how you do this will depend on your framework, but here we'll just check for `window`
+// to be defined
+if (typeof window !== "undefined") {
+ onMount();
+}
+```
+
+```ts config.ts
+import { createConfig } from "@account-kit/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+
+export const config = createConfig({
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ ssr: true,
+});
+```
+
+
+
+## Persisting the Account State
+
+To consistently pass the state between the server and the client, you can pass in a cookie storage to the `config` object created above. The cookie storage allows the client state to be written serialized to a cookie which can be passed along to the server on each request. This allows the server to have access to certain parts of the account state when rendering, ensuring a consistent render between client and server (eg. user's address displayed in the top nav). Instances which can only be created on the client will still not be available on the server, however. This includes the signer or smart contract account instances.
+
+```ts twoslash {7-8}
+import { createConfig, cookieStorage } from "@account-kit/core"; // Add cookieStorage import
+import { sepolia, alchemy } from "@account-kit/infra";
+
+export const config = createConfig({
+ transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
+ chain: sepolia,
+ ssr: true,
+ storage: cookieStorage,
+});
+```
+
+Now you can get the initial state from cookies and pass it to the `hydrate` method to hydrate the account state on the client or on the server.
+
+
+
+```ts hydrate.ts {7}
+import { cookieToInitialState, hydrate } from "@account-kit/core";
+import { config } from "./config";
+
+// this is an example of how to get the cookie on the client
+// but on the server you might get it from the `req.cookies` object
+// it all depends on your framework
+const initialState = cookieToInitialState(config, document.cookie);
+const { onMount } = hydrate(config, initialState);
+
+if (typeof window !== "undefined") {
+ onMount();
+}
+```
+
+```ts config.ts {7-8}
+import { createConfig, cookieStorage } from "@account-kit/core";
+import { sepolia, alchemy } from "@account-kit/infra";
+
+export const config = createConfig({
+ transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
+ chain: sepolia,
+ ssr: true,
+ storage: cookieStorage,
+});
+```
+
+
+
+
+
diff --git a/fern/wallets/pages/troubleshooting/wallet-apis-errors.mdx b/fern/wallets/pages/troubleshooting/wallet-apis-errors.mdx
new file mode 100644
index 000000000..61ad3ee88
--- /dev/null
+++ b/fern/wallets/pages/troubleshooting/wallet-apis-errors.mdx
@@ -0,0 +1,143 @@
+---
+title: Wallet API Errors
+description: Learn how to troubleshoot errors from Wallet APIs
+slug: wallets/troubleshooting/wallet-apis-errors
+---
+
+When working with Wallet APIs, you may encounter various errors. Here are some common errors and how to troubleshoot them.
+
+### Replacement underpriced
+
+```json
+{
+ "code": -32602,
+ "message": "replacement underpriced",
+ ...
+}
+```
+
+
+ Replacement underpriced errors occur when you attempt to send call from the
+ same sender before a pending call has been confirmed on-chain. Please follow
+ [these
+ recommendations](https://www.alchemy.com/support/how-to-fix-replacement-underpriced-errors).
+
+
+### Execution reverted
+
+```json
+{
+ "code": -32521,
+ "message": "execution reverted",
+ ...
+}
+```
+
+
+ This error normally happens if the calls you are attempting to make are
+ reverting on-chain. Double check that you are calling the correct method on
+ the correct contract address, you are using the correct chain, and that the
+ method arguments are correct and properly ABI-encoded.
+
+
+### ERC20: transfer amount exceeds balance
+
+```json
+{
+ "code": -32521,
+ "message": "ERC20: transfer amount exceeds balance",
+ ...
+}
+```
+
+
+ If [paying for gas with ERC20
+ tokens](https://www.alchemy.com/docs/wallets/transactions/pay-gas-with-any-token),
+ ensure the sender has the required balance.
+
+
+### AA23 reverted
+
+```json
+{
+ "code": -32500,
+ "message": "validation reverted: [reason]: AA23 reverted",
+ ...
+}
+```
+
+
+ AA23 errors happen if sender signature validation reverted or ran out of gas
+ (OOG). Please follow [these
+ recommendations](https://www.alchemy.com/support/how-to-resolve-entrypoint-aaxx-errors).
+
+
+### AA25 invalid account nonce
+
+```json
+{
+ "code": -32500,
+ "message": "validation reverted: [reason]: AA25 invalid account nonce",
+ ...
+}
+```
+
+
+ The nonce used is invalid. This usually happens if trying to reuse an old
+ nonce. Please follow [these
+ recommendations](https://www.alchemy.com/support/how-to-resolve-entrypoint-aaxx-errors).
+
+
+### Policy ID(s) not found
+
+```json
+{
+ "code": -32600,
+ "message": "Sponsorship failed: [invalid_argument] Policy ID(s) not found. Please ensure you're sending requests with the API key associated with the policies' app, that the policies are active, and the network is allowed.",
+ ...
+}
+```
+
+
+ Please ensure you're sending requests with the API key associated with the
+ policy's app, the policy is active, and the network is allowed. See [gas
+ sponsorship
+ docs](https://www.alchemy.com/docs/wallets/transactions/sponsor-gas) for more
+ info.
+
+
+### Invalid account signature
+
+```json
+{
+ "code": -32507,
+ "message": "invalid account signature",
+ ...
+}
+```
+
+
+ The call was rejected because it contains an invalid signature from the
+ sender. Be sure that you are correctly signing the signature request(s). Refer
+ to the [Wallet API
+ quickstart](/docs/wallets/smart-wallet-quickstart/api#3-sign-the-userop) for
+ more info.
+
+
+### Precheck failed: sender balance
+
+```json
+{
+ "code": -32000,
+ "message": "precheck failed: sender balance and deposit together is ____ but must be at least ____ to pay for this operation",
+ ...
+}
+```
+
+
+ The sender's balance is too low to cover gas fees. This normally happens when
+ you mistakenly forget to include your Gas Sponsorship Policy ID. See [gas
+ sponsorship
+ docs](https://www.alchemy.com/docs/wallets/transactions/sponsor-gas) for more
+ info.
+
diff --git a/fern/wallets/shared/core/config.mdx b/fern/wallets/shared/core/config.mdx
new file mode 100644
index 000000000..f496de86d
--- /dev/null
+++ b/fern/wallets/shared/core/config.mdx
@@ -0,0 +1,11 @@
+```ts config.ts
+import { createConfig } from "@account-kit/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+
+export const config = createConfig({
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ // optional if you want to sponsor gas
+ policyId: "YOUR_POLICY_ID",
+});
+```
diff --git a/fern/wallets/shared/core/ssr-config.mdx b/fern/wallets/shared/core/ssr-config.mdx
new file mode 100644
index 000000000..f02a47465
--- /dev/null
+++ b/fern/wallets/shared/core/ssr-config.mdx
@@ -0,0 +1,10 @@
+```ts config.ts
+import { createConfig } from "@account-kit/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+
+export const config = createConfig({
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+ chain: sepolia,
+ ssr: true,
+});
+```
diff --git a/fern/wallets/shared/create-gas-policy.mdx b/fern/wallets/shared/create-gas-policy.mdx
new file mode 100644
index 000000000..2daa3a19c
--- /dev/null
+++ b/fern/wallets/shared/create-gas-policy.mdx
@@ -0,0 +1,14 @@
+A gas manager policy is a set of rules that define which UOs are eligible for gas sponsorship. You can control which operations are eligible for sponsorship by defining rules:
+
+* **Spending rules**: limit the amount of money or the number of transactions that can be sponsored by this policy
+* **Allowlist**: restrict wallet addresses that are eligible for sponsorship. The policy will only sponsor gas for UOs that were sent by addresses on this list.
+* **Blocklist**: ban certain addresses from receiving sponsorship under this policy
+* **Policy duration**: define the duration of your policy and the sponsorship expiry period. This is the period for which the sponsor gas signature (gas sponsorship data) will remain valid once it is generated.
+
+To learn more about policy configuration, refer to the guide on [setting up a gas manager policy](https://alchemy.com/docs/setup-a-gas-manager-policy/?a=ak-docs).
+
+Once you have decided on policy rules for your app, [create a policy](https://dashboard.alchemy.com/gas-manager/policy/create/?a=ak-docs) in the Gas Manager dashboard.
+
+Now you should have a Gas policy created with a policy id you can use to sponsor gas for your users.
+
+
diff --git a/fern/wallets/shared/estimate-gas-with-any-token.mdx b/fern/wallets/shared/estimate-gas-with-any-token.mdx
new file mode 100644
index 000000000..424c045a8
--- /dev/null
+++ b/fern/wallets/shared/estimate-gas-with-any-token.mdx
@@ -0,0 +1,39 @@
+You can use the [alchemy_requestPaymasterTokenQuote](https://www.alchemy.com/docs/wallets/api/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-paymaster-token-quote) endpoint to do off-chain simulation to verify both the approval and the underlying userOp will succeed.
+
+
+
+Note that changes in blockchain's state could lead to different outcomes.
+
+
+
+Example:
+
+```tsx
+const userOp = await client?.buildUserOperation({
+ uo: ...
+})
+const quote = await client?.request({
+ method: 'alchemy_requestPaymasterTokenQuote',
+ params: [
+ {
+ policyId: policyId,
+ entryPoint: '0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789',
+ dummySignature: userOp.signature,
+ userOperation: {
+ sender: userOp.sender,
+ nonce: userOp.nonce,
+ initCode: userOp.initCode,
+ callData: userOp.callData,
+ signature: userOp.signature,
+ paymasterAndData: userOp.paymasterAndData,
+ maxFeePerGas: userOp.maxFeePerGas,
+ maxPriorityFeePerGas: userOp.maxPriorityFeePerGas,
+ callGasLimit: userOp.callGasLimit,
+ verificationGasLimit: userOp.verificationGasLimit,
+ preVerificationGas: userOp.preVerificationGas,
+ },
+ erc20Context: {tokenAddress: tokenAddress},
+ },
+ ],
+});
+```
diff --git a/fern/wallets/shared/get-api-key.mdx b/fern/wallets/shared/get-api-key.mdx
new file mode 100644
index 000000000..698551872
--- /dev/null
+++ b/fern/wallets/shared/get-api-key.mdx
@@ -0,0 +1,42 @@
+1. Get your **API key** by creating a new app in your [Alchemy Dashboard](https://dashboard.alchemy.com/apps)
+
+ Make sure **your desired network** is enabled for your app under the
+ Networks tab.
+
+
+2. Activate **smart wallets** for your app in your [dashboard](https://dashboard.alchemy.com/apps/latest/services/smart-wallets)
+
+ 1. Apply the config to your app from step 1
+
+
+
+ 2. Enable authentication methods you want to support.
+
+
+
+ **Email auth**
+
+ If you want to use email auth, toggle on email.
+
+ * For testing, use [http://localhost:3000](http://localhost:3000/)
+ as the **Redirect URL** (Note **http** not https)
+ * The user will be redirected to the **Redirect URL** if you use the magic link email flow
+ * Optionally stylize ✨ the email with your brand color and logo!
+
+
+
+
+
+ **Social auth**
+
+ If you want to enable social login, toggle which auth providers
+ you want to support.
+
+ * For testing, add [http://localhost:3000](http://localhost:3000/) as a **whitelisted origin**
+ * Add the link that your DApp will be running on to the **whitelisted origin** list
+ * Optionally enter your own OAuth credentials or use our defaults
+
+
+
+3. Create the config and copy the **API Key**
+
diff --git a/fern/wallets/shared/infra/api-endpoints/bundler.mdx b/fern/wallets/shared/infra/api-endpoints/bundler.mdx
new file mode 100644
index 000000000..70512e686
--- /dev/null
+++ b/fern/wallets/shared/infra/api-endpoints/bundler.mdx
@@ -0,0 +1,12 @@
+## What is the Bundler API?
+
+The Bundler APIs are a collection of ERC-4337 compliant JSON-RPC endpoints which makes it possible for users to work with user operations. Below are the Bundler API endpoints available and the corresponding docs for each of them.
+
+| Method | Description |
+| ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| [eth\_sendUserOperation](/wallets/api-reference/bundler-api/bundler-api-endpoints/eth-send-user-operation) | Submits a user operation to a Bundler. If the request is successful, the endpoint will return a user operation hash that the caller can use to look up the status of the user operation. If it fails, or another error occurs, an error code and description will be returned. |
+| [eth\_estimateUserOperationGas](/wallets/api-reference/bundler-api/bundler-api-endpoints/eth-estimate-user-operation-gas) | Estimates the gas values for a user operation. It returns the \[preVerificationGas], \[verificationGasLimit], and \[callGasLimit] values associated with the provided user operation. |
+| [eth\_getUserOperationByHash](/wallets/api-reference/bundler-api/bundler-api-endpoints/eth-get-user-operation-by-hash) | Returns a user operation based on the given user operation hash. It returns the user operation along with extra information including what block/transaction it was included in. If the operation has not yet been included, it will return \[null]. |
+| [eth\_getUserOperationReceipt](/wallets/api-reference/bundler-api/bundler-api-endpoints/eth-get-user-operation-receipt) | Returns a user operation receipt ( metadata associated with the given user operation ) based on the given user operation hash. It returns \[null] if the user operation has not yet been included. |
+| [rundler\_maxPriorityFeePerGas](/wallets/api-reference/bundler-api/bundler-api-endpoints/rundler-max-priority-fee-per-gas) | Returns a fee per gas that is an estimate of how much users should set as a priority fee in UOs for Rundler endpoints. |
+| [eth\_supportedEntryPoints](/wallets/api-reference/bundler-api/bundler-api-endpoints/eth-supported-entry-points) | Returns a list of Entrypoint contract addresses supported by the bundler endpoints. |
diff --git a/fern/wallets/shared/infra/api-endpoints/gas-manager-policy.mdx b/fern/wallets/shared/infra/api-endpoints/gas-manager-policy.mdx
new file mode 100644
index 000000000..14e04ed52
--- /dev/null
+++ b/fern/wallets/shared/infra/api-endpoints/gas-manager-policy.mdx
@@ -0,0 +1,14 @@
+## What are Gas Manager Admin APIs?
+
+The Gas Manager Admin APIs allow you to programmatically manage gas sponsorship policies. Developers can define and manage policies governing gas sponsorship behavior—such as spending limits and eligibility. Below are the Gas Manager Admin API endpoints available and the corresponding docs for each of them.
+
+| Method | Description |
+| ------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
+| [create-policy](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/create-policy) | Creates a new gas manager policy with the specified rules. |
+| [get-policy](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/get-policy) | Returns a policy by id. |
+| [replace-policy](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/replace-policy) | Replaces all rules in an existing policy by id. |
+| [delete-policy](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/delete-policy) | Deletes a policy by id. |
+| [get-all-policies](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/get-all-policies) | Returns all policies. The results are paginated. |
+| [update-policy-status](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/update-policy-status) | Modifies the status of a policy to either “active” or “inactive”. |
+| [get-policy-stats](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/get-policy-stats) | Returns stats about a policy specified by ID. |
+| [get-sponsorships](/wallets/api-reference/gas-manager-admin-api/admin-api-endpoints/get-sponsorships) | Returns a list of sponsorships associated with the specified policy ID. The results are paginated. |
diff --git a/fern/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx b/fern/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx
new file mode 100644
index 000000000..21b2feb51
--- /dev/null
+++ b/fern/wallets/shared/infra/api-endpoints/gas-manager-sponsorship.mdx
@@ -0,0 +1,12 @@
+# What are Gas Coverage API Endpoints?
+
+The Gas Coverage API is a set of methods that allow you to sponsor gas for your users’ transactions, abstracting away the concept of “gas” from them. Below are the Gas Manager Admin API endpoints available and the corresponding docs for each of them.
+
+| Method | Description |
+| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| [alchemy_requestGasAndPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-gas-and-paymaster-and-data) | **\[Start here / Recommended]** Requests gas sponsorship and gas estimates for a `UserOperation` on EVM networks. If approved, returns: 1. `paymaster` and `paymasterData` 2. gas and fee estimates. |
+| [alchemy_requestPaymasterAndData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-paymaster-and-data) | **\[To be deprecated]** Requests gas sponsorship for a `UserOperation` on EVM networks. If approved, returns`paymaster` and `paymasterData`. |
+| [pm_getPaymasterStubData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-stub-data) | Returns stub values to be used in paymaster-related fields of an unsigned user operation for gas estimation on EVM networks. |
+| [pm_getPaymasterData](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/pm-get-paymaster-data) | Returns values to be used in paymaster-related fields of a signed user operation on EVM networks. These are not stub values and will be included in a signed user operation as part of an `eth_sendUserOperation` call. |
+| [alchemy_requestFeePayer](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-fee-payer) | Request gas sponsorship for a `transaction` on Solana. Returns the `serializedTransaction`, including the `feePayer` signature. |
+| [alchemy_requestPaymasterTokenQuote](/wallets/api-reference/gas-manager-admin-api/gas-abstraction-api-endpoints/alchemy-request-paymaster-token-quote) | Requests ERC20 fee quote for a `UserOperation`. Optionally accepts fee parameter overrides to be used in the `UserOperation` |
diff --git a/fern/wallets/shared/infra/client.mdx b/fern/wallets/shared/infra/client.mdx
new file mode 100644
index 000000000..94159625a
--- /dev/null
+++ b/fern/wallets/shared/infra/client.mdx
@@ -0,0 +1,27 @@
+```ts client.ts
+import {
+ alchemy,
+ createAlchemySmartAccountClient,
+ sepolia,
+} from "@account-kit/infra";
+import { createLightAccount } from "@account-kit/smart-contracts";
+// You can replace this with any signer you'd like
+// We're using a LocalAccountSigner to generate a local key to sign with
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+
+const alchemyTransport = alchemy({
+ apiKey: "YOUR_API_KEY",
+});
+
+export const client = createAlchemySmartAccountClient({
+ transport: alchemyTransport,
+ policyId: "YOUR_POLICY_ID",
+ chain: sepolia,
+ account: await createLightAccount({
+ chain: sepolia,
+ transport: alchemyTransport,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ }),
+});
+```
diff --git a/fern/wallets/shared/infra/drop-and-replace-description.mdx b/fern/wallets/shared/infra/drop-and-replace-description.mdx
new file mode 100644
index 000000000..c8cac9c84
--- /dev/null
+++ b/fern/wallets/shared/infra/drop-and-replace-description.mdx
@@ -0,0 +1,28 @@
+Network fees can change quickly. When fees rise, pending transactions may become underpriced and stop progressing. Without retries, these transactions may never confirm. Use retries to keep your app reliable.
+
+**Why retries matter**
+
+* **Production reliability**: Proper wait and retry mechanisms ensure transactions don’t get stuck and fail silently.
+* **Fee volatility**: Gas markets fluctuate constantly, especially on testnets, so transactions can become underpriced and take much longer to mine.
+* **Automatic recovery**: Retries automatically replace stuck transactions with higher fees so they continue to progress.
+
+## How it works
+
+Drop and replace resubmits a transaction with whichever is greater:
+
+* 10% higher fees
+* The current minimum fees
+
+The SDK exports a `dropAndReplace` function from `@aa-sdk/core`. It is available on the `Smart Account Client`, so use it directly.
+
+Here’s a common production flow:
+
+1. Send a transaction with `sendUserOperation`
+2. Wait for confirmation using `waitForUserOperationTransaction`
+3. If waiting times out, call `dropAndReplace` to resend the transaction with higher fees
+
+
+ If you sponsor gas, each retry creates a pending gas sponsorship in your
+ dashboard. Pending sponsorships expire after 10 minutes of inactivity.
+ Excessive retries can temporarily exhaust your sponsorship limit.
+
diff --git a/fern/wallets/shared/infra/mav2-client.mdx b/fern/wallets/shared/infra/mav2-client.mdx
new file mode 100644
index 000000000..348b7a0ac
--- /dev/null
+++ b/fern/wallets/shared/infra/mav2-client.mdx
@@ -0,0 +1,32 @@
+
+```ts title="config.ts"
+import { alchemy, sepolia } from "@account-kit/infra";
+
+const YOUR_API_KEY = "";
+
+export const chain = sepolia;
+
+export const policyId = "";
+
+export const transport = alchemy({
+ apiKey: YOUR_API_KEY,
+});
+```
+
+```ts title="client.ts"
+import { createModularAccountV2Client } from "@account-kit/smart-contracts";
+import { chain, transport, policyId } from "./config";
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { generatePrivateKey } from "viem/accounts";
+
+export async function createClient() {
+ return createModularAccountV2Client({
+ chain,
+ transport,
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ policyId,
+ });
+}
+```
+
+
diff --git a/fern/wallets/shared/passkey-email-warning.mdx b/fern/wallets/shared/passkey-email-warning.mdx
new file mode 100644
index 000000000..1758ea477
--- /dev/null
+++ b/fern/wallets/shared/passkey-email-warning.mdx
@@ -0,0 +1,11 @@
+
+ If you create a passkey without an email associated with the user, you risk your users losing access to their wallets if they lose their device.
+
+ **Recommended security practice:** Proxy authentication requests to your backend server to enforce additional security measures:
+
+ * When a user attempts to sign up with both passkey and email, you can first require email verification before allowing the passkey to be created
+ * Alternatively, you can restrict initial signup to email-based methods only (which inherently verify email ownership), then allow users to add passkeys after their account is established
+ * This approach gives you greater control over the authentication flow and helps prevent account recovery issues
+
+ By implementing server-side verification, you ensure that passkeys are only created for verified identities, reducing the risk of permanent access loss.
+
diff --git a/fern/wallets/shared/react-native/account-provider-setup.mdx b/fern/wallets/shared/react-native/account-provider-setup.mdx
new file mode 100644
index 000000000..35958f667
--- /dev/null
+++ b/fern/wallets/shared/react-native/account-provider-setup.mdx
@@ -0,0 +1,67 @@
+```ts _layout.tsx (Expo)
+import "react-native-get-random-values"; // Shims for the crypto module
+import React from "react";
+import { alchemy, sepolia } from "@account-kit/infra";
+import {
+ AlchemyAccountProvider,
+ createConfig,
+} from "@account-kit/react-native";
+import { QueryClient } from "@tanstack/react-query";
+
+const queryClient = new QueryClient();
+
+const config = createConfig({
+ chain: sepolia,
+ transport: alchemy({
+ apiKey: "YOUR_ALCHEMY_API_KEY",
+ }),
+ signerConnection: {
+ apiKey: "YOUR_ALCHEMY_API_KEY",
+ },
+ sessionConfig: {
+ expirationTimeMs: 1000 * 60 * 60 * 24, // <-- Adjust the session expiration time as needed (currently 24 hours)
+ },
+});
+
+export default function App() {
+ return (
+
+ {/* The rest of your app here... */}
+
+ );
+}
+```
+
+```ts App.tsx (Bare React Native)
+import "react-native-get-random-values"; // Shims for the crypto module
+import React from "react";
+import { alchemy, sepolia } from "@account-kit/infra";
+import {
+ AlchemyAccountProvider,
+ createConfig,
+} from "@account-kit/react-native";
+import { QueryClient } from "@tanstack/react-query";
+
+const queryClient = new QueryClient();
+
+const config = createConfig({
+ chain: sepolia,
+ transport: alchemy({
+ apiKey: "YOUR_ALCHEMY_API_KEY",
+ }),
+ signerConnection: {
+ apiKey: "YOUR_ALCHEMY_API_KEY",
+ },
+ sessionConfig: {
+ expirationTimeMs: 1000 * 60 * 60 * 24, // <-- Adjust the session expiration time as needed (currently 24 hours)
+ },
+});
+
+export default function App() {
+ return (
+
+ {/* The rest of your app here... */}
+
+ );
+}
+```
diff --git a/fern/wallets/shared/react-native/prerequisites.mdx b/fern/wallets/shared/react-native/prerequisites.mdx
new file mode 100644
index 000000000..b977d97a8
--- /dev/null
+++ b/fern/wallets/shared/react-native/prerequisites.mdx
@@ -0,0 +1,9 @@
+## Pre-requisites
+
+* React Native version 0.76 or later
+* iOS Minumum Deployment Target: 17.0
+* [Hermes](https://reactnative.dev/docs/hermes) and [Fabric](https://reactnative.dev/architecture/fabric-renderer) must be enabled (if using expo these are on by default)
+* The Signer package requires you to be on React Native's [new architecture](https://reactnative.dev/architecture/landing-page#should-i-use-the-new-architecture-today). For information on how to enable it in your Expo project, check their [documentation](https://docs.expo.dev/guides/new-architecture/).
+* The Signer package is incompatible with Expo Go and as a result, you'd
+ need to use a Development Build. Check the [Expo Development Builds documentation](https://docs.expo.dev/guides/local-app-development/)
+ for more information.
diff --git a/fern/wallets/shared/react-native/signer-setup.mdx b/fern/wallets/shared/react-native/signer-setup.mdx
new file mode 100644
index 000000000..e66496068
--- /dev/null
+++ b/fern/wallets/shared/react-native/signer-setup.mdx
@@ -0,0 +1,99 @@
+## Dashboard Setup
+
+1. Get your **API key** by creating a new app in your [Alchemy Dashboard](https://dashboard.alchemy.com/apps)
+
+
+ Make sure **Ethereum** is enabled for your app under the Networks tab
+
+
+2. Activate **smart wallets** in your [Smart Wallets Dashboard](https://dashboard.alchemy.com/apps/latest/services/smart-wallets/overview)
+
+
+
+ a. Apply the config to your app from step 1
+
+
+
+
+
+ b. Enable authentication methods you want to support.
+
+
+
+ **Email auth (OTP)**
+
+
+
+ If you're using OTP Auth, toggle on email fill in the required fields. For **Redirect
+ URL**, you can just pass in the URL of your website (it doesn't get used during
+ OTP flows).
+
+
+
+
+ If you're only using OTP auth, you can skip to step 3!
+
+
+ **Email auth (Magic Link)**
+
+ * To use email auth in your React Native app, your app needs to be set
+ up to handle deep links that can be served via http or https.
+
+ * In production
+ scenarios, you would want to set up [Android App Links](https://developer.android.com/training/app-links)
+ on android or [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app)
+ on iOS to gracefully handle deep links.
+
+ * For testing, simply setting up a simple
+ server on [http://localhost:3000](http://localhost:3000/) or any other server
+ you control which redirects an incoming request to your app scheme is the easiest
+ way to get started. a sample implementation of this can be found the [`example`](https://github.com/alchemyplatform/aa-sdk/blob/main/examples/react-native-expo-example/redirect-server/index.ts)
+ folder of the `React Native Signer` repo.
+
+ * The http or https url you set up
+ in the previous step would need to be added as the **Redirect URL** in the Account
+ Kit Dashboard.
+
+ * This is the url that the user will be redirected to if you use
+ the magic link email flow to log in your users.
+
+ * Optionally stylize ✨ the email
+ with your brand color and logo!
+
+
+
+
+
+
+
+ **Social auth**
+
+ If you want to enable social login, toggle which auth providers
+ you want to support.
+
+ * Make sure to add your app's deep link as a **whitelisted origin**. This is necessary to handle redirects from the signer after a user has been authenticated.
+ * Add the link that your dapp will be running on to the **whitelisted origin** list
+ * Optionally enter your own OAuth credentials or use our defaults
+
+
+
+
+
+3. Create the config and copy the **API Key**
+
+
+## Ensure Deep Linking is properly configured
+
+
+ If you're using OTP Auth, skip this step! We recommend starting with OTP flows
+ as they are easier to set up.
+
+
+If you plan on using email magic link auth or social auth, you'll need to ensure deep linking is properly configured in your app.
+
+Depending on if your app uses `expo-router`, `react-navigation`, or some other navigation library, you'll need to configure deep linking accordingly.
+
+Here are some useful links for some common navigation libraries:
+
+* [Expo Router](https://docs.expo.dev/linking/into-your-app/)
+* [React Navigation](https://reactnavigation.org/docs/deep-linking/)
diff --git a/fern/wallets/shared/setup-smart-account-client/how-to-setup-smart-account-client.mdx b/fern/wallets/shared/setup-smart-account-client/how-to-setup-smart-account-client.mdx
new file mode 100644
index 000000000..94839a4bf
--- /dev/null
+++ b/fern/wallets/shared/setup-smart-account-client/how-to-setup-smart-account-client.mdx
@@ -0,0 +1,10 @@
+# How to set up a smart account client
+
+Once users are [authenticated](/wallets/react/getting-started), you can start landing transactions onchain using a Smart Account Client. Setting up a new account client is easy using the `useSmartAccountClient` hook!
+
+This extends `viem`'s [Client](https://viem.sh/docs/clients/custom#build-your-own-client), enabling account creation, sending transactions, signing messages, sponsoring gas, and more. It's also EIP-1193 compliant, so you can swap it in for other web3 providers like `window.ethereum`.
+
+
+ Building on Solana? Check out our Solana
+ [quickstart](/wallets/react/solana-wallets/get-started).
+
diff --git a/fern/wallets/shared/setup-smart-account-client/smart-account-client-details.mdx b/fern/wallets/shared/setup-smart-account-client/smart-account-client-details.mdx
new file mode 100644
index 000000000..28eb4e8c7
--- /dev/null
+++ b/fern/wallets/shared/setup-smart-account-client/smart-account-client-details.mdx
@@ -0,0 +1,49 @@
+The default smart account type is Modular Account V2 — the most cost-effective and advanced smart account option. You can specify other [account types](#account-type) with additional configuration.
+
+Note: React hooks only support Alchemy Signer. If you want to use a 3rd party signer, see this [guide](/wallets/third-party/signers/privy).
+
+## Configuration Options
+
+You can customize your client by specifying additional parameters to change account type, add gas sponsorship, etc. These parameters are optional.
+
+### Account Type
+
+We recommend using [ModularAccountV2](/wallets/smart-contracts/modular-account-v2/overview) as it is the cheapest and most advanced Smart Account, but you can specify other smart contract account types as needed. Learn more about the different smart accounts [here](/wallets/smart-contracts/choosing-a-smart-account).
+
+* **`type`** (string) - Defines the smart account type. Options:
+ * `"ModularAccountV2"` (recommended and default)
+ * `"LightAccount"`
+ * `"MultiOwnerLightAccount"`
+ * `"MultiOwnerModularAccount"`
+
+Looking for 7702 support? Learn how to use advanced `ModularAccountV2` functionality [here](#TODO/702-guide).
+
+
+ Note: Changing the account type will deploy a different account. If you've
+ already deployed an account for a user and want to change the underlying
+ account type, you'll need to upgrade it. Learn how to upgrade
+ [here](/wallets/smart-contracts/other-accounts/modular-account/upgrading-to-modular-account).
+
+
+### Gas Sponsorship
+
+To improve transaction UX, we recommend sponsoring gas for users.
+
+* **`policyId`** (string) - [Gas Manager Policy ID](https://dashboard.alchemy.com/gas-manager/policy/create/?a=ak-docs) that enables gas sponsorship within policy limits
+
+See the [full guide](/wallets/transactions/sponsor-gas) for gas sponsorship.
+
+### Advanced options
+
+
+ | Usage | Parameter | Type |
+ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+ | Smart contract account implementation | `type` | One of `"ModularAccountV2"`, `"LightAccount"`, `"MultiOwnerLightAccount"`, `"MultiOwnerModularAccount"` |
+ | Sponsor gas for users. Get your policy ID [here](https://dashboard.alchemy.com/gas-manager) | `policyId` | `string` |
+ | Simulate user operations before sending (i.e. use [`alchemyUserOperationSimulator`](/wallets/reference/account-kit/infra/functions/alchemyUserOperationSimulator)) | `useSimulation` | `boolean` |
+ | Custom middleware to run before requesting sponsorship | `customMiddleware` | `ClientMiddlewareFn` |
+ | Override fee estimation middleware (if you are using our RPCs, it's important to use the default [`alchemyFeeEstimator`](/wallets/reference/account-kit/infra/variables/alchemyFeeEstimator)) | `feeEstimator` | `ClientMiddlewareFn` |
+ | Override gas estimation middleware (the default middleware calls the underlying bundler RPC to `eth_estimateUserOperationGas`) | `gasEstimator` | `ClientMiddlewareFn` |
+ | Override middleware that signs user operation(s) | `signUserOperation` | `ClientMiddlewareFn` |
+ | Advanced account params | `accountParams` | `AccountConfig` with the following fields:
`mode`: `"default"` | `"7702"`
`entryPoint`: `EntryPointDef`
`signerEntity`: `SignerEntity`
`salt`: `bigint`
`factoryAddress`: `Address`
`initCode`: `Hex`
`accountAddress`: `Address`
|
+
diff --git a/fern/wallets/shared/signer/signer.mdx b/fern/wallets/shared/signer/signer.mdx
new file mode 100644
index 000000000..5799cb72d
--- /dev/null
+++ b/fern/wallets/shared/signer/signer.mdx
@@ -0,0 +1,14 @@
+```ts signer.ts
+import { AlchemyWebSigner } from "@account-kit/signer";
+
+export const signer = new AlchemyWebSigner({
+ client: {
+ connection: {
+ apiKey: "API_KEY",
+ },
+ iframeConfig: {
+ iframeContainerId: "alchemy-signer-iframe-container",
+ },
+ },
+});
+```
diff --git a/fern/wallets/shared/signer/wallet-client-signer.mdx b/fern/wallets/shared/signer/wallet-client-signer.mdx
new file mode 100644
index 000000000..7c0b74f8f
--- /dev/null
+++ b/fern/wallets/shared/signer/wallet-client-signer.mdx
@@ -0,0 +1,17 @@
+```ts wallet-client-signer.ts
+import { WalletClientSigner, type SmartAccountSigner } from "@aa-sdk/core";
+import { createWalletClient, custom } from "viem";
+import { sepolia } from "viem/chains";
+
+const externalProvider = window.ethereum; // or any other EIP-1193 provider
+
+const walletClient = createWalletClient({
+ chain: sepolia, // can provide a different chain here
+ transport: custom(externalProvider),
+});
+
+export const signer: SmartAccountSigner = new WalletClientSigner(
+ walletClient,
+ "json-rpc", // signerType
+);
+```
diff --git a/fern/wallets/shared/simulate-sponsor-with-any-token.mdx b/fern/wallets/shared/simulate-sponsor-with-any-token.mdx
new file mode 100644
index 000000000..0faf82b86
--- /dev/null
+++ b/fern/wallets/shared/simulate-sponsor-with-any-token.mdx
@@ -0,0 +1,73 @@
+You can use the [alchemy_simulateUserOperationAssetChanges](https://www.alchemy.com/docs/wallets/api/bundler-api/useroperation-simulation-endpoints/alchemy-simulate-user-operation-asset-changes) endpoint to do off-chain simulation to verify both the approval and the underlying userOp will succeed.
+
+
+
+Note that changes in blockchain's state could lead to different outcomes.
+
+
+
+Example:
+
+```tsx
+...
+const erc20Abi = parseAbi([
+ "function approve(address spender, uint256 amount) public returns (bool)",]);
+const client = createAlchemyPublicRpcClient(...);
+const response = await simulateUserOperationChanges(client, {
+uo: [{
+ // approve
+ target: tokenAddress,
+ data: encodeFunctionData({abi: erc20Abi,
+ functionName: "approve",
+ args: [paymasterAddress, 1_000_000_000n]}
+ ) as `0x${string}`,
+ },
+ // underlying transaction.
+ {
+ target: tokenAddress,
+ data: transfer(paymasterAddress_07, 1_000n) as `0x${string}`,
+ }],
+});
+```
+
+Sample response:
+
+```tsx
+{
+ "jsonrpc": "2.0",
+ "id": 1,
+ "result": {
+ "changes": [
+ {
+ "assetType": "ERC20",
+ "changeType": "APPROVE",
+ "from": "0x717ec68b238dc95c7fe1a5c9bcfaa2998915edba",
+ "to": "0x4fd9098af9ddcb41da48a1d78f91f1398965addc",
+ "rawAmount": "1000000000",
+ "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ "tokenId": null,
+ "decimals": 6,
+ "symbol": "USDC",
+ "name": "USDC",
+ "logo": "https://alchemyapi-res.cloudinary.com/image/upload/v1764187402/docs/aa-sdk/shared/3408.png",
+ "amount": "1000"
+ },
+ {
+ "assetType": "ERC20",
+ "changeType": "TRANSFER",
+ "from": "0x717ec68b238dc95c7fe1a5c9bcfaa2998915edba",
+ "to": "0xffffffffffffffffffffffffffffffffffffffff",
+ "rawAmount": "4633",
+ "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ "tokenId": null,
+ "decimals": 6,
+ "symbol": "USDC",
+ "name": "USDC",
+ "logo": "https://alchemyapi-res.cloudinary.com/image/upload/v1764187402/docs/aa-sdk/shared/3408.png",
+ "amount": "0.004633"
+ }
+ ],
+ "error": null
+ }
+}
+```
diff --git a/fern/wallets/shared/smart-contracts/light-account-client.mdx b/fern/wallets/shared/smart-contracts/light-account-client.mdx
new file mode 100644
index 000000000..b05e376ef
--- /dev/null
+++ b/fern/wallets/shared/smart-contracts/light-account-client.mdx
@@ -0,0 +1,12 @@
+```ts client.ts
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { generatePrivateKey } from "viem/accounts";
+
+export const lightAccountClient = await createLightAccountAlchemyClient({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ chain: sepolia,
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+});
+```
diff --git a/fern/wallets/shared/smart-contracts/modular-account-client.mdx b/fern/wallets/shared/smart-contracts/modular-account-client.mdx
new file mode 100644
index 000000000..53cba20e1
--- /dev/null
+++ b/fern/wallets/shared/smart-contracts/modular-account-client.mdx
@@ -0,0 +1,14 @@
+```ts client.ts
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { generatePrivateKey } from "viem/accounts";
+
+export const chain = sepolia;
+
+export const modularAccountClient = await createModularAccountAlchemyClient({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ chain,
+ transport: alchemy({ apiKey: "YOUR_API_KEY" }),
+});
+```
diff --git a/fern/wallets/shared/smart-contracts/multi-owner-light-account-client.mdx b/fern/wallets/shared/smart-contracts/multi-owner-light-account-client.mdx
new file mode 100644
index 000000000..3406c9966
--- /dev/null
+++ b/fern/wallets/shared/smart-contracts/multi-owner-light-account-client.mdx
@@ -0,0 +1,15 @@
+```ts client.ts
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createMultiOwnerLightAccountAlchemyClient } from "@account-kit/smart-contracts";
+import { generatePrivateKey } from "viem/accounts";
+
+export const multiOwnerLightAccountClient =
+ await createMultiOwnerLightAccountAlchemyClient({
+ signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
+ chain: sepolia,
+ transport: alchemy({
+ apiKey: "YOUR_API_KEY",
+ }),
+ });
+```
diff --git a/fern/wallets/shared/smart-contracts/multisig-client.mdx b/fern/wallets/shared/smart-contracts/multisig-client.mdx
new file mode 100644
index 000000000..48102e871
--- /dev/null
+++ b/fern/wallets/shared/smart-contracts/multisig-client.mdx
@@ -0,0 +1,37 @@
+```ts client.ts
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import { createMultisigAccountAlchemyClient } from "@account-kit/smart-contracts";
+
+const MODULAR_MULTISIG_ACCOUNT_OWNER_MNEMONIC = "YOUR MNEMONIC";
+
+// Creating a 3/3 multisig account
+export const signers = [
+ LocalAccountSigner.mnemonicToAccountSigner(
+ MODULAR_MULTISIG_ACCOUNT_OWNER_MNEMONIC,
+ { accountIndex: 0 },
+ ),
+ LocalAccountSigner.mnemonicToAccountSigner(
+ MODULAR_MULTISIG_ACCOUNT_OWNER_MNEMONIC,
+ { accountIndex: 1 },
+ ),
+ LocalAccountSigner.mnemonicToAccountSigner(
+ MODULAR_MULTISIG_ACCOUNT_OWNER_MNEMONIC,
+ { accountIndex: 2 },
+ ),
+];
+
+export const threshold = 3n;
+
+export const owners = await Promise.all(signers.map((s) => s.getAddress()));
+
+export const multisigAccountClient = await createMultisigAccountAlchemyClient({
+ chain: sepolia,
+ signer: signers[0],
+ owners,
+ threshold,
+ transport: alchemy({
+ apiKey: "YOUR_API_KEY",
+ }),
+});
+```
diff --git a/fern/wallets/shared/smart-contracts/session-keys/base-client.mdx b/fern/wallets/shared/smart-contracts/session-keys/base-client.mdx
new file mode 100644
index 000000000..907b70331
--- /dev/null
+++ b/fern/wallets/shared/smart-contracts/session-keys/base-client.mdx
@@ -0,0 +1,16 @@
+```ts base-client.ts
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import {
+ createModularAccountAlchemyClient,
+ sessionKeyPluginActions,
+} from "@account-kit/smart-contracts";
+
+export const client = (
+ await createModularAccountAlchemyClient({
+ chain: sepolia,
+ signer: LocalAccountSigner.mnemonicToAccountSigner("MNEMONIC"),
+ transport: alchemy({ apiKey: "ALCHEMY_API_KEY" }),
+ })
+).extend(sessionKeyPluginActions);
+```
diff --git a/fern/wallets/shared/smart-contracts/session-keys/full-example.mdx b/fern/wallets/shared/smart-contracts/session-keys/full-example.mdx
new file mode 100644
index 000000000..552b0eeb7
--- /dev/null
+++ b/fern/wallets/shared/smart-contracts/session-keys/full-example.mdx
@@ -0,0 +1,88 @@
+```ts
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { LocalAccountSigner } from "@aa-sdk/core";
+import { alchemy, sepolia } from "@account-kit/infra";
+import {
+ SessionKeyAccessListType,
+ SessionKeyPermissionsBuilder,
+ SessionKeyPlugin,
+ SessionKeySigner,
+ createModularAccountAlchemyClient,
+ sessionKeyPluginActions,
+} from "@account-kit/smart-contracts";
+import { zeroHash } from "viem";
+
+const chain = sepolia;
+const transport = alchemy({ apiKey: "YOUR_API_KEY" });
+
+// this is the signer to connect with the account, later we will create a new client using a session key signe
+const signer = LocalAccountSigner.mnemonicToAccountSigner("MNEMONIC");
+const sessionKeySigner = new SessionKeySigner();
+const client = (
+ await createModularAccountAlchemyClient({
+ chain,
+ transport,
+ signer,
+ })
+).extend(sessionKeyPluginActions);
+
+// 1. check if the plugin is installed
+const isPluginInstalled = await client
+ .getInstalledPlugins({})
+ // This checks using the default address for the chain, but you can always pass in your own plugin address here as an override
+ .then((x) => x.includes(SessionKeyPlugin.meta.addresses[chain.id]));
+
+// 2. if the plugin is not installed, then install it and set up the session key
+if (!isPluginInstalled) {
+ // lets create an initial permission set for the session key giving it an eth spend limit
+ const initialPermissions = new SessionKeyPermissionsBuilder()
+ .setNativeTokenSpendLimit({
+ spendLimit: 1000000n,
+ })
+ // this will allow the session key plugin to interact with all addresses
+ .setContractAccessControlType(SessionKeyAccessListType.ALLOW_ALL_ACCESS)
+ .setTimeRange({
+ validFrom: Math.round(Date.now() / 1000),
+ // valid for 1 hour
+ validUntil: Math.round(Date.now() / 1000 + 60 * 60),
+ });
+
+ const { hash } = await client.installSessionKeyPlugin({
+ // 1st arg is the initial set of session keys
+ // 2nd arg is the tags for the session keys
+ // 3rd arg is the initial set of permissions
+ args: [
+ [await sessionKeySigner.getAddress()],
+ [zeroHash],
+ [initialPermissions.encode()],
+ ],
+ });
+
+ await client.waitForUserOperationTransaction({ hash });
+}
+
+// 3. set up a client that's using our session key
+const sessionKeyClient = (
+ await createModularAccountAlchemyClient({
+ chain,
+ signer: sessionKeySigner,
+ transport,
+ // this is important because it tells the client to use our previously deployed account
+ accountAddress: client.getAddress({ account: client.account }),
+ })
+).extend(sessionKeyPluginActions);
+
+// 4. send a user operation using the session key
+const result = await sessionKeyClient.executeWithSessionKey({
+ args: [
+ [
+ {
+ target: "0x1234123412341234123412341234123412341234",
+ value: 1n,
+ data: "0x",
+ },
+ ],
+ await sessionKeySigner.getAddress(),
+ ],
+});
+```
diff --git a/fern/wallets/shared/sponsor-gas-erc20-create-policy.mdx b/fern/wallets/shared/sponsor-gas-erc20-create-policy.mdx
new file mode 100644
index 000000000..35eba8c68
--- /dev/null
+++ b/fern/wallets/shared/sponsor-gas-erc20-create-policy.mdx
@@ -0,0 +1,14 @@
+To enable your users to pay gas using an ERC-20 token, create a "Pay gas with any token" Policy via the [Gas Manager dashboard](https://dashboard.alchemy.com/gas-manager/policy/create). You can customize the policy with the following:
+
+* Receiving address: **you must specify** an address you own where the users' ERC20 tokens will be sent to as they pay for gas. The token transfer to this address is orchestrated by the gas sponsorship contract and happens automatically at transaction time.
+* Tokens: **you must select** which tokens users should be able to pay gas with. Learn more [here](https://www.alchemy.com/docs/reference/gas-manager-faqs).
+* ERC-20 transfer mode: choose when the user's token payment occurs.
+ * \[Recommended] After: No upfront allowance is required. The user signs an approval inside the same transactions batch, and the gas sponsorship pulls the token after the operation has executed. If that post-execution transfer fails, the entire transactions is reverted and you still pay the gas fee.
+ * Before: You (the developer) must ensure the gas sponsorship already has sufficient allowance—either through a prior `approve()` transaction or a permit signature—*before* the UserOperation is submitted. If the required allowance isn't in place when the transactions is submitted, it will be rejected upfront.
+* Sponsorship expiry period: this is the period for which the Gas Manager signature and ERC-20 exchange rate will remain valid once generated.
+
+
+
+Now you should have a Gas policy created with a policy id you can use to enable gas payments with ERC-20 tokens.
+
+
diff --git a/fern/wallets/shared/sponsor-gas-erc20-how-it-works.mdx b/fern/wallets/shared/sponsor-gas-erc20-how-it-works.mdx
new file mode 100644
index 000000000..749afa3dd
--- /dev/null
+++ b/fern/wallets/shared/sponsor-gas-erc20-how-it-works.mdx
@@ -0,0 +1,7 @@
+
+ *How it works:* To enable this capability you will need to create a Gas
+ Manager policy. When a user pays for gas with an ERC-20 token, we front the
+ gas using the network's native gas token and transfer the ERC-20 tokens from
+ the user's wallet to a wallet you control. The equivalent USD amount and the
+ admin fee is then added to your monthly invoice.
+
diff --git a/fern/wallets/shared/sponsor-gas-erc20-prerequisites.mdx b/fern/wallets/shared/sponsor-gas-erc20-prerequisites.mdx
new file mode 100644
index 000000000..abce496c3
--- /dev/null
+++ b/fern/wallets/shared/sponsor-gas-erc20-prerequisites.mdx
@@ -0,0 +1,2 @@
+* Smart Wallets already installed and configured in your project.
+* An Alchemy API key.
diff --git a/src/openapi/accounts/accounts.yaml b/src/openapi/accounts/accounts.yaml
new file mode 100644
index 000000000..0d120e187
--- /dev/null
+++ b/src/openapi/accounts/accounts.yaml
@@ -0,0 +1,573 @@
+# yaml-language-server: $schema=https://spec.openapis.org/oas/3.1/schema/2022-10-07
+
+openapi: 3.1.0
+info:
+ title: "👤 Smart Wallets"
+ version: "1.0"
+servers:
+ - url: https://api.g.alchemy.com/signer/v1
+security:
+ - apiKey: []
+paths:
+ /signup:
+ post:
+ summary: Create Wallet
+ description: >
+ Enables the creation of a Smart Wallet using Alchemy Signer. It allows applications to authenticate users and facilitate signature operations on their behalf. A Smart Wallet is either an on-chain Modular Account with Alchemy Signer as the owner, a standalone EOA signer managed through Alchemy Signer, or a standalone Solana Wallet managed through the Alchemy Signer.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ type: string
+ description: Used for user authentication when passkey is not provided. Specifies the user's email address.
+ passkey:
+ type: object
+ description: Used for authentication when email is not provided. Enables passkey-based authentication.
+ properties:
+ challenge:
+ type: string
+ description: A challenge string for passkey authentication.
+ attestation:
+ type: object
+ description: Provides attestation details required for passkey authentication.
+ properties:
+ credentialId:
+ type: string
+ description: The credential ID used for attestation.
+ clientDataJson:
+ type: string
+ description: JSON data from the client used for attestation.
+ attestationObject:
+ type: string
+ description: The attestation object received during the authentication process.
+ transports:
+ type: array
+ description: Transport protocols used during attestation.
+ items:
+ type: string
+ required:
+ - credentialId
+ - clientDataJson
+ - attestationObject
+ - transports
+ required:
+ - challenge
+ - attestation
+ targetPublicKey:
+ $ref: "#/components/schemas/targetPublicKey"
+ expirationSeconds:
+ $ref: "#/components/schemas/expirationSeconds"
+ redirectParams:
+ $ref: "#/components/schemas/redirectParams"
+ responses:
+ "200":
+ description: Wallet creation response.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orgId:
+ $ref: "#/components/schemas/orgId"
+ userId:
+ type: string
+ description: A unique identifier for the user.
+ address:
+ type: string
+ description: The EVM address of the signer.
+ solanaAddress:
+ type: string
+ description: The Solana address of the signer.
+ otpId:
+ $ref: "#/components/schemas/otpId"
+ required:
+ - orgId
+ operationId: createAccount
+ /auth:
+ post:
+ summary: Send Auth Email
+ description: >
+ Sends a user an email containing a magic link for authentication, allowing them to complete the login process and access their Smart Wallet through a secure, simple email verification method. Developers can customize the appearance and content of the authentication email through Alchemy dashboard to align with their application's branding and user experience requirements.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ $ref: "#/components/schemas/email"
+ description: The email address of the user requesting access. This is where the authentication email, containing the magic link, will be sent.
+ targetPublicKey:
+ $ref: "#/components/schemas/targetPublicKey"
+ expirationSeconds:
+ $ref: "#/components/schemas/expirationSeconds"
+ redirectParams:
+ $ref: "#/components/schemas/redirectParams"
+ required:
+ - email
+ - targetPublicKey
+ responses:
+ "200":
+ description: Authentication email sent successfully.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orgId:
+ $ref: "#/components/schemas/orgId"
+ description: The organization ID associated with the user and the application, facilitating the authentication process within the context of the user's application.
+ otpId:
+ $ref: "#/components/schemas/otpId"
+ required:
+ - orgId
+ operationId: sendEmailAuth
+ /whoami:
+ post:
+ summary: Authenticate User
+ description: >
+ Authenticates a user and returns their identity and wallet information, enabling the application to grant the user access to their Smart Wallet functionalities. This endpoint is used to confirm user authentication via email or passkey, thereby authorizing them to perform actions as the owner of a smart account or to conduct transactions as an EOA signer. It ensures that only authenticated users can access and manage their wallets.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ stampedRequest:
+ $ref: "#/components/schemas/SignedTurnkeyRequest"
+ description: >
+ A stamped [Turnkey Whoami Request](https://docs.turnkey.com/api#tag/Sessions/operation/GetWhoami).
+ required:
+ - stampedRequest
+ responses:
+ "200":
+ description: User authentication successful.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ $ref: "#/components/schemas/email"
+ description: The authenticated user's email address.
+ userId:
+ type: string
+ description: A unique identifier for the authenticated user.
+ orgId:
+ $ref: "#/components/schemas/orgId"
+ description: The organization ID associated with the authenticated user's account.
+ address:
+ type: string
+ description: The Ethereum address of the user's signer. Essential for executing transactions and managing the wallet.
+ solanaAddress:
+ type: string
+ description: The Solana address of the user's signer. Required for Solana transactions and wallet management.
+ required:
+ - userId
+ - orgId
+ - address
+ operationId: authUser
+ /lookup:
+ post:
+ summary: Get User
+ description: >
+ Enables applications to query the existence of a user wallet based on an email address.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ email:
+ $ref: "#/components/schemas/email"
+ description: The email address of the user to look up. This is used to verify if an associated account exists within the system.
+ required:
+ - email
+ responses:
+ "200":
+ description: Query successful. Returns organization ID if an account exists for the provided email address.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ orgId:
+ $ref: "#/components/schemas/orgId"
+ description: >
+ The organization ID associated with the user. This ID is returned if an account exists for the given email address. If no account is found, this value will be null. The response is intentionally limited to this information to protect user privacy and security.
+ operationId: getUser
+ /otp:
+ post:
+ summary: Verify OTP Code
+ description: >
+ Verifies a one-time password (OTP) code sent to the user's email for authentication. This endpoint allows users to complete the authentication process by providing the OTP code they received, enabling secure access to their Smart Wallet functionalities.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ otpCode:
+ type: string
+ description: The one-time password code sent to the user's email or phone number. This code is typically 6-8 digits long and expires after a short period.
+ otpId:
+ $ref: "#/components/schemas/otpId"
+ description: The OTP request identifier returned from the initial authentication request. This links the verification attempt to the original OTP generation.
+ orgId:
+ $ref: "#/components/schemas/orgId"
+ targetPublicKey:
+ $ref: "#/components/schemas/targetPublicKey"
+ expirationSeconds:
+ $ref: "#/components/schemas/expirationSeconds"
+ required:
+ - orgId
+ - otpCode
+ - otpId
+ - targetPublicKey
+ responses:
+ "200":
+ description: OTP verification successful. User is now authenticated.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ credentialBundle:
+ type: string
+ description: An encrypted API key credential bundle that can be used for subsequent authenticated requests. This bundle contains the authentication credentials encrypted with the provided targetPublicKey and can be decrypted client-side for stamping requests.
+ required:
+ - credentialBundle
+ "400":
+ description: Invalid OTP code or expired OTP.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ description: Error message describing why the OTP verification failed.
+ code:
+ type: string
+ description: Error code for programmatic handling of the error.
+ "429":
+ description: Too many OTP verification attempts. Please wait before trying again.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ description: Error message indicating rate limiting is in effect.
+ retryAfter:
+ type: integer
+ description: Number of seconds to wait before making another verification attempt.
+ operationId: verifyOtp
+ /auth-jwt:
+ post:
+ summary: Authenticate with JWT
+ description: >
+ Authenticates a user using a JSON Web Token (JWT) for secure access to their Smart Wallet functionalities. This endpoint validates the provided JWT token and can be used for authenticating an existing user or to pregenerate a wallet.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ jwt:
+ type: string
+ description: |
+ The JSON Web Token (JWT) used for authentication. The JWT must be a valid OIDC ID Token containing the required claims for user identification and authentication.
+ **Required OIDC Claims:**
+ - `iss` (Issuer): The identity of the OIDC provider. Should be the same as issuer URL specified in your /.well-known/openid-configuration, for example see the [Google OpenID configuration](https://accounts.google.com/.well-known/openid-configuration)
+ - `sub` (Subject): A unique identifier that identifies the user with this auth provider.
+ - `aud` (Audience): A unique identifier for the project that can be found on the [Alchemy Dashboard](https://dashboard.alchemy.com/apps/latest/services/smart-wallets).
+ - `exp` (Expiration): Token expiration time as Unix timestamp
+ - `iat` (Issued At): Token issuance time as Unix timestamp
+ - `nonce` (Nonce): toHex(sha256(targetPublicKey)) without the leading 0x
+ **Example JWT Payload:**
+ ```json
+ {
+ "iss": "https://accounts.google.com",
+ "sub": "1234567890abcdef",
+ "aud": "project_id",
+ "exp": 1640995200,
+ "iat": 1640991600,
+ "nonce": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
+ }
+ ```
+ example: "eyJhbGciOiJSUzI1NiIsImtpZCI6Im15LWtl..."
+ targetPublicKey:
+ $ref: "#/components/schemas/targetPublicKey"
+ description: Required for authentication. Optional for pregeneration.
+ expirationSeconds:
+ $ref: "#/components/schemas/expirationSeconds"
+
+ required:
+ - jwt
+ responses:
+ "200":
+ description: JWT authentication successful. User is now authenticated or wallet has been pregenerated.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ isSignup:
+ type: boolean
+ description: If true, indicates a new wallet was created.
+ default: false
+ orgId:
+ $ref: "#/components/schemas/orgId"
+ description: The organization ID associated with the authenticated user and the application.
+ credentialBundle:
+ type: string
+ description: An encrypted API key credential bundle that shall be used for subsequent authenticated requests. This bundle contains the authentication credentials encrypted with the provided targetPublicKey and can be decrypted client-side for stamping requests. A credential bundle will be returned when this endpoint is being used for authentication and would require a targetPublicKey in the request params
+ userId:
+ type: string
+ description: A unique identifier for the authenticated user.
+ address:
+ type: string
+ description: The Ethereum address of the user's signer, available after successful authentication.
+ solanaAddress:
+ type: string
+ description: The Solana address of the user's signer, available after successful authentication.
+ required:
+ - orgId
+ - isSignUp
+ "400":
+ description: Invalid JWT token or authentication failed.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ description: Error message describing why the JWT authentication failed.
+ code:
+ type: string
+ description: Error code for programmatic handling of the error.
+ details:
+ type: object
+ description: Additional details about the authentication failure, such as expired token, invalid signature, or missing claims.
+ additionalProperties:
+ type: string
+ "401":
+ description: Unauthorized - JWT token is invalid, expired, or malformed.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ description: Error message indicating the JWT token is not valid for authentication.
+ tokenError:
+ type: string
+ description: Specific details about why the JWT token was rejected.
+ "429":
+ description: Too many authentication attempts. Please wait before trying again.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ description: Error message indicating rate limiting is in effect.
+ retryAfter:
+ type: integer
+ description: Number of seconds to wait before making another authentication attempt.
+ operationId: authJwt
+ /sign-payload:
+ post:
+ summary: Sign Message
+ description: >
+ Allows for the signing of arbitrary payloads using the authenticated user's signer. The payload to be signed should be included within the body of a stamped request, ensuring the integrity and authenticity of the message to be signed. This operation is important for executing transactions and interactions on the blockchain that require verified signatures from the user's wallet.
+ security:
+ - apiKey: []
+ x-readme:
+ samples-languages:
+ - javascript
+ - curl
+ - python
+ - go
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ stampedRequest:
+ $ref: "#/components/schemas/SignedTurnkeyRequest"
+ description: |
+ A stamped request that includes the payload to be signed. The payload is part of the stamped request's body, which should conform to the structure outlined in the Turnkey API documentation for signing raw payloads.
+
+ Because users are given wallets for both Ethereum and Solana, it's possible to request signatures for both chains. The following is an example of the payload structure for signing bytes on Ethereum and Solana:
+
+ Signing bytes for Ethereum:
+ ```
+ {
+ organizationId: this.user.orgId,
+ type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
+ timestampMs: Date.now().toString(),
+ parameters: {
+ encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
+ hashFunction: "HASH_FUNCTION_NO_OP",
+ payload: msg,
+ signWith: this.user.address,
+ },
+ }
+ ```
+
+ Signing bytes for Solana:
+ ```
+ {
+ organizationId: this.user.orgId,
+ type: "ACTIVITY_TYPE_SIGN_RAW_PAYLOAD_V2",
+ timestampMs: Date.now().toString(),
+ parameters: {
+ encoding: "PAYLOAD_ENCODING_HEXADECIMAL",
+ hashFunction: "HASH_FUNCTION_NOT_APPLICABLE",
+ payload: msg,
+ signWith: this.user.solanaAddress,
+ },
+ }
+ ```
+
+ For more details on the parameters of the request body, see [Turnkey's API Reference](https://docs.turnkey.com/api#tag/Signing/operation/SignRawPayload).
+ required:
+ - stampedRequest
+ responses:
+ "200":
+ description: Message signing successful.
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ signature:
+ type: string
+ description: The signature generated for the specified payload.
+ required:
+ - signature
+ operationId: signMessage
+
+components:
+ securitySchemes:
+ apiKey:
+ type: http
+ scheme: bearer
+ description: Bearer token authentication. Use 'Bearer ' as the value.
+ x-default: "Bearer API_KEY"
+ schemas:
+ email:
+ type: string
+ description: The email of the user that is signing in.
+ expirationSeconds:
+ type: string
+ description: >
+ Specifies the duration of the login session in seconds. After this period, the user has to re-login to refresh their session. The default value is 900 seconds (15 minutes).
+ targetPublicKey:
+ type: string
+ description: |
+ Authentication of a client is done via an HPKE flow that allows the client and TEE to exchange an encrypted bundle without revealing it to a middleman (you, us, or Turnkey). The targetPublicKey is the public key that the client uses to decrypt the shared secret.
+
+ See more in the [Turnkey Docs](https://docs.turnkey.com/embedded-wallets/sub-organization-auth).
+ orgId:
+ type: string
+ description: The organization ID associated with the user and application, enabling the management of Smart Wallets.
+ SignedTurnkeyRequestStamp:
+ type: object
+ description: >
+ A JSON-encoded object containing stamping information.
+ properties:
+ stampHeaderName:
+ type: string
+ description: >
+ For WebAuthN based stamps, this should be `X-Stamp-Webauthn`. For all other stamps, this should be `X-Stamp`.
+ stampHeaderValue:
+ type: string
+ description: >
+ The stamp over the request body. See the [Turnkey Stamps Documentation](https://docs.turnkey.com/api-design/stamps) for information on the format of this value.
+ required:
+ - stampHeaderName
+ - stampHeaderValue
+ SignedTurnkeyRequest:
+ type: object
+ description: >
+ The signed request object containing session information. It is used to ensure the integrity and authenticity of requests to our API. For detailed steps on how to create a stamped request refer to the [Turnkey Stamps Documentation](https://docs.turnkey.com/api-design/stamps).
+ properties:
+ url:
+ type: string
+ description: Generated by the turnkey client, but will be ignored on our end.
+ body:
+ type: string
+ description: JSON stringified request body.
+ stamp:
+ $ref: "#/components/schemas/SignedTurnkeyRequestStamp"
+ required:
+ - body
+ - stamp
+ otpId:
+ type: string
+ description: OTP request identifier
+ redirectParams:
+ type: string
+ description: Redirect parameters appended to the magic link.
+ credentialBundle:
+ type: string
+ description: An encrypted API key credential bundle that can be used for subsequent authenticated requests. This bundle contains the authentication credentials encrypted with the provided targetPublicKey and can be decrypted client-side for stamping requests.
diff --git a/src/openapi/admin/admin.yaml b/src/openapi/admin/admin.yaml
new file mode 100644
index 000000000..6aa3d71c4
--- /dev/null
+++ b/src/openapi/admin/admin.yaml
@@ -0,0 +1,722 @@
+openapi: 3.1.0
+info:
+ title: 🪄 Gas Manager Admin API
+ version: "1.0"
+servers:
+ - url: https://manage.g.alchemy.com
+paths:
+ "/api/gasManager/policy":
+ post:
+ summary: Create Policy
+ description: |
+ Creates a new gas manager policy with the specified rules. The newly created policy will be in the `inactive` status.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+
+ operationId: create-policy
+ security:
+ - BearerAuth: []
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - policyName
+ - policyType
+ - appId
+ - networks
+ properties:
+ policyName:
+ type: string
+ description: Name of the policy
+ default: "My Policy"
+ policyType:
+ $ref: "#/components/schemas/PolicyType"
+ description: Type of the policy. Currently we support `sponsorship` (for sponsoring gas on EVM networks), `erc20` (for enabling users to pay gas with any ERC-20 token on EVM networks), and `solana` (for sponsoring fees and rent on Solana).
+ appId:
+ type: string
+ description: |
+ ID for the app associated with the new policy.
+ default: "6d834x9k1yh4dx6z"
+ networks:
+ description: |
+ Networks to be enabled for the policy. Example: `ETH_MAINNET`. For Solana policies, valid values are `SOLANA_MAINNET` and `SOLANA_DEVNET`.
+ type: array
+ items:
+ type: string
+ rules:
+ description: Rules for `sponsorship` policy type. Empty if `policyType` is not `sponsorship`.
+ $ref: "#/components/schemas/Rules"
+ solana_rules:
+ $ref: "#/components/schemas/SolanaRules"
+ description: Rules for `solana` policy type. Empty if `policyType` is not `solana`.
+ erc20_rules:
+ $ref: "#/components/schemas/Erc20Rules"
+ description: Rules for `erc20` policy type. Empty if `policyType` is not `erc20`.
+
+ responses:
+ "200":
+ description: Policy created successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - data
+ - error
+ properties:
+ data:
+ type: object
+ properties:
+ policy:
+ $ref: "#/components/schemas/Policy"
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ "/api/gasManager/policy/{id}":
+ get:
+ summary: Get Policy
+ description: |
+ Returns a policy by id.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: get-policy
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: id
+ description: ID of the policy to be fetched
+ in: path
+ required: true
+ schema:
+ type: string
+ default: "a844e221-3c13-40c6-95db-d2db390e14b5"
+ responses:
+ "200":
+ description: Policy fetched successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ policy:
+ $ref: "#/components/schemas/Policy"
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ delete:
+ summary: Delete Policy
+ description: |
+ Deletes a policy by id.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: delete-policy
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: id
+ description: ID of the policy to be deleted
+ in: path
+ required: true
+ schema:
+ type: string
+ default: "6d834x9k1yh4dx6z"
+ responses:
+ "200":
+ description: "`200` - Policy deleted successfully"
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ success:
+ type: boolean
+ default: true
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ put:
+ summary: Replace Policy
+ description: |
+ Replaces all rules in an existing policy by id.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: replace-policy
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: id
+ description: ID of the policy to be replaced
+ in: path
+ required: true
+ schema:
+ type: string
+ default: "a844e221-3c13-40c6-95db-d2db390e14b5"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ policyName:
+ type: string
+ description: Name of the policy
+ default: "My Policy"
+ rules:
+ $ref: "#/components/schemas/Rules"
+ description: Rules for "sponsorship" policy type. Empty if `policyType` is not "sponsorship".
+ networks:
+ description: |
+ Networks for the policy. Example: `ETH_MAINNET`. For Solana policies, valid values are `SOLANA_MAINNET` and `SOLANA_DEVNET`.
+ type: array
+ items:
+ type: string
+ solana_rules:
+ $ref: "#/components/schemas/SolanaRules"
+ description: Rules for `solana` policy type. Empty if `policyType` is not `solana`.
+ erc20_rules:
+ $ref: "#/components/schemas/Erc20Rules"
+ description: Rules for `erc20` policy type. Empty if `policyType` is not `erc20`.
+ responses:
+ "200":
+ description: Policy rules replaced successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ policy:
+ $ref: "#/components/schemas/Policy"
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ "/api/gasManager/policies":
+ get:
+ summary: Get All Policies
+ description: |
+ Returns all policies. The results are paginated.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: get-all-policies
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: appId
+ in: query
+ description: |
+ An optional app id to filter results by policies associated with that app.
+ schema:
+ type: string
+ default: "6d834x9k1yh4dx6z"
+ - name: limit
+ in: query
+ description: An optional parameter to limit the number of results per page.
+ schema:
+ type: integer
+ default: 10
+ - name: before
+ in: query
+ description: String - used for pagination. If there are previous results, `before` field is returned in the response and can be passed in the request to fetch the previous page.
+ schema:
+ type: string
+ - name: after
+ in: query
+ description: String - used for pagination. If more results are available `after` field is returned in the response and can be passed in the request to fetch the next page.
+ schema:
+ type: string
+ responses:
+ "200":
+ description: Policies fetched successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ policies:
+ type: array
+ items:
+ $ref: "#/components/schemas/Policy"
+ before:
+ type: string
+ description: String - used for pagination. If there are previous results, `before` field is returned in the response and can be passed in the request to fetch the previous page. Can be null if there are no previous results. Can be null if there are no previous results.
+ after:
+ type: string
+ description: String - used for pagination. If more results are available `after` field is returned in the response and can be passed in the request to fetch the next page. Can be null if there are no more results.
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ "/api/gasManager/policy/{id}/status":
+ put:
+ summary: Update Policy Status
+ description: |
+ Modifies the status of a policy to either "active" or "inactive".
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: update-policy-status
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: id
+ description: ID of the policy to be updated
+ in: path
+ required: true
+ schema:
+ type: string
+ default: "a844e221-3c13-40c6-95db-d2db390e14b5"
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - status
+ properties:
+ status:
+ description: The new status of the policy
+ type: string
+ enum: [active, inactive]
+ default: active
+ responses:
+ "200":
+ description: Policy status updated successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ policy:
+ $ref: "#/components/schemas/Policy"
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ "/api/gasManager/policy/{id}/stats/details":
+ get:
+ summary: Get Policy Stats
+ description: |
+ Returns stats about a policy specified by ID.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: get-policy-stats
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: id
+ description: ID of the policy to be fetched
+ in: path
+ required: true
+ schema:
+ type: string
+ default: "a844e221-3c13-40c6-95db-d2db390e14b5"
+ responses:
+ "200":
+ description: Policy stats fetched successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ policyStats:
+ type: object
+ properties:
+ signaturesMined:
+ type: integer
+ default: 0
+ signaturesExpired:
+ type: integer
+ default: 0
+ signaturesPending:
+ type: integer
+ default: 0
+ usdPending:
+ type: string
+ default: "0.0"
+ usdMined:
+ type: string
+ default: "0.0"
+ policyNetworkStats:
+ type: array
+ items:
+ type: object
+ properties:
+ network:
+ type: string
+ default: "MATIC_MAINNET"
+ signaturesMined:
+ type: integer
+ default: 0
+ signaturesExpired:
+ type: integer
+ default: 0
+ signaturesPending:
+ type: integer
+ default: 0
+ usdPending:
+ type: string
+ default: "0.0"
+ usdMined:
+ type: string
+ default: "0.0"
+ pendingNativeToken:
+ type: number
+ default: 0
+ minedNativeToken:
+ type: number
+ default: 0
+ currency:
+ type: string
+ default: "ETH"
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+ "/api/gasManager/policy/{id}/sponsorships":
+ get:
+ summary: Get Sponsorships
+ description: |
+ Returns a list of sponsorships associated with the specified policy ID. The results are paginated.
+
+
+ To call this endpoint, you must use your [access token](/docs/how-to-create-access-keys) in the [authorization header](/docs/how-to-use-api-keys-in-http-headers) of the API request.
+
+ operationId: get-sponsorships
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: id
+ in: path
+ required: true
+ description: ID of the policy.
+ schema:
+ type: string
+ default: "a844e221-3c13-40c6-95db-d2db390e14b5"
+ - name: limit
+ in: query
+ description: Limits the number of sponsorships returned.
+ schema:
+ type: integer
+ default: 5
+ responses:
+ "200":
+ description: Sponsorships fetched successfully
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ data:
+ type: object
+ properties:
+ before:
+ type: string
+ description: String - used for pagination. If there are previous results, `before` field is returned in the response and can be passed in the request to fetch the previous page. Can be null if there are no previous results.
+ after:
+ type: string
+ description: String - used for pagination. If more results are available `after` field is returned in the response and can be passed in the request to fetch the next page. Can be null if there are no more results.
+ sponsorships:
+ type: array
+ items:
+ $ref: "#/components/schemas/Sponsorship"
+ error:
+ type: object
+ properties:
+ msg:
+ type: string
+
+components:
+ securitySchemes:
+ BearerAuth:
+ type: http
+ scheme: bearer
+
+ schemas:
+ Rules:
+ type: object
+ properties:
+ maxSpendUsd:
+ type: string
+ description: Maximum amount of USD that can be sponsored
+ default: "5000.00"
+ maxSpendPerSenderUsd:
+ type: string
+ description: Maximum amount of USD that can be sponsored for a single sender (not enforced on testnets)
+ default: "100.00"
+ maxSpendPerUoUsd:
+ type: string
+ description: Maximum amount of USD that can be sponsored for a single userOperation (not enforced on testnets)
+ default: "20.00"
+ maxCount:
+ type: string
+ description: Maximum number of userOperations that can be sponsored
+ default: "100"
+ maxCountPerSender:
+ type: string
+ description: Maximum number of userOperations that can be sponsored for a single sender
+ default: "2"
+ senderAllowlist:
+ type: array
+ items:
+ type: string
+ description: List of addresses that are eligible for sponsorship
+ default:
+ - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266"
+ - "0x70997970c51812dc3a010c7d01b50e0d17dc79c8"
+ senderBlocklist:
+ type: array
+ items:
+ type: string
+ description: List of addresses that are banned from receiving sponsorship
+ startTimeUnix:
+ type: string
+ description: Unix timestamp of when the policy starts
+ default: "1674228753"
+ endTimeUnix:
+ type: string
+ description: Unix timestamp of when the policy ends
+ default: "1679340742"
+ sponsorshipExpiryMs:
+ type: string
+ description: Milliseconds from signing that the sponsorship expires
+ default: "600000"
+ webhookRules:
+ type: object
+ description: Enable conditional gas sponsorship by making a request to your server to verify sponsorship eligibility
+ properties:
+ webhookUrl:
+ type: string
+ format: uri
+ description: The URL to call to verify sponsorship eligibility.
+ example: "https://dashboard.alchemy.com/gas-manager/policy/create"
+ approveOnFailure:
+ type: boolean
+ description: If true, the userOp will be sponsored in the event on an error or timeout.
+ default: false
+ required:
+ - webhookUrl
+ - approveOnFailure
+ required:
+ - startTimeUnix
+ - sponsorshipExpiryMs
+
+ SolanaRules:
+ type: object
+ properties:
+ maxSpendUsd:
+ type: string
+ description: Maximum amount of USD that can be sponsored
+ default: "6500.00"
+ maxSpendPerTxnUsd:
+ type: string
+ description: Maximum amount of USD that can be sponsored per transaction (not enforced on testnet)
+ default: "30.00"
+ maxCount:
+ type: string
+ description: Maximum number of transactions that can be sponsored
+ default: "200"
+ startTimeUnix:
+ type: string
+ description: Unix timestamp of when the policy starts
+ default: "1674228753"
+ endTimeUnix:
+ type: string
+ description: Unix timestamp of when the policy ends
+ default: "1679340742"
+
+ PolicyType:
+ type: string
+ enum: [sponsorship, erc20, solana]
+ description: Type of the policy. Currently we support `sponsorship` (for sponsoring gas on EVM networks), `erc20` (for enabling users to pay gas with any ERC-20 token on EVM networks), and `solana` (for sponsoring fees and rent on Solana).
+ default: "sponsorship"
+
+ Erc20Token:
+ type: object
+ properties:
+ network:
+ type: string
+ description: Network of the Erc20 token
+ token_address:
+ type: string
+ description: Erc20 token contract address
+ price_reference_network:
+ type: string
+ description: Network to be used to calculate conversion rate (required for testnet ERC-20 tokens)
+ price_reference_token_address:
+ type: string
+ description: Erc20 token contract address (on `price_reference_network`) to be used to calculate conversion rate (required for testnet ERC-20 tokens)
+ required:
+ - network
+ - token_address
+
+ Erc20Rules:
+ type: object
+ properties:
+ tokens:
+ type: array
+ items:
+ $ref: "#/components/schemas/Erc20Token"
+ description: Erc20 tokens allowed under this policy
+ recipient_address:
+ type: string
+ description: Wallet address that will receive the tokens paid by users. Please ensure that you own this address across all networks enabled under this policy
+ use_post_op:
+ type: boolean
+ description: |
+ Erc20 transfer mode
+ - [Recommended] True: The user pays for the gas after the userOp execution. Enables the user to allow the paymaster contract to spend the ERC-20 token on their behalf (`approve()`) without a separate tx, improving ux. If the transfer fails, the userOp will revert but you’ll remain liable for the gas costs.
+ - False: The user pays for the gas before the userOp execution. Requires the paymaster contract to have allowance onchain before the userOp gets submitted. If the allowance doesn’t exist, the userOp will fail immediately.
+ price_multiplier:
+ type: number
+ description: |
+ Adjust the Erc20 amount the user pays. For example:
+ - `1.1` means that they user pays 10% more (you monetize gas payments)
+ - `0.95` means that the user pays 5% less (you partially sponsor gas)
+ rules:
+ $ref: "#/components/schemas/Rules"
+ description: Rules for `erc20` policy type.
+
+ Policy:
+ type: object
+ properties:
+ policyId:
+ type: string
+ description: String ID of the policy
+ default: "0x1234567890abcdef"
+ appId:
+ type: string
+ description: String ID of the app
+ default: "0x1234567890abcdef"
+ status:
+ type: string
+ description: Status of the policy
+ default: active
+ rules:
+ $ref: "#/components/schemas/Rules"
+ description: Rules for `sponsorship` policy type. Empty if `policyType` is not `sponsorship`.
+ policyName:
+ type: string
+ description: Name of the policy
+ default: "Gas Manager Policy"
+ lastUpdatedUnix:
+ type: string
+ description: Unix timestamp of when the policy was last updated
+ default: "1674228753"
+ policyVersion:
+ type: number
+ description: Version of the policy
+ default: 0
+ policyType:
+ $ref: "#/components/schemas/PolicyType"
+ description: Type of the policy.
+ policyState:
+ type: string
+ description: State of the policy
+ default: "ongoing"
+ networks:
+ type: array
+ items:
+ type: string
+ description: List of networks the policy is active on
+ gas_pump_id:
+ type: string
+ description: Gas pump id
+ erc20_rules:
+ $ref: "#/components/schemas/Erc20Rules"
+ description: Erc20 rules for policy. Empty if `policyType` is not `erc20`.
+ solana_rules:
+ $ref: "#/components/schemas/SolanaRules"
+ description: Solana rules for policy. Empty if `policyType` is not `solana`.
+
+ required:
+ - policyId
+ - appId
+ - status
+
+ Sponsorship:
+ type: object
+ properties:
+ sender:
+ type: string
+ description: Address of the sender.
+ grantedAt:
+ type: string
+ description: Unix timestamp of when the sponsorship was granted.
+ confirmedTotalUsd:
+ type: string
+ description: Total amount of USD that was sponsored. Can be null if the userOp has not been mined.
+ status:
+ type: string
+ description: Status of the sponsorship. Can be PENDING, MINED or EXPIRED.
+ uoHash:
+ type: string
+ description: Hash of the userOperation.
+ uoExplorerUrl:
+ type: string
+ description: URL to view the userOperation in a block explorer.
+ txnHash:
+ type: string
+ description: Hash of the bundle transaction containing the userOperation. Can be null if not yet mined.
+ txnExplorerUrl:
+ type: string
+ description: URL to view the bundle transaction in an explorer. Can be null if not yet mined.
+ network:
+ type: string
+ description: Network where the userOperation was submitted.
+ required:
+ - sender
+ - grantedAt
+ - status
+ - uoHash
+ - uoExplorerUrl
+ - network
diff --git a/src/openrpc/alchemy/_shared_wallets/components.yaml b/src/openrpc/alchemy/_shared_wallets/components.yaml
new file mode 100644
index 000000000..424d60ae8
--- /dev/null
+++ b/src/openrpc/alchemy/_shared_wallets/components.yaml
@@ -0,0 +1,715 @@
+components:
+ schemas:
+ address:
+ title: hex encoded address
+ type: string
+ pattern: ^0x[0-9a-fA-F]{40}$
+
+ addresses:
+ title: hex encoded address
+ type: array
+ items:
+ $ref: "#/components/schemas/address"
+
+ byte:
+ title: hex encoded byte
+ type: string
+ pattern: ^0x([0-9a-fA-F]?){1,2}$
+
+ bytes:
+ title: hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]*$
+
+ bytesMax32:
+ title: 32 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{0,64}$
+
+ bytes8:
+ title: 8 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{16}$
+
+ bytes32:
+ title: 32 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{64}$
+
+ bytes48:
+ title: 48 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{96}$
+
+ bytes65:
+ title: 65 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{130}$
+
+ bytes96:
+ title: 96 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{192}$
+
+ bytes256:
+ title: 256 hex encoded bytes
+ type: string
+ pattern: ^0x[0-9a-f]{512}$
+
+ bytesAnyCase:
+ title: hex encoded bytes (any case)
+ type: string
+ pattern: ^0x[0-9a-fA-F]*$
+
+ blockTag:
+ title: block tag
+ type: string
+ enum: ["latest", "earliest", "pending"]
+
+ bytes256AnyCase:
+ title: 256 hex encoded bytes (any case)
+ type: string
+ pattern: ^0x[0-9a-fA-F]{512}$
+
+ hash32:
+ title: 32 byte hex value
+ type: string
+ pattern: ^0x[0-9a-f]{64}$
+
+ hash32AnyCase:
+ title: 32 byte hex value (any case)
+ type: string
+ pattern: ^0[xX][0-9A-Fa-f]{64}$
+
+ uint:
+ title: hex encoded unsigned integer
+ type: string
+ pattern: ^0x([1-9a-f]+[0-9a-f]*|0)$
+
+ uint64:
+ title: hex encoded 64 bit unsigned integer
+ type: string
+ pattern: ^0x([1-9a-f]+[0-9a-f]{0,15})|0$
+
+ uint256:
+ title: hex encoded 256 bit unsigned integer
+ type: string
+ pattern: ^0x([1-9a-f]+[0-9a-f]{0,31})|0$
+
+ uintHexAnyCase:
+ title: hex encoded unsigned integer (any case)
+ type: string
+ pattern: ^0[xX]([1-9A-Fa-f]+[0-9A-Fa-f]*|0)$
+
+ ratio:
+ title: normalized ratio
+ type: number
+ minimum: 0
+ maximum: 1
+
+ positiveDecimal:
+ title: positive decimal number
+ type: number
+ minimum: 0
+
+ BlockChange:
+ type: object
+ required:
+ - assetType
+ - changeType
+ properties:
+ assetType:
+ type: string
+ enum: [NATIVE, ERC20, ERC721, ERC1155, SPECIAL_NFT]
+ changeType:
+ type: string
+ enum: [APPROVE, TRANSFER]
+ from:
+ $ref: "#/components/schemas/address"
+ description: "address the transaction is sent from"
+ to:
+ $ref: "#/components/schemas/address"
+
+ notFound:
+ title: "Not Found (null)"
+ type: "string"
+
+ AssetChange:
+ type: object
+ required:
+ - assetType
+ - changeType
+ - from
+ - to
+ - rawAmount
+ - amount
+ - symbol
+ - decimals
+ - contractAddress
+ - name
+ - logo
+ - tokenId
+ properties:
+ assetType:
+ type: string
+ enum: [NATIVE, ERC20, ERC721, ERC1155, SPECIAL_NFT]
+ changeType:
+ type: string
+ enum: [APPROVE, TRANSFER]
+ from:
+ description: "address the transaction is sent from"
+ $ref: "#/components/schemas/address"
+ to:
+ description: "address the transaction is directed to"
+ $ref: "#/components/schemas/address"
+ rawAmount:
+ type: string
+ amount:
+ type: string
+ symbol:
+ type: string
+ decimals:
+ type: [number, "null"]
+ contractAddress:
+ type: [string, "null"]
+ name:
+ type: [string, "null"]
+ logo:
+ type: [string, "null"]
+ tokenId:
+ type: [number, "null"]
+
+ RevertMessage:
+ type: string
+ nullable: true
+ description: "The reason why a transaction would revert. Provides details about potential transaction failure before execution."
+
+ Eip7702Auth:
+ title: "Eip 7702 Auth"
+ type: object
+ properties:
+ chain_id:
+ $ref: "#/components/schemas/uint64"
+ description: "The chain Id of the authorization"
+ address:
+ $ref: "#/components/schemas/address"
+ description: "The address of the authorization"
+ nonce:
+ $ref: "#/components/schemas/uint64"
+ description: "The nonce for the authorization"
+ y_parity:
+ $ref: "#/components/schemas/uint"
+ description: "Y parity of signed authorizzation tuple"
+ r:
+ $ref: "#/components/schemas/uint256"
+ description: "R of signed authorizzation tuple"
+ s:
+ $ref: "#/components/schemas/uint256"
+ description: "S of signed authorizzation tuple"
+
+ UserOperationV06:
+ type: object
+ title: "User Operation v0.6"
+ properties:
+ sender:
+ $ref: "#/components/schemas/address"
+ description: "The account making the operation"
+ nonce:
+ $ref: "#/components/schemas/uint"
+ description: "Anti-replay parameter; used as salt for first-time account creation"
+ initCode:
+ $ref: "#/components/schemas/bytes"
+ description: "The initCode of the account (needed if the account is not yet on-chain and needs creation)"
+ callData:
+ $ref: "#/components/schemas/bytes"
+ description: "Encoded data for the primary function call or operation"
+ callGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for the main execution call"
+ verificationGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for verification"
+ preVerificationGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas for pre-verification execution and calldata"
+ maxFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Maximum fee per gas (EIP-1559)"
+ maxPriorityFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Max priority fee per gas (EIP-1559)"
+ signature:
+ $ref: "#/components/schemas/bytesAnyCase"
+ description: "Data passed during verification"
+ paymasterAndData:
+ $ref: "#/components/schemas/bytes"
+ description: "Paymaster address and extra data"
+ eip7702Auth:
+ $ref: "#/components/schemas/Eip7702Auth"
+ description: "The authorization tuple that an EOA account delegates to in EIP-7702"
+
+ UserOperationV06Partial:
+ type: object
+ properties:
+ sender:
+ $ref: "#/components/schemas/address"
+ description: "The account making the operation"
+ nonce:
+ $ref: "#/components/schemas/uint"
+ description: "Anti-replay parameter; used as salt for first-time account creation"
+ initCode:
+ $ref: "#/components/schemas/bytes"
+ description: "The initCode of the account (needed if the account is not yet on-chain and needs creation)"
+ callData:
+ $ref: "#/components/schemas/bytes"
+ description: "Encoded data for the primary function call or operation"
+ callGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for the main execution call"
+ verificationGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for verification"
+ preVerificationGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas for pre-verification execution and calldata"
+ maxFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Maximum fee per gas (EIP-1559)"
+ maxPriorityFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Max priority fee per gas (EIP-1559)"
+ eip7702Auth:
+ $ref: "#/components/schemas/Eip7702Auth"
+ description: "The authorization tuple that an EOA account delegates to in EIP-7702"
+
+ UserOperationV07:
+ type: object
+ title: "User Operation v0.7"
+ properties:
+ sender:
+ $ref: "#/components/schemas/address"
+ description: "Account initiating operation"
+ nonce:
+ $ref: "#/components/schemas/uint"
+ description: "Account nonce or creation salt"
+ callData:
+ $ref: "#/components/schemas/bytes"
+ description: "Data for operation call"
+ callGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for call"
+ verificationGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for verification"
+ maxFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Max fee per gas (EIP-1559)"
+ maxPriorityFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Priority fee per gas (EIP-1559)"
+ paymaster:
+ $ref: "#/components/schemas/address"
+ description: "Paymaster contract address"
+ paymasterData:
+ $ref: "#/components/schemas/bytes"
+ description: "Data for paymaster"
+ paymasterVerificationGasLimit:
+ type: string
+ description: "The gas limit for paymaster verification."
+ factory:
+ $ref: "#/components/schemas/address"
+ description: "The account factory address (needed if and only if the account is not yet on-chain and needs to be created)"
+ factoryData:
+ $ref: "#/components/schemas/bytes"
+ description: "Data for the account factory (only if the account factory exists)"
+ preVerificationGas:
+ $ref: "#/components/schemas/uint64"
+ description: "The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata"
+ paymasterPostOpGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "The amount of gas to allocate for the paymaster post-op code (only if a paymaster exists)"
+ signature:
+ $ref: "#/components/schemas/bytesAnyCase"
+ description: "Data passed into the account along with the nonce during the verification step"
+ eip7702Auth:
+ $ref: "#/components/schemas/Eip7702Auth"
+ description: "The authorization tuple that an EOA account delegates to in EIP-7702"
+
+ UserOperationV07Partial:
+ type: object
+ properties:
+ sender:
+ $ref: "#/components/schemas/address"
+ description: "Account initiating operation"
+ nonce:
+ $ref: "#/components/schemas/uint"
+ description: "Account nonce or creation salt"
+ callData:
+ $ref: "#/components/schemas/bytes"
+ description: "Data for operation call"
+ callGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for call"
+ verificationGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "Gas allocated for verification"
+ maxFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Max fee per gas (EIP-1559)"
+ maxPriorityFeePerGas:
+ $ref: "#/components/schemas/uint64"
+ description: "Priority fee per gas (EIP-1559)"
+ paymaster:
+ $ref: "#/components/schemas/address"
+ description: "Paymaster contract address"
+ paymasterVerificationGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "The gas limit for paymaster verification."
+ factory:
+ $ref: "#/components/schemas/address"
+ description: "The account factory address (needed if and only if the account is not yet on-chain and needs to be created)"
+ factoryData:
+ $ref: "#/components/schemas/bytes"
+ description: "Data for the account factory (only if the account factory exists)"
+ preVerificationGas:
+ $ref: "#/components/schemas/uint64"
+ description: "The amount of gas to pay for to compensate the bundler for pre-verification execution and calldata"
+ paymasterPostOpGasLimit:
+ $ref: "#/components/schemas/uint64"
+ description: "The amount of gas to allocate for the paymaster post-op code (only if a paymaster exists)"
+ eip7702Auth:
+ $ref: "#/components/schemas/Eip7702Auth"
+ description: "The authorization tuple that an EOA account delegates to in EIP-7702"
+
+ erc20Context:
+ title: Context for Erc20 paymaster
+ type: object
+ properties:
+ tokenAddress:
+ $ref: "#/components/schemas/address"
+ description: "Erc20 contract address"
+ maxTokenAmount:
+ $ref: "#/components/schemas/positiveDecimal"
+ description: "Maximum allowed amount of value in raw token amount"
+ permit:
+ $ref: "#/components/schemas/bytes"
+ description: "A 7597 typed permit"
+
+ transactionObject:
+ title: Transaction Object
+ type: object
+ properties:
+ from:
+ type: string
+ pattern: "^0[xX][0-9a-fA-F]{40}$"
+ to:
+ type: string
+ pattern: "^0[xX][0-9a-fA-F]{40}$"
+ gas:
+ type: string
+ pattern: "^0[xX]([1-9a-fA-F][0-9a-fA-F]*|0)$"
+ gasPrice:
+ type: string
+ pattern: "^0[xX]([1-9a-fA-F][0-9a-fA-F]*|0)$"
+ value:
+ type: string
+ pattern: "^0[xX]([1-9a-fA-F][0-9a-fA-F]*|0)$"
+ data:
+ type: string
+ pattern: "^0[xX][0-9a-fA-F]*$"
+
+ tracer:
+ title: Tracer
+ type: object
+ properties:
+ tracer:
+ type: string
+ enum: ["callTracer", "prestateTracer"]
+ tracerConfig:
+ type: object
+ properties:
+ onlyTopCall:
+ type: boolean
+
+ options:
+ title: Options
+ type: object
+ properties:
+ tracer:
+ type: string
+ enum: ["callTracer", "prestateTracer"]
+ tracerConfig:
+ type: object
+ properties:
+ onlyTopCall:
+ type: boolean
+ timeout:
+ type: string
+ description: A duration string that overrides the default timeout.
+
+ tracerResult:
+ type: array
+ description: Array of block traces.
+ items:
+ type: object
+ description: Array of block traces.
+ properties:
+ type:
+ type: string
+ description: CALL or CREATE
+ from:
+ type: string
+ description: 20-byte address of the caller
+ to:
+ type: string
+ description: 20-byte address of the recipient. Null when its a contract creation transaction.
+ value:
+ type: string
+ description: Amount of value included in the transfer (in hex)
+ gas:
+ type: string
+ description: Amount of gas provided for the call (in hex)
+ gasUsed:
+ type: string
+ description: Amount of gas used during the call (in hex)
+ input:
+ type: string
+ description: Call data
+ output:
+ type: string
+ description: Return data
+ error:
+ type: string
+ description: Error message, if any.
+ revertReason:
+ type: string
+ description: Solidity revert reason, if any.
+ calls:
+ type: array
+ description: Array of sub-calls made within the transaction.
+ items:
+ type: object
+ description: Array of block traces.
+ properties:
+ type:
+ type: string
+ description: CALL or CREATE
+ from:
+ type: string
+ description: 20-byte address of the caller
+ to:
+ type: string
+ description: 20-byte address of the recipient. Null when its a contract creation transaction.
+ value:
+ type: string
+ description: Amount of value included in the transfer (in hex)
+ gas:
+ type: string
+ description: Amount of gas provided for the call (in hex)
+ gasUsed:
+ type: string
+ description: Amount of gas used during the call (in hex)
+ input:
+ type: string
+ description: Call data
+ output:
+ type: string
+ description: Return data
+ error:
+ type: string
+ description: Error message, if any.
+ revertReason:
+ type: string
+ description: Solidity revert reason, if any.
+
+ SimulateAssetChangesResult:
+ title: SimulateAssetChangesResult
+ type: object
+ required:
+ - changes
+ - error
+ properties:
+ changes:
+ type: array
+ items:
+ $ref: "#/components/schemas/AssetChange"
+ gasUsed:
+ type: string
+ description: "The amount of gas used for the simulation"
+ error:
+ type: ["string", "null"]
+ description: "Error message if the transaction would revert, null otherwise"
+
+ SimulateExecutionResult:
+ title: SimulateExecutionResult
+ type: object
+ required:
+ - calls
+ - logs
+ - error
+ properties:
+ calls:
+ type: array
+ items:
+ $ref: "#/components/schemas/Call"
+ logs:
+ type: array
+ items:
+ $ref: "#/components/schemas/Log"
+ error:
+ type: ["string", "null"]
+ description: "If the transaction would revert, error explains the reason"
+ revertReason:
+ $ref: "#/components/schemas/RevertMessage"
+
+ Call:
+ title: Call
+ type: object
+ required:
+ - type
+ - from
+ - to
+ - gas
+ - gasUsed
+ - input
+ - output
+ - decoded
+ properties:
+ type:
+ $ref: "#/components/schemas/CallType"
+ from:
+ $ref: "#/components/schemas/address"
+ description: Address the transaction is sent from
+ to:
+ $ref: "#/components/schemas/address"
+ description: Address the transaction is directed to
+ value:
+ description: Amount in wei to transfer from sender to recipient
+ anyOf:
+ - $ref: "#/components/schemas/uint"
+ - type: "null"
+ gas:
+ $ref: "#/components/schemas/uint"
+ description: Hex-encoded gas limit
+ gasUsed:
+ $ref: "#/components/schemas/bytesAnyCase"
+ description: Hex-encoded gas actually used by this call
+ input:
+ $ref: "#/components/schemas/bytesAnyCase"
+ description: Hex-encoded call data
+ output:
+ type: string
+ description: Hex-encoded return data
+ decoded:
+ anyOf:
+ - $ref: "#/components/schemas/DecodedCall"
+ - type: "null"
+
+ CallType:
+ title: CallType
+ type: string
+ enum: [CALL, STATICCALL, DELEGATECALL]
+
+ DecodedCall:
+ title: DecodedCall
+ type: object
+ required:
+ - methodName
+ - inputs
+ - outputs
+ - authority
+ properties:
+ methodName:
+ type: string
+ inputs:
+ type: array
+ items:
+ $ref: "#/components/schemas/DecodedCallParam"
+ outputs:
+ type: array
+ items:
+ $ref: "#/components/schemas/DecodedCallParam"
+ authority:
+ $ref: "#/components/schemas/DecodingAuthority"
+
+ DecodedCallParam:
+ title: DecodedCallParam
+ type: object
+ required:
+ - name
+ - type
+ - value
+ properties:
+ name:
+ type: string
+ type:
+ type: string
+ value:
+ anyOf:
+ - $ref: "#/components/schemas/bytesAnyCase"
+ - type: string
+ description: The decoded field value
+
+ DecodingAuthority:
+ title: DecodingAuthority
+ type: string
+ enum: [ETHERSCAN]
+
+ Log:
+ title: Log
+ type: object
+ required:
+ - topics
+ - address
+ - data
+ - decoded
+ properties:
+ topics:
+ type: array
+ items:
+ $ref: "#/components/schemas/bytesAnyCase"
+ address:
+ $ref: "#/components/schemas/address"
+ data:
+ $ref: "#/components/schemas/bytesAnyCase"
+ decoded:
+ anyOf:
+ - $ref: "#/components/schemas/DecodedLog"
+ - type: "null"
+
+ DecodedLog:
+ title: DecodedLog
+ type: object
+ required:
+ - eventName
+ - inputs
+ - authority
+ properties:
+ eventName:
+ type: string
+ inputs:
+ type: array
+ items:
+ $ref: "#/components/schemas/DecodedLogInput"
+ authority:
+ $ref: "#/components/schemas/DecodingAuthority"
+
+ DecodedLogInput:
+ title: DecodedLogInput
+ type: object
+ required:
+ - name
+ - type
+ - value
+ - indexed
+ properties:
+ name:
+ type: string
+ type:
+ type: string
+ value:
+ anyOf:
+ - $ref: "#/components/schemas/bytesAnyCase"
+ - type: string
+ indexed:
+ type: boolean
diff --git a/src/openrpc/alchemy/bundler/bundler.yaml b/src/openrpc/alchemy/bundler/bundler.yaml
new file mode 100644
index 000000000..0d5c5b390
--- /dev/null
+++ b/src/openrpc/alchemy/bundler/bundler.yaml
@@ -0,0 +1,662 @@
+# yaml-language-server: $schema=https://meta.open-rpc.org/
+
+$schema: https://meta.open-rpc.org/
+openrpc: 1.2.4
+info:
+ title: Alchemy Bundler API JSON-RPC Specification
+ description: A specification of the standard JSON-RPC methods for the Alchemy Bundler API.
+ version: 0.0.0
+servers:
+ - url: https://eth-mainnet.g.alchemy.com/v2
+ name: Ethereum Mainnet
+ - url: https://eth-sepolia.g.alchemy.com/v2
+ name: Ethereum Sepolia
+ - url: https://anime-mainnet.g.alchemy.com/v2
+ name: Anime Mainnet
+ - url: https://anime-sepolia.g.alchemy.com/v2
+ name: Anime Sepolia
+ - url: https://apechain-mainnet.g.alchemy.com/v2
+ name: ApeChain Mainnet
+ - url: https://apechain-curtis.g.alchemy.com/v2
+ name: ApeChain Curtis
+ - url: https://arb-mainnet.g.alchemy.com/v2
+ name: Arbitrum Mainnet
+ - url: https://arb-sepolia.g.alchemy.com/v2
+ name: Arbitrum Sepolia
+ - url: https://arbnova-mainnet.g.alchemy.com/v2
+ name: Arbitrum Nova Mainnet
+ - url: https://base-mainnet.g.alchemy.com/v2
+ name: Base Mainnet
+ - url: https://base-sepolia.g.alchemy.com/v2
+ name: Base Sepolia
+ - url: https://berachain-mainnet.g.alchemy.com/v2
+ name: Berachain Mainnet
+ - url: https://bnb-mainnet.g.alchemy.com/v2
+ name: BNB Smart Chain Mainnet
+ - url: https://bnb-testnet.g.alchemy.com/v2
+ name: BNB Smart Chain Testnet
+ - url: https://celo-mainnet.g.alchemy.com/v2
+ name: Celo Mainnet
+ - url: https://celo-sepolia.g.alchemy.com/v2
+ name: Celo Sepolia
+ - url: https://frax-mainnet.g.alchemy.com/v2
+ name: Frax Mainnet
+ - url: https://gensyn-testnet.g.alchemy.com/v2
+ name: Gensyn Testnet
+ - url: https://hyperliquid-mainnet.g.alchemy.com/v2
+ name: Hyperliquid Mainnet
+ - url: https://hyperliquid-testnet.g.alchemy.com/v2
+ name: Hyperliquid Testnet
+ - url: https://ink-mainnet.g.alchemy.com/v2
+ name: Ink Mainnet
+ - url: https://ink-sepolia.g.alchemy.com/v2
+ name: Ink Sepolia
+ - url: https://polygon-mainnet.g.alchemy.com/v2
+ name: Polygon Mainnet
+ - url: https://polygon-amoy.g.alchemy.com/v2
+ name: Polygon Amoy
+ - url: https://monad-mainnet.g.alchemy.com/v2
+ name: Monad Mainnet
+ - url: https://monad-testnet.g.alchemy.com/v2
+ name: Monad Testnet
+ - url: https://mythos-mainnet.g.alchemy.com/v2
+ name: Mythos Mainnet
+ - url: https://opbnb-mainnet.g.alchemy.com/v2
+ name: opBNB Mainnet
+ - url: https://opbnb-testnet.g.alchemy.com/v2
+ name: opBNB Testnet
+ - url: https://opt-mainnet.g.alchemy.com/v2
+ name: OP Mainnet Mainnet
+ - url: https://opt-sepolia.g.alchemy.com/v2
+ name: OP Mainnet Sepolia
+ - url: https://plasma-mainnet.g.alchemy.com/v2
+ name: Plasma Mainnet
+ - url: https://plasma-testnet.g.alchemy.com/v2
+ name: Plasma Testnet
+ - url: https://polynomial-mainnet.g.alchemy.com/v2
+ name: Polynomial Mainnet
+ - url: https://polynomial-sepolia.g.alchemy.com/v2
+ name: Polynomial Sepolia
+ - url: https://rise-testnet.g.alchemy.com/v2
+ name: Rise Testnet
+ - url: https://shape-mainnet.g.alchemy.com/v2
+ name: Shape Mainnet
+ - url: https://shape-sepolia.g.alchemy.com/v2
+ name: Shape Sepolia
+ - url: https://solana-mainnet.g.alchemy.com/v2
+ name: Solana Mainnet
+ - url: https://solana-devnet.g.alchemy.com/v2
+ name: Solana Devnet
+ - url: https://soneium-mainnet.g.alchemy.com/v2
+ name: Soneium Mainnet
+ - url: https://soneium-minato.g.alchemy.com/v2
+ name: Soneium Minato
+ - url: https://stable-mainnet.g.alchemy.com/v2
+ name: Stable Mainnet
+ - url: https://stable-testnet.g.alchemy.com/v2
+ name: Stable Testnet
+ - url: https://story-mainnet.g.alchemy.com/v2
+ name: Story Mainnet
+ - url: https://story-aeneid.g.alchemy.com/v2
+ name: Story Aeneid
+ - url: https://unichain-mainnet.g.alchemy.com/v2
+ name: Unichain Mainnet
+ - url: https://unichain-sepolia.g.alchemy.com/v2
+ name: Unichain Sepolia
+ - url: https://worldchain-mainnet.g.alchemy.com/v2
+ name: World Chain Mainnet
+ - url: https://worldchain-sepolia.g.alchemy.com/v2
+ name: World Chain Sepolia
+ - url: https://zora-mainnet.g.alchemy.com/v2
+ name: Zora Mainnet
+ - url: https://zora-sepolia.g.alchemy.com/v2
+ name: Zora Sepolia
+methods:
+ - name: rundler_maxPriorityFeePerGas
+ description: Returns a fee per gas that is an estimate of how much users should set as a priority fee in userOperations for Rundler endpoints.
+ params: []
+ result:
+ name: Estimated priority fee
+ description: Returns the estimated priority fee per gas to be used in the userOperation for Rundler endpoints.
+ schema:
+ type: string
+ examples:
+ - name: rundler_maxPriorityFeePerGas example
+ params: []
+ result:
+ name: Estimated priority fee
+ value: "0xb1770efb14906e509893b6190359658208ae64d0c56e22f748a1b0869885559e"
+
+ - name: eth_getUserOperationReceipt
+ description: |
+ Get the `UserOperationReceipt` based on the `userOpHash`.
+
+
+ This method retrieves and decodes userOperations from the logs of mined transactions by querying the node. If you attempt to fetch a receipt for a transaction from a distant past block, the request may be rejected by the node due to limitations on the block range size it can process.
+
+ The default range we support is 150 blocks, however the following networks have an unlimited range.
+
+ - Ethereum
+ - Polygon
+ - Base
+ - Optimism
+ - Worldchain
+ - Arbitrum
+
+ params:
+ - name: userOpHash
+ required: true
+ description: The `userOpHash` of the `UserOperation` to get the receipt for (as returned by `eth_sendUserOperation`).
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/hash32"
+ - name: tag
+ required: false
+ description: The blocktag for checking the UO status. \"pending\" or \"latest\". Defaults to \"latest\".".
+ schema:
+ type: string
+ enum: ["pending", "latest"]
+ result:
+ name: UserOperationReceipt
+ description: "`UserOperationReceipt` object representing the outcome of a `UserOperation`."
+ schema:
+ type: object
+ required:
+ [
+ "userOpHash",
+ "entryPoint",
+ "sender",
+ "nonce",
+ "paymaster",
+ "actualGasCost",
+ "actualGasUsed",
+ "success",
+ "reason",
+ "logs",
+ "receipt",
+ "status",
+ ]
+ properties:
+ userOpHash:
+ type: string
+ description: The hash of the `UserOperation`.
+ entryPoint:
+ type: string
+ description: The EntryPoint address the request should be sent through. This MUST be one of the EntryPoints returned by the `supportedEntryPoints` RPC call.
+ sender:
+ type: string
+ description: The account initiating the `UserOperation`.
+ nonce:
+ type: string
+ description: The nonce used in the `UserOperation`.
+ paymaster:
+ type: string
+ description: The paymaster used for this `UserOperation` (or empty if self-sponsored).
+ actualGasCost:
+ type: string
+ description: The actual amount paid (by account or paymaster) for this `UserOperation`.
+ actualGasUsed:
+ type: string
+ description: The total gas used by this `UserOperation` (including `preVerification`, `creation`, `validation`, and `execution`).
+ success:
+ type: boolean
+ description: Indicates whether the execution completed without reverting.
+ reason:
+ type: string
+ description: In case of revert, this is the revert reason.
+ logs:
+ type: array
+ description: The logs generated by this `UserOperation` (not including logs of other `UserOperations` in the same bundle).
+ items:
+ type: string
+ receipt:
+ type: object
+ description: The `TransactionReceipt` object for the entire bundle, not only for this `UserOperation`.
+ required:
+ [
+ "blockHash",
+ "blockNumber",
+ "transactionIndex",
+ "transactionHash",
+ "from",
+ "to",
+ "cumulativeGasUsed",
+ "gasUsed",
+ "contractAddress",
+ "logs",
+ "logsBloom",
+ "root",
+ "status",
+ "effectiveGasPrice",
+ "type",
+ ]
+ properties:
+ blockHash:
+ type: string
+ description: 32 Bytes - The hash of the block where the given transaction was included.
+ blockNumber:
+ type: string
+ description: The number of the block where the given transaction was included.
+ transactionIndex:
+ type: string
+ description: The index of the transaction in the block.
+ transactionHash:
+ type: string
+ description: 32 Bytes - The hash of the transaction.
+ from:
+ type: string
+ description: 20 Bytes - address of the sender.
+ to:
+ type: string
+ description: 20 Bytes - address of the receiver. Null when its a contract creation transaction.
+ cumulativeGasUsed:
+ type: string
+ description: The total amount of gas used when this transaction was executed in the block.
+ gasUsed:
+ type: string
+ description: The amount of gas used by this specific transaction alone.
+ contractAddress:
+ type: string
+ description: 20 Bytes - The contract address created, if the transaction was a contract creation, otherwise null.
+ logs:
+ type: array
+ description: Array of `Log` objects.
+ required:
+ [
+ "blockHash",
+ "blockNumber",
+ "transactionIndex",
+ "address",
+ "logIndex",
+ "data",
+ "removed",
+ "topics",
+ "transactionHash",
+ ]
+ items:
+ type: object
+ properties:
+ blockHash:
+ type: string
+ description: hash of the block where this log was in. `null` when its pending log.
+ blockNumber:
+ type: string
+ description: The block num.
+ transactionIndex:
+ type: integer
+ description: Integer of the transactions index position log was created from. null when its pending log.
+ address:
+ type: string
+ description: 20 Bytes - address from which this log originated.
+ logIndex:
+ type: integer
+ description: Integer of the log index position in the block. null when its pending log.
+ data:
+ type: string
+ description: Contains one or more 32 Bytes non-indexed arguments of the log.
+ removed:
+ type: boolean
+ description: "`true` when the log was removed, due to a chain reorganization. `false` if its a valid log."
+ topics:
+ type: array
+ description: "Array of zero to four 32 Bytes `DATA` of indexed log arguments. In solidity: The first topic is the hash of the signature of the event (e.g. `Deposit(address,bytes32,uint256)`), except you declare the event with the anonymous specifier."
+ items:
+ type: string
+ transactionHash:
+ type: string
+ description: Hash of the transactions this log was created from. null when its pending log.
+ logsBloom:
+ type: string
+ description: 256 Bytes - Bloom filter for light clients to quickly retrieve related logs.
+ root:
+ type: string
+ description: 32 bytes of post-transaction stateroot. (pre Byzantium hard fork at block 4,370,000).
+ status:
+ type: integer
+ description: Either `1` (success) or `0` (failure). (post Byzantium hard fork at block 4,370,000).
+ effectiveGasPrice:
+ type: string
+ description: The actual price per unit of gas paid by the sender.
+ type:
+ type: string
+ description: The type of the transaction.
+ status:
+ type: string
+ description: The status of the `UserOperation`. Could be \"Mined\" or \"Preconfirmed\"."
+ examples:
+ - name: eth_getUserOperationReceipt example
+ params:
+ - name: userOpHash
+ value: "0x93c06f3f5909cc2b192713ed9bf93e3e1fde4b22fcd2466304fa404f9b80ff90"
+ result:
+ name: UserOperationReceipt
+ value:
+ userOpHash: "0x77c0b560eb0b042902abc5613f768d2a6b2d67481247e9663bf4d68dec0ca122"
+ entryPoint: "0xc944E90C64B2c07662A292be6244BDf05Cda44a7"
+ sender: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
+ nonce: 42
+ paymaster: "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6"
+ actualGasCost: 21000
+ actualGasUsed: 314159
+ success: true
+ reason: ""
+ logs: []
+ receipt:
+ transactionHash: "0x8fc90a6c3ee3001cdcbbb685b4fbe67b1fa2bec575b15b0395fea5540d0901ae"
+ blockHash: "0x58a945e1558810523df00490ff28cbe111b37851c44679ce5be1eeaebb4b4907"
+ blockNumber: "0xeb8822"
+ transactionIndex: "0x4e"
+ status: "Mined"
+
+ - name: eth_supportedEntryPoints
+ description: |
+ Returns an array of the `entryPoint` addresses supported by the client.
+
+ params: []
+ result:
+ name: Supported entryPoints
+ description: Array of supported `entryPoint` addresses.
+ schema:
+ type: array
+ items:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/address"
+ examples:
+ - name: eth_supportedEntryPoints example
+ params: []
+ result:
+ name: Supported entryPoints
+ value:
+ - "0xcd01C8aa8995A59eB7B2627E69b40e0524B5ecf8"
+ - "0x7A0A0d159218E6a2f407B99173A2b12A6DDfC2a6"
+
+ - name: eth_getUserOperationByHash
+ description: |
+ Return a `UserOperation` based on a `userOpHash`.
+
+
+ This method method retrieves and decodes userOperations from the logs of mined transactions by querying the node. If you attempt to fetch a userOperation from a distant past block, the request may be rejected by the node due to limitations on the block range size it can process.
+
+ The default range we support is 150 blocks, however the following networks have an unlimited range.
+
+ - Ethereum
+ - Polygon
+ - Base
+ - Optimism
+ - Worldchain
+ - Arbitrum
+
+ params:
+ - name: userOpHash
+ required: true
+ description: The `userOpHash` of the `UserOperation` to retrieve.
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/hash32"
+ result:
+ name: UserOperation
+ description: The `UserOperation` object.
+ schema:
+ type: object
+ required: ["entrypointV06Response", "entrypointV07Response"]
+ properties:
+ entrypointV06Response:
+ title: "Entrypoint v0.6 Response"
+ type: "object"
+ properties:
+ sender:
+ type: "string"
+ description: The account sending the userOperation.
+ nonce:
+ type: "string"
+ description: Anti-replay parameter; also used as the salt for first-time account creation.
+ initCode:
+ type: "string"
+ description: The initCode of the account (needed if and only if the account is not yet on-chain and needs to be created).
+ callData:
+ type: "string"
+ description: Encoded data for executing the primary function call or operation within the user's transaction, such as calling a smart contract function or transferring tokens. This data is passed to the sender's address during the execution of the userOperation.
+ callGasLimit:
+ type: "string"
+ description: The amount of gas to allocate for the main execution call.
+ verificationGasLimit:
+ type: "string"
+ description: The amount of gas to allocate for the verification step.
+ preVerificationGas:
+ type: "string"
+ description: The amount of gas to compensate the bundler for pre-verification execution and calldata.
+ maxFeePerGas:
+ type: "string"
+ description: The maximum fee per gas to pay for the execution of this operation (similar to EIP-1559 max_fee_per_gas).
+ maxPriorityFeePerGas:
+ type: "string"
+ description: Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas).
+ signature:
+ type: "string"
+ description: Data passed into the account along with the nonce during the verification step.
+ paymasterAndData:
+ type: "string"
+ description: Address of paymaster sponsoring the transaction, followed by extra data to send to the paymaster (empty for self-sponsored transaction).
+ entryPoint:
+ type: "string"
+ description: Address of the EntryPoint contract.
+ blockNumber:
+ type: "string"
+ description: Block number in which `UserOperation` is included.
+ blockHash:
+ type: "string"
+ description: Block hash of the block containing `UserOperation`.
+ transactionHash:
+ type: "string"
+ description: Transaction hash of the `UserOperation`.
+ entrypointV07Response:
+ title: "Entrypoint v0.7 Response"
+ type: "object"
+ properties:
+ sender:
+ type: "string"
+ description: The account sending the userOperation.
+ nonce:
+ type: "string"
+ description: Anti-replay parameter; also used as the salt for first-time account creation.
+ factory:
+ type: "string"
+ description: The account factory address (needed if and only if the account is not yet on-chain and needs to be created).
+ factoryData:
+ type: "string"
+ description: Data for the account factory (only if the account factory exists).
+ callData:
+ type: "string"
+ description: Encoded data for executing the primary function call or operation within the user's transaction, such as calling a smart contract function or transferring tokens. This data is passed to the sender's address during the execution of the userOperation.
+ callGasLimit:
+ type: "string"
+ description: The amount of gas to allocate for the main execution call.
+ verificationGasLimit:
+ type: "string"
+ description: The amount of gas to allocate for the verification step.
+ preVerificationGas:
+ type: "string"
+ description: The amount of gas to compensate the bundler for pre-verification execution and calldata.
+ maxFeePerGas:
+ type: "string"
+ description: The maximum fee per gas to pay for the execution of this operation (similar to EIP-1559 max_fee_per_gas).
+ maxPriorityFeePerGas:
+ type: "string"
+ description: Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas).
+ paymasterVerificationGasLimit:
+ type: "string"
+ description: The amount of gas to allocate for the paymaster validation code (only if a paymaster exists).
+ paymasterPostOpGasLimit:
+ type: "string"
+ description: The amount of gas to allocate for the paymaster post-op code (only if a paymaster exists).
+ signature:
+ type: "string"
+ description: Data passed into the account along with the nonce during the verification step.
+ paymaster:
+ type: "string"
+ description: Address of the paymaster contract (or empty, if the account pays for itself).
+ paymasterData:
+ type: "string"
+ description: Data for the paymaster (only if the paymaster exists).
+ entryPoint:
+ type: "string"
+ description: Address of the EntryPoint contract.
+ blockNumber:
+ type: "string"
+ description: Block number in which `UserOperation` is included.
+ blockHash:
+ type: "string"
+ description: Block hash of the block containing `UserOperation`.
+ transactionHash:
+ type: "string"
+ description: Transaction hash of the `UserOperation`.
+ examples:
+ - name: eth_getUserOperationByHash example
+ params:
+ - name: userOpHash
+ value: "0x77c0b560eb0b042902abc5613f768d2a6b2d67481247e9663bf4d68dec0ca122"
+ result:
+ name: UserOperation
+ value:
+ sender: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
+ nonce: 1
+ initCode: "0x"
+ callData: "0x"
+ callGasLimit: "0x0"
+ verificationGasLimit: "0x0"
+ preVerificationGas: "0x0"
+ maxFeePerGas: "0x0"
+ maxPriorityFeePerGas: "0x0"
+ paymasterAndData: "0x"
+ signature: "0x"
+ entryPoint: "0x"
+ blockNumber: "0x"
+ blockHash: "0x"
+ transactionHash: "0x"
+
+ - name: eth_sendUserOperation
+ description: |
+ Sends a `UserOperation` to the given EVM network.
+
+ params:
+ - name: UserOperation
+ required: true
+ description: The UserOperation object. This can be either a v0.6 or v0.7 user operation, but MUST match the version of the entry point at the address of the second parameter.
+ schema:
+ oneOf:
+ - $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06"
+ - $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07"
+ - name: Entrypoint address
+ required: true
+ description: The EntryPoint address the request should be sent through. This MUST be one of the EntryPoints returned by the `supportedEntryPoints` RPC call.
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/address"
+ result:
+ name: userOpHash
+ description: The calculated `userOpHash` for the `UserOperation`.
+ schema:
+ type: string
+ format: "hex"
+ examples:
+ - name: eth_sendUserOperation example
+ params:
+ - name: UserOperation
+ value: { sender: "0x...", nonce: "0x...", callData: "0x..." }
+ - name: Entrypoint address
+ value: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ result:
+ name: userOpHash
+ value:
+ result: "0x1234...5678"
+
+ - name: eth_estimateUserOperationGas
+ description: |
+ Estimates the gas values for a `UserOperation`.
+
+
+ This endpoint requires a dummy signature in the `userOp`. Check our [FAQs](/docs/reference/bundler-faqs#what-is-a-dummy-signature) to learn what a dummy signature is.
+
+ params:
+ - name: UserOperation
+ required: true
+ description: Contains gas limits and prices (optional). This can be either a v0.6 or v0.7 userOperation but must match the version of the `EntryPoint` at the address of the second parameter.
+ schema:
+ oneOf:
+ - $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06"
+ - $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07"
+ - name: entryPoint
+ required: true
+ description: The address to which the request should be sent. This must be one of the `EntryPoint` returned by the `supportedEntryPoints` method and should match the version of the `userOp` in the first parameter.
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/address"
+ - name: "stateOverrideSet"
+ required: false
+ description: |
+ Allows changes to the state of a contract before executing the call. For example, you can modify variable values (like balances or approvals) for that call without changing the contract itself on the blockchain.
+
+ In more technical terms, the state override set is an optional parameter that allows executing the call against a modified chain state. It is an address-to-state mapping, where each entry specifies some state to be overridden prior to executing the call.
+ schema:
+ type: "object"
+ properties:
+ balance:
+ type: "string"
+ description: "Fake balance to set for the account before executing the call (<= 32 bytes)"
+ nonce:
+ type: "string"
+ description: Fake nonce to set for the account before executing the call (<= 8 bytes).
+ code:
+ type: "string"
+ description: "Fake EVM bytecode to inject into the account before executing the call."
+ state:
+ type: "object"
+ description: "Fake key-value mapping to override all slots in the account storage before executing the call."
+ stateDiff:
+ type: "object"
+ description: "Fake key-value mapping to override individual slots in the account storage before executing the call."
+ result:
+ name: Gas estimation
+ description: Gas values estimated for the userOperation.
+ schema:
+ type: object
+ required: ["entrypointV06Response", "entrypointV07Response"]
+ properties:
+ entrypointV06Response:
+ type: object
+ title: Entrypoint v0.6 Response
+ properties:
+ preVerificationGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Gas overhead of this userOperation.
+ verificationGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Actual gas used by the validation of this userOperation.
+ callGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Value used by inner account execution.
+ entrypointV07Response:
+ type: object
+ title: Entrypoint v0.7 Response
+ properties:
+ preVerificationGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Gas overhead of this userOperation.
+ verificationGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Actual gas used by the validation of this userOperation.
+ callGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Value used by inner account execution.
+ paymasterVerificationGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint256"
+ description: Value used by the paymaster during verification.
+ examples:
+ - name: eth_estimateUserOperationGas example
+ params:
+ - name: UserOperation
+ value: { sender: "0x...", nonce: "0x...", callData: "0x..." }
+ - name: Entrypoint address
+ value: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ result:
+ name: Gas estimation
+ value:
+ preVerificationGas: "0x1"
+ verificationGasLimit: "0x1"
+ callGasLimit: "0x1"
+ paymasterVerificationGasLimit: "0x1"
diff --git a/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml b/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml
new file mode 100644
index 000000000..744383cb2
--- /dev/null
+++ b/src/openrpc/alchemy/gas-manager-coverage/gas-manager-coverage.yaml
@@ -0,0 +1,924 @@
+# yaml-language-server: $schema=https://meta.open-rpc.org/
+
+$schema: https://meta.open-rpc.org/
+openrpc: 1.2.4
+info:
+ title: Alchemy Gas Manager Coverage API JSON-RPC Specification
+ description: A specification of the standard JSON-RPC methods for Gas Manager Coverage API.
+ version: 0.0.0
+servers:
+ - url: https://eth-mainnet.g.alchemy.com/v2
+ name: Ethereum Mainnet
+ - url: https://eth-sepolia.g.alchemy.com/v2
+ name: Ethereum Sepolia
+ - url: https://anime-mainnet.g.alchemy.com/v2
+ name: Anime Mainnet
+ - url: https://anime-sepolia.g.alchemy.com/v2
+ name: Anime Sepolia
+ - url: https://apechain-mainnet.g.alchemy.com/v2
+ name: ApeChain Mainnet
+ - url: https://apechain-curtis.g.alchemy.com/v2
+ name: ApeChain Curtis
+ - url: https://arb-mainnet.g.alchemy.com/v2
+ name: Arbitrum Mainnet
+ - url: https://arb-sepolia.g.alchemy.com/v2
+ name: Arbitrum Sepolia
+ - url: https://arbnova-mainnet.g.alchemy.com/v2
+ name: Arbitrum Nova Mainnet
+ - url: https://base-mainnet.g.alchemy.com/v2
+ name: Base Mainnet
+ - url: https://base-sepolia.g.alchemy.com/v2
+ name: Base Sepolia
+ - url: https://berachain-mainnet.g.alchemy.com/v2
+ name: Berachain Mainnet
+ - url: https://bnb-mainnet.g.alchemy.com/v2
+ name: BNB Smart Chain Mainnet
+ - url: https://bnb-testnet.g.alchemy.com/v2
+ name: BNB Smart Chain Testnet
+ - url: https://celo-mainnet.g.alchemy.com/v2
+ name: Celo Mainnet
+ - url: https://celo-sepolia.g.alchemy.com/v2
+ name: Celo Sepolia
+ - url: https://frax-mainnet.g.alchemy.com/v2
+ name: Frax Mainnet
+ - url: https://gensyn-testnet.g.alchemy.com/v2
+ name: Gensyn Testnet
+ - url: https://hyperliquid-mainnet.g.alchemy.com/v2
+ name: Hyperliquid Mainnet
+ - url: https://hyperliquid-testnet.g.alchemy.com/v2
+ name: Hyperliquid Testnet
+ - url: https://ink-mainnet.g.alchemy.com/v2
+ name: Ink Mainnet
+ - url: https://ink-sepolia.g.alchemy.com/v2
+ name: Ink Sepolia
+ - url: https://polygon-mainnet.g.alchemy.com/v2
+ name: Polygon Mainnet
+ - url: https://polygon-amoy.g.alchemy.com/v2
+ name: Polygon Amoy
+ - url: https://monad-mainnet.g.alchemy.com/v2
+ name: Monad Mainnet
+ - url: https://monad-testnet.g.alchemy.com/v2
+ name: Monad Testnet
+ - url: https://mythos-mainnet.g.alchemy.com/v2
+ name: Mythos Mainnet
+ - url: https://opbnb-mainnet.g.alchemy.com/v2
+ name: opBNB Mainnet
+ - url: https://opbnb-testnet.g.alchemy.com/v2
+ name: opBNB Testnet
+ - url: https://opt-mainnet.g.alchemy.com/v2
+ name: OP Mainnet Mainnet
+ - url: https://opt-sepolia.g.alchemy.com/v2
+ name: OP Mainnet Sepolia
+ - url: https://plasma-mainnet.g.alchemy.com/v2
+ name: Plasma Mainnet
+ - url: https://plasma-testnet.g.alchemy.com/v2
+ name: Plasma Testnet
+ - url: https://polynomial-mainnet.g.alchemy.com/v2
+ name: Polynomial Mainnet
+ - url: https://polynomial-sepolia.g.alchemy.com/v2
+ name: Polynomial Sepolia
+ - url: https://rise-testnet.g.alchemy.com/v2
+ name: Rise Testnet
+ - url: https://shape-mainnet.g.alchemy.com/v2
+ name: Shape Mainnet
+ - url: https://shape-sepolia.g.alchemy.com/v2
+ name: Shape Sepolia
+ - url: https://solana-mainnet.g.alchemy.com/v2
+ name: Solana Mainnet
+ - url: https://solana-devnet.g.alchemy.com/v2
+ name: Solana Devnet
+ - url: https://soneium-mainnet.g.alchemy.com/v2
+ name: Soneium Mainnet
+ - url: https://soneium-minato.g.alchemy.com/v2
+ name: Soneium Minato
+ - url: https://stable-mainnet.g.alchemy.com/v2
+ name: Stable Mainnet
+ - url: https://stable-testnet.g.alchemy.com/v2
+ name: Stable Testnet
+ - url: https://story-mainnet.g.alchemy.com/v2
+ name: Story Mainnet
+ - url: https://story-aeneid.g.alchemy.com/v2
+ name: Story Aeneid
+ - url: https://unichain-mainnet.g.alchemy.com/v2
+ name: Unichain Mainnet
+ - url: https://unichain-sepolia.g.alchemy.com/v2
+ name: Unichain Sepolia
+ - url: https://worldchain-mainnet.g.alchemy.com/v2
+ name: World Chain Mainnet
+ - url: https://worldchain-sepolia.g.alchemy.com/v2
+ name: World Chain Sepolia
+ - url: https://zora-mainnet.g.alchemy.com/v2
+ name: Zora Mainnet
+ - url: https://zora-sepolia.g.alchemy.com/v2
+ name: Zora Sepolia
+methods:
+ - name: "alchemy_requestGasAndPaymasterAndData"
+ description: |
+ Requests gas coverage for a `UserOperation`. Optionally accepts fee parameter overrides to be used in the `UserOperation`.
+ params:
+ - name: "policyId"
+ required: true
+ description: "The Gas Manager Policy ID or list of Gas Manager Policy IDs."
+ schema:
+ oneOf:
+ - type: "string"
+ title: "policyId"
+ format: "uuid"
+ - type: "array"
+ title: "policyId Array"
+ items:
+ type: "string"
+ format: "uuid"
+
+ - name: "erc20Context"
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/erc20Context"
+ description: "Erc20 context, required to enable users to pay gas with ERC-20 tokens."
+ - name: "entryPoint"
+ required: true
+ description: The EntryPoint address the request should be sent through. This MUST be one of the EntryPoints returned by the `supportedEntryPoints` RPC call and match the version of the userOperation in the `userOperation` field.
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]*$"
+ - name: "dummySignature"
+ required: true
+ description: Dummy signature for the userOperation. This is useful for estimating gas costs. Check our [FAQs](/docs/reference/bundler-faqs#what-is-a-dummy-signature) to learn what a dummy signature is.
+ schema:
+ type: "string"
+ - name: "userOperation"
+ required: true
+ description: An object containing optional gas parameters, `paymasterAndData`, and signature fields. It can be either a v0.6 or v0.7 userOperation but must match the version of the EntryPoint at the specified `entryPoint`.
+ schema:
+ title: "User Operation, either v0.6 or v0.7 (missing signature, paymasterData, and gas fields)"
+ oneOf:
+ - title: "User Operation v0.6"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06Partial"
+
+ - title: "User Operation v0.7"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07Partial"
+ - name: "overrides"
+ description: |
+ Optional fields that override our gas and fee estimates. Use this parameter to override: `maxFeePerGas`, `maxPriorityFeePerGas`, `callGasLimit`, `verificationGasLimit`, or `preVerificationGas`.
+
+ This will apply either:
+ - absolutes overrides (using a hex string) simply overriding estimated values entirely OR
+ - multiplier overrides relative to our estimates (in the format of { "multiplier": number } with max precision of 4 decimal places). For example, if the override value is { multiplier: 1.1 } for the `maxPriorityFeePerGas` field, then a 1.1 multiplier, or a 10% increase, is applied to the estimated `maxPriorityFeePerGas` of the userOp.
+
+ A higher buffer may give userOps a better chance to mine if the L1/L2 gas and fees change.
+
+ ```json
+ "overrides": {
+ "maxFeePerGas": "hex string" | { "multiplier": number },
+ "maxPriorityFeePerGas": "hex string" | { "multiplier": number },
+ "callGasLimit": "hex string" | { "multiplier": number },
+ "verificationGasLimit": "hex string" | { "multiplier": number },
+ "preVerificationGas": "hex string" | { "multiplier": number },
+ }
+ ```
+
+ schema:
+ type: "object"
+ properties:
+ maxFeePerGas:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ maxPriorityFeePerGas:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ callGasLimit:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ verificationGasLimit:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ preVerificationGas:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ - name: "stateOverrideSet"
+ description: |
+ Allows changes to the state of a contract before executing the call. For example, you can modify variable values (like balances or approvals) for that call without changing the contract itself on the blockchain.
+
+ In more technical terms, the state override set is an optional parameter that allows executing the call against a modified chain state. It is an address-to-state mapping, where each entry specifies some state to be overridden prior to executing the call.
+ schema:
+ type: "object"
+ properties:
+ balance:
+ type: "string"
+ description: "Fake balance to set for the account before executing the call (<= 32 bytes)"
+ nonce:
+ type: "string"
+ description: Fake nonce to set for the account before executing the call (<= 8 bytes).
+ code:
+ type: "string"
+ description: "Fake EVM bytecode to inject into the account before executing the call."
+ state:
+ type: "object"
+ description: "Fake key-value mapping to override all slots in the account storage before executing the call."
+ stateDiff:
+ type: "object"
+ description: "Fake key-value mapping to override individual slots in the account storage before executing the call."
+ - name: "webhookData"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ schema:
+ type: "string"
+ result:
+ name: "Response"
+ description: "The response object may be one of two types:"
+ schema:
+ type: "object"
+ required: ["entrypointV06Response", "entrypointV07Response"]
+ properties:
+ entrypointV06Response:
+ title: "Entrypoint v0.6 Response"
+ type: "object"
+ properties:
+ paymasterAndData:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/bytes"
+ description: "The 0x‑prefixed hex string for the user to put into the UO's paymasterAndData field. Contains gas manager address, sigTimeRange, and secp256k1 signature."
+ callGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The amount of gas to allocate for the main execution call."
+ verificationGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The amount of gas to allocate for the verification step."
+ preVerificationGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The amount of gas to compensate the bundler for pre-verification execution and calldata."
+ maxFeePerGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The maximum fee per gas to pay for the execution of this operation (similar to EIP-1559 max_fee_per_gas)."
+ maxPriorityFeePerGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas)."
+ entrypointV07Response:
+ title: "Entrypoint v0.7 Response"
+ type: "object"
+ properties:
+ paymaster:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/address"
+ description: "The paymaster address used in the operation."
+ paymasterData:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/bytes"
+ description: "The data for the paymaster in the operation."
+ callGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The amount of gas to allocate for the main execution call."
+ verificationGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The amount of gas to allocate for the verification step."
+ preVerificationGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The amount of gas to compensate the bundler for pre-verification execution and calldata."
+ maxFeePerGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The maximum fee per gas to pay for the execution of this operation (similar to EIP-1559 max_fee_per_gas)."
+ maxPriorityFeePerGas:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas)."
+ paymasterVerificationGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The gas limit for paymaster verification."
+ paymasterPostOpGasLimit:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint64"
+ description: "The gas limit for paymaster post-operation."
+ examples:
+ - name: "alchemy_requestGasAndPaymasterAndData example"
+ params:
+ - name: "params"
+ value:
+ webhookData: "example webhook data"
+ policyId: "69d524a7-e932-4214-8673-dcdcba31bb42"
+ entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ dummySignature: "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
+ userOperation:
+ sender: "0x1234567890123456789012345678901234567890"
+ nonce: "0x1"
+ callData: "0xabcdef"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+ result:
+ name: "alchemy_requestGasAndPaymasterAndData response"
+ value:
+ entrypointV06Response:
+ paymasterAndData: "0xabcde"
+ entrypointV07Response:
+ paymasterData: "0x67890"
+ paymasterVerificationGasLimit: "0x5208"
+ paymasterPostOpGasLimit: "0x5208"
+
+ - name: "alchemy_requestPaymasterAndData"
+ description: |
+ Requests gas coverage for a `UserOperation`.
+
+
+ Please use `alchemy_requestGasAndPaymasterAndData` instead.
+
+ While you can use `alchemy_requestPaymasterAndData` to request a paymaster signature, it's important to note that this function requires gas fields to be part of the `UserOperation` passed as a parameter. These gas fields are influenced by the response of this endpoint, creating a circular dependency.
+
+ To simplify this process, we introduced alchemy_requestGasAndPaymasterAndData. This function both estimates gas and provides signed `paymaster` and `paymasterData` fields, therefore resolving the circular dependency.
+
+
+ params:
+ - name: "policyId"
+ required: true
+ description: "The Gas Manager policy ID or list of Gas Manager policy IDs."
+ schema:
+ oneOf:
+ - type: "string"
+ title: "policyId"
+ format: "uuid"
+ - type: "array"
+ title: "policyId Array"
+ items:
+ type: "string"
+ format: "uuid"
+ - name: "erc20Context"
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/erc20Context"
+ description: "Erc20 context, required to enable users to pay gas with ERC-20 tokens."
+ - name: "entryPoint"
+ required: true
+ description: The EntryPoint address the request should be sent through. This MUST be one of the EntryPoints returned by the `supportedEntryPoints` RPC call and match the version of the userOperation in the `userOperation` field.
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]*$"
+ - name: "userOperation"
+ required: true
+ description: "Test Partial UserOperation object, missing `paymasterAndData` and `signature` fields. This can be either a v0.6 or v0.7 userOperation, but **MUST** match the version of the EntryPoint at the address in the `entryPoint` field."
+ schema:
+ title: "User Operation, either v0.6 or v0.7 (missing signature and paymasterData)"
+ oneOf:
+ - title: "User Operation v0.6"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06Partial"
+
+ - title: "User Operation v0.7"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07Partial"
+
+ - name: "webhookData"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ schema:
+ type: "string"
+ result:
+ name: "Response"
+ description: "The response object may be one of two types:"
+ schema:
+ type: "object"
+ required: ["entrypointV06Response", "entrypointV07Response"]
+ properties:
+ entrypointV06Response:
+ title: "Entrypoint v0.6 Response"
+ type: "object"
+ properties:
+ paymasterAndData:
+ type: "string"
+ description: "The 0x-prefixed hex string for the user to put into the UO's paymasterAndData field. Contains gas manager `address`, `sigTimeRange`, and secp256k1 `signature`."
+ entrypointV07Response:
+ title: "Entrypoint v0.7 Response"
+ type: "object"
+ properties:
+ paymaster:
+ type: "string"
+ description: "The paymaster address used in the operation."
+ paymasterData:
+ type: "string"
+ description: "The data for the paymaster in the operation."
+ paymasterVerificationGasLimit:
+ type: "string"
+ description: "The gas limit for paymaster verification."
+ paymasterPostOpGasLimit:
+ type: "string"
+ description: "The gas limit for paymaster post-operation."
+ examples:
+ - name: "alchemy_requestPaymasterAndData example"
+ params:
+ - name: "params"
+ value:
+ webhookData: "example webhook data"
+ policyId: "69d524a7-e932-4214-8673-dcdcba31bb42"
+ entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ userOperation:
+ sender: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
+ nonce: "0x2"
+ initCode: "0x"
+ callData: "0xdeadbeef"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+ result:
+ name: "alchemy_requestPaymasterAndData response"
+ value:
+ entrypointV06Response:
+ paymasterAndData: "0xdead"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+ entrypointV07Response:
+ paymaster: "0xfeed"
+ paymasterData: "0xfeed"
+ paymasterVerificationGasLimit: "0x5208"
+ paymasterPostOpGasLimit: "0x5208"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+
+ - name: "pm_getPaymasterData"
+ description: Returns values to be used in paymaster-related fields of a signed userOperation. These values are not stubs and will be included in a signed userOperation as part of an `eth_sendUserOperation` call.
+ params:
+ - name: "userOperation"
+ description: "Partial UserOperation object (either v0.6 or v0.7, missing signature and paymasterData)."
+ required: true
+ schema:
+ title: "User Operation, either v0.6 or v0.7 (missing signature and paymasterData)"
+ oneOf:
+ - title: "User Operation v0.6"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06Partial"
+
+ - title: "User Operation v0.7"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07Partial"
+ - name: "entryPoint"
+ required: true
+ description: "The EntryPoint address the request should be sent through. Must be one of the EntryPoints returned by the `supportedEntryPoints` RPC call."
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]*$"
+ - name: "chainId"
+ required: true
+ description: "The chain ID of the network the userOperation will be submitted on."
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]+$"
+ - name: "context"
+ description: "An object containing context. Must include a Gas Manager Policy Id and may include webhookData."
+ schema:
+ type: "object"
+ required: ["policyId"]
+ properties:
+ policyId:
+ oneOf:
+ - type: "string"
+ format: "uuid"
+ - type: "array"
+ items:
+ type: "string"
+ format: "uuid"
+ description: "The policy ID or list of policy IDs."
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ erc20Context:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/erc20Context"
+ description: "Erc20 context, required to enable users to pay gas with ERC-20 tokens."
+ result:
+ name: "Response"
+ description: "The response object may be one of two types:"
+ schema:
+ type: "object"
+ required: ["entrypointV06Response", "entrypointV07Response"]
+ properties:
+ entrypointV06Response:
+ title: "EntryPoint v0.6 Response"
+ type: "object"
+ properties:
+ paymasterAndData:
+ type: "string"
+ description: "The 0x‑prefixed hex string for the user to put into the UO's paymasterAndData field."
+ entrypointV07Response:
+ title: "EntryPoint v0.7 Response"
+ type: "object"
+ properties:
+ paymaster:
+ type: "string"
+ description: "The address of the paymaster."
+ paymasterData:
+ type: "string"
+ description: "The data for the paymaster in the userOperation."
+ examples:
+ - name: "pm_getPaymasterData example"
+ params:
+ - name: "userOperation"
+ value:
+ sender: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
+ nonce: "0x2"
+ callData: "0xdeadbeef"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+ - name: "entryPoint"
+ value: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ - name: "chainId"
+ value: "0x1"
+ - name: "context"
+ value:
+ policyId: "69d524a7-e932-4214-8673-dcdcba31bb42"
+ result:
+ name: "pm_getPaymasterData response"
+ value:
+ entrypointV06Response:
+ paymasterAndData: "0xabc123"
+ entrypointV07Response:
+ paymaster: "0xfeed"
+ paymasterData: "0x789abc"
+
+ - name: "pm_getPaymasterStubData"
+ description: "Returns stub values to be used in paymaster-related fields of an unsigned userOperation for gas estimation."
+ params:
+ - name: "userOperation"
+ description: "Partial UserOperation object (either v0.6 or v0.7, missing signature and paymasterData)."
+ required: true
+ schema:
+ title: "User Operation, either v0.6 or v0.7 (missing signature and paymasterData)"
+ oneOf:
+ - title: "User Operation v0.6"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06Partial"
+
+ - title: "User Operation v0.7"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07Partial"
+ - name: "entryPoint"
+ required: true
+ description: "The EntryPoint address the request should be sent through. Must be one of the EntryPoints returned by the supportedEntryPoints RPC call."
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]*$"
+ - name: "chainId"
+ required: true
+ description: "The chain ID of the network the userOperation will be submitted on."
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]+$"
+ - name: "context"
+ description: "An object containing context. Must include a policyId and may include webhookData."
+ schema:
+ type: "object"
+ required: ["policyId"]
+ properties:
+ policyId:
+ oneOf:
+ - type: "string"
+ format: "uuid"
+ - type: "array"
+ items:
+ type: "string"
+ format: "uuid"
+ description: "The policy ID or list of policy IDs."
+ erc20Context:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/erc20Context"
+ description: "Erc20 context, required to enable users to pay gas with ERC-20 tokens."
+ result:
+ name: "Response"
+ description: "The response object may be one of two types:"
+ schema:
+ type: "object"
+ required: ["entrypointV06Response", "entrypointV07Response"]
+ properties:
+ entrypointV06Response:
+ title: "EntryPoint v0.6 Stub Response"
+ type: "object"
+ properties:
+ paymasterAndData:
+ type: "string"
+ description: "The 0x‑prefixed hex string for the user to put into the userOperation's paymasterAndData field."
+ sponsor:
+ type: "object"
+ description: "Information about who is sponsoring the user's transaction."
+ properties:
+ name:
+ type: "string"
+ description: "The name of the entity sponsoring the userOperation."
+ entrypointV07Response:
+ title: "EntryPoint v0.7 Stub Response"
+ type: "object"
+ properties:
+ paymaster:
+ type: "string"
+ description: "The address of the paymaster."
+ paymasterData:
+ type: "string"
+ description: "The data for the paymaster in the userOperation."
+ sponsor:
+ type: "object"
+ description: "Information about who is sponsoring the user's transaction."
+ properties:
+ name:
+ type: "string"
+ description: "The name of the entity sponsoring the userOperation."
+ examples:
+ - name: "pm_getPaymasterDataStub example"
+ params:
+ - name: "userOperation"
+ value:
+ sender: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"
+ nonce: "0x3"
+ callData: "0x1234"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+ - name: "entryPoint"
+ value: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ - name: "chainId"
+ value: "0x1"
+ - name: "context"
+ value:
+ policyId: "69d524a7-e932-4214-8673-dcdcba31bb42"
+ result:
+ name: "pm_getPaymasterDataStub response"
+ value:
+ entrypointV06Response:
+ paymasterAndData: "0xabc123"
+ sponsor:
+ name: "SponsorName"
+ entrypointV07Response:
+ paymasterData: "0x789abc"
+ sponsor:
+ name: "SponsorName"
+
+ - name: "alchemy_requestFeePayer"
+ description: |
+ Requests fee and rent sponsorship for a Solana transaction and returns the **fully-signed** `serializedTransaction` (now containing the fee-payer signature).
+
+
+ We support rent sponsorship for `createAccount` and `createAssociatedTokenAccount` programs. If you need support for sponsoring rent in custom programs, please contact us at [wallets@alchemy.com](mailto:wallets@alchemy.com).
+
+
+ params:
+ - name: policyId
+ required: true
+ description: "The Gas Manager Policy ID or list of Gas Manager Policy IDs."
+ schema:
+ oneOf:
+ - title: policyId
+ type: "string"
+ format: "uuid"
+ - title: policyIdArray
+ type: "array"
+ items:
+ type: "string"
+ format: "uuid"
+ - name: serializedTransaction
+ required: true
+ description: |
+ The unsigned transaction produced by `tx.serialize()`.
+ It may already contain a `feePayer`; the service will overwrite it
+ with the sponsoring account and add the fee-payer signature.
+ schema:
+ type: "string"
+ pattern: "^[A-Za-z0-9+/]+={0,2}$"
+ result:
+ name: "Sponsored transaction wrapper"
+ description: >
+ Object containing the **fully-signed** transaction ready for
+ `sendRawTransaction`.
+ schema:
+ type: "object"
+ required: ["serializedTransaction"]
+ properties:
+ serializedTransaction:
+ title: "base-64-encoded Solana transaction"
+ type: "string"
+ description: >
+ The transaction after the service sets the `feePayer` field and
+ adds the fee-payer signature.
+ servers:
+ [
+ {
+ "url": "https://solana-mainnet.g.alchemy.com/v2",
+ "name": "Solana Mainnet",
+ },
+ {
+ "url": "https://solana-devnet.g.alchemy.com/v2",
+ "name": "Solana Devnet",
+ },
+ ]
+ examples:
+ - name: "alchemy_requestFeePayer example"
+ params:
+ - name: "sponsorshipRequest"
+ value:
+ policyId: "69d524a7-e932-4214-8673-dcdcba31bb42"
+ serializedTransaction: "AgICAQMEBQYHCAkKCwwNDg8BCg==" # unsigned tx
+ result:
+ name: "Sponsored transaction wrapper"
+ value:
+ serializedTransaction: "AQIDBAUGBwgJCgsMDQ4PEA==" # signed tx
+ - name: "alchemy_requestPaymasterTokenQuote"
+ description: |
+ Requests ERC20 fee quote for a `UserOperation`. Optionally accepts fee parameter overrides to be used in the `UserOperation`.
+ params:
+ - name: "policyId"
+ required: true
+ description: "The Gas Manager Policy ID or list of Gas Manager Policy IDs."
+ schema:
+ oneOf:
+ - type: "string"
+ title: "policyId"
+ format: "uuid"
+ - type: "array"
+ title: "policyId Array"
+ items:
+ type: "string"
+ format: "uuid"
+
+ - name: "erc20Context"
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/erc20Context"
+ description: "Erc20 context, required to enable users to pay gas with ERC-20 tokens."
+ - name: "entryPoint"
+ required: true
+ description: The EntryPoint address the request should be sent through. This MUST be one of the EntryPoints returned by the `supportedEntryPoints` RPC call and match the version of the userOperation in the `userOperation` field.
+ schema:
+ type: "string"
+ pattern: "^0[xX][0-9a-fA-F]*$"
+ - name: "dummySignature"
+ required: true
+ description: Dummy signature for the userOperation. This is useful for estimating gas costs. Check our [FAQs](/docs/reference/bundler-faqs#what-is-a-dummy-signature) to learn what a dummy signature is.
+ schema:
+ type: "string"
+ - name: "userOperation"
+ required: true
+ description: An object containing optional gas parameters, `paymasterAndData`, and signature fields. It can be either a v0.6 or v0.7 userOperation but must match the version of the EntryPoint at the specified `entryPoint`.
+ schema:
+ title: "User Operation, either v0.6 or v0.7 (missing signature, paymasterData, and gas fields)"
+ oneOf:
+ - title: "User Operation v0.6"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06Partial"
+
+ - title: "User Operation v0.7"
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07Partial"
+ - name: "overrides"
+ description: |
+ Optional fields that override our gas and fee estimates. Use this parameter to override: `maxFeePerGas`, `maxPriorityFeePerGas`, `callGasLimit`, `verificationGasLimit`, or `preVerificationGas`.
+
+ This will apply either:
+ - absolutes overrides (using a hex string) simply overriding estimated values entirely OR
+ - multiplier overrides relative to our estimates (in the format of { "multiplier": number } with max precision of 4 decimal places). For example, if the override value is { multiplier: 1.1 } for the `maxPriorityFeePerGas` field, then a 1.1 multiplier, or a 10% increase, is applied to the estimated `maxPriorityFeePerGas` of the userOp.
+
+ A higher buffer may give userOps a better chance to mine if the L1/L2 gas and fees change.
+
+ ```json
+ "overrides": {
+ "maxFeePerGas": "hex string" | { "multiplier": number },
+ "maxPriorityFeePerGas": "hex string" | { "multiplier": number },
+ "callGasLimit": "hex string" | { "multiplier": number },
+ "verificationGasLimit": "hex string" | { "multiplier": number },
+ "preVerificationGas": "hex string" | { "multiplier": number },
+ }
+ ```
+
+ schema:
+ type: "object"
+ properties:
+ maxFeePerGas:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ maxPriorityFeePerGas:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ callGasLimit:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ verificationGasLimit:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ preVerificationGas:
+ oneOf:
+ - type: "string"
+ description: "Hex string."
+ - type: "object"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier value."
+ - name: "stateOverrideSet"
+ description: |
+ Allows changes to the state of a contract before executing the call. For example, you can modify variable values (like balances or approvals) for that call without changing the contract itself on the blockchain.
+
+ In more technical terms, the state override set is an optional parameter that allows executing the call against a modified chain state. It is an address-to-state mapping, where each entry specifies some state to be overridden prior to executing the call.
+ schema:
+ type: "object"
+ properties:
+ balance:
+ type: "string"
+ description: "Fake balance to set for the account before executing the call (<= 32 bytes)"
+ nonce:
+ type: "string"
+ description: Fake nonce to set for the account before executing the call (<= 8 bytes).
+ code:
+ type: "string"
+ description: "Fake EVM bytecode to inject into the account before executing the call."
+ state:
+ type: "object"
+ description: "Fake key-value mapping to override all slots in the account storage before executing the call."
+ stateDiff:
+ type: "object"
+ description: "Fake key-value mapping to override individual slots in the account storage before executing the call."
+ result:
+ name: "Response"
+ description: "The response object may be one of two types:"
+ schema:
+ type: "object"
+ properties:
+ tokensPerEth:
+ title: "erc20 token amount per ethereum"
+ type: "string"
+ description: >
+ The erc20 amout per 1 eth.
+ estimatedTokenAmount:
+ title: "estimated erc20 token amount to pay for the user operation"
+ type: "string"
+ description: >
+ The estimated erc20 token amount to pay for the user operation.
+ estimatedUsd:
+ title: "estimated USD amount to pay for the user operation"
+ type: "string"
+ description: >
+ The estimated USD amount to pay for the user operation.
+
+ examples:
+ - name: "alchemy_requestPaymasterTokenQuote example"
+ params:
+ - name: "params"
+ value:
+ policyId: "69d524a7-e932-4214-8673-dcdcba31bb42"
+ entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ dummySignature: "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
+ userOperation:
+ sender: "0x1234567890123456789012345678901234567890"
+ nonce: "0x1"
+ callData: "0xabcdef"
+ callGasLimit: "0x5208"
+ verificationGasLimit: "0x5208"
+ preVerificationGas: "0x5208"
+ maxFeePerGas: "0x1"
+ maxPriorityFeePerGas: "0x1"
+ result:
+ name: "alchemy_requestPaymasterTokenQuote response"
+ value:
+ tokensPerEth: "2413210000"
+ estimatedTokenAmount: "100000"
+ estimatedUsd: "0.1"
diff --git a/src/openrpc/alchemy/userop-sim/userop-sim.yaml b/src/openrpc/alchemy/userop-sim/userop-sim.yaml
new file mode 100644
index 000000000..b3e6eb498
--- /dev/null
+++ b/src/openrpc/alchemy/userop-sim/userop-sim.yaml
@@ -0,0 +1,107 @@
+# yaml-language-server: $schema=https://meta.open-rpc.org/
+
+$schema: https://meta.open-rpc.org/
+openrpc: 1.2.4
+info:
+ title: Alchemy User Operation Simulation API JSON-RPC Specification
+ description: A specification of the standard JSON-RPC methods for Alchemy User Operation Simulation API.
+ version: 0.0.0
+servers:
+ - url: https://eth-mainnet.g.alchemy.com/v2
+ name: Ethereum Mainnet
+ - url: https://eth-mainnetbeacon.g.alchemy.com/v2
+ name: Ethereum Mainnet Beacon
+ - url: https://eth-holeskybeacon.g.alchemy.com/v2
+ name: Ethereum Holešky Beacon
+ - url: https://eth-hoodi.g.alchemy.com/v2
+ name: Ethereum Hoodi
+ - url: https://eth-hoodibeacon.g.alchemy.com/v2
+ name: Ethereum Hoodi Beacon
+ - url: https://eth-sepolia.g.alchemy.com/v2
+ name: Ethereum Sepolia
+ - url: https://eth-sepoliabeacon.g.alchemy.com/v2
+ name: Ethereum Sepolia Beacon
+ - url: https://arb-mainnet.g.alchemy.com/v2
+ name: Arbitrum Mainnet
+ - url: https://arb-sepolia.g.alchemy.com/v2
+ name: Arbitrum Sepolia
+ - url: https://base-mainnet.g.alchemy.com/v2
+ name: Base Mainnet
+ - url: https://base-sepolia.g.alchemy.com/v2
+ name: Base Sepolia
+ - url: https://polygon-mainnet.g.alchemy.com/v2
+ name: Polygon Mainnet
+ - url: https://polygon-amoy.g.alchemy.com/v2
+ name: Polygon Amoy
+ - url: https://opt-mainnet.g.alchemy.com/v2
+ name: OP Mainnet Mainnet
+ - url: https://opt-sepolia.g.alchemy.com/v2
+ name: OP Mainnet Sepolia
+methods:
+ - name: alchemy_simulateUserOperationAssetChanges
+ description: |
+ Simulates userOperations and returns a list of asset changes.
+
+
+ The results provided by this method are based on the blockchain's state at the moment of simulation. Changes in the blockchain state, such as updates to contract variables or balances, can occur between the time of simulation and userOperation execution.
+
+ This could lead to different outcomes than predicted. For instance, if a userOperation's effect is conditional on the current state of a contract, and this state is altered before the transaction is executed, the final result may not match the simulation.
+
+ Please conside this potential variance when using the API.
+
+
+ params:
+ - name: UserOperation
+ required: true
+ description: userOperation to simulate. The signature can be a [dummy value](/docs/reference/bundler-faqs#what-is-a-dummy-signature); it doesn't need to be valid.
+ schema:
+ oneOf:
+ - $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV06"
+ - $ref: "../_shared_wallets/components.yaml#/components/schemas/UserOperationV07"
+ - name: entryPoint
+ required: true
+ description: "`EntryPoint` to use for the simulation. This MUST be one of the EntryPoints returned by the `supportedEntryPoints` RPC call."
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/address"
+ - name: blockNumber
+ required: false
+ description: Optional block number in context of which the simulation should be executed. If not specified, the current block number is used.
+ schema:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/uint"
+ result:
+ name: Simulation Result
+ description: Object containing simulated asset changes and any potential errors.
+ schema:
+ type: object
+ properties:
+ changes:
+ type: array
+ items:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/AssetChange"
+ error:
+ type: [object, "null"]
+ properties:
+ message:
+ type: string
+ revertReason:
+ $ref: "../_shared_wallets/components.yaml#/components/schemas/RevertMessage"
+ examples:
+ - name: alchemy_simulateUserOperationAssetChanges example
+ params:
+ - name: UserOperation
+ value:
+ sender: "0xceb161d3e0B6d01bc0e87ECC27fF9f2E2eCDCD81"
+ nonce: "0x3"
+ initCode: "0x"
+ callData: "0xb61d27f600000000000000000000000043f6bfbe9dad44cf0a60570c30c307d949be4cd40000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000645c833bfd000000000000000000000000613c64104b98b048b93289ed20aefd80912b3cde0000000000000000000000000000000000000000000000000de123e8a84f9901000000000000000000000000c9371ea30dea5ac745b71e191ba8cde2c4e66df500000000000000000000000000000000000000000000000000000000"
+ callGasLimit: "0x7A1200"
+ verificationGasLimit: "0x927C0"
+ preVerificationGas: "0x15F90"
+ maxFeePerGas: "0x656703D00"
+ maxPriorityFeePerGas: "0x13AB6680"
+ signature: "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"
+ paymasterAndData: "0x9db7f05b0eb93eb242b5913596dcfaada756af5c"
+ - name: entryPoint
+ value: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"
+ - name: blockNumber
+ value: "0x113CF6E"
diff --git a/src/openrpc/alchemy/wallet-api/wallet-api.yaml b/src/openrpc/alchemy/wallet-api/wallet-api.yaml
new file mode 100644
index 000000000..b0b0969b3
--- /dev/null
+++ b/src/openrpc/alchemy/wallet-api/wallet-api.yaml
@@ -0,0 +1,8906 @@
+# yaml-language-server: $schema=https://meta.open-rpc.org/
+
+$schema: https://meta.open-rpc.org/
+openrpc: 1.2.4
+info:
+ title: Alchemy Wallet APIs JSON-RPC spec
+ description: A specification of the standard JSON-RPC methods for the Alchemy Wallet APIs.
+ version: 0.0.0
+servers:
+ - url: https://api.g.alchemy.com/v2
+methods:
+ - name: wallet_requestAccount
+ description: This method is used to get the smart account address and relevant info for a given signer. If an account does not already exist for a given signer, this method will create one before returning the counterfactual address.
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ anyOf:
+ - type: "object"
+ required:
+ - "signerAddress"
+ properties:
+ signerAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ id:
+ type: "string"
+ format: "uuid"
+ errorMessage: "Must be a valid UUID v4 string (e.g., '3061cc5f-1f96-48a9-ab45-41faad2dd23b')"
+ creationHint:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ createAdditional:
+ description: "If true, will allow creating more than one smart wallet per signer."
+ errorMessage: "createAdditional must be true or omitted"
+ type: "boolean"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ properties:
+ accountType:
+ anyOf:
+ - description: "Modular Account V2 SMA-B account type"
+ enum:
+ - "sma-b"
+ - description: "Light Account V2 account type"
+ enum:
+ - "la-v2"
+ - description: "(deprecated - backwards compatibility only - reach out to support@alchemy.com with questions/concerns) Light Account V1.0.1 account type"
+ enum:
+ - "la-v1.0.1"
+ - description: "(deprecated - backwards compatibility only - reach out to support@alchemy.com with questions/concerns) Light Account V1.0.2 account type"
+ enum:
+ - "la-v1.0.2"
+ - description: "(deprecated - backwards compatibility only - reach out to support@alchemy.com with questions/concerns) Light Account V1.1.0 account type"
+ enum:
+ - "la-v1.1.0"
+ errorMessage: "accountType must be one of: 'sma-b', 'la-v2', 'la-v1.0.1', 'la-v1.0.2', 'la-v1.1.0', 'la-v2-multi-owner', or 'ma-v1-multi-owner'"
+ description: "Single owner account types"
+ - type: "object"
+ required:
+ - "accountType"
+ properties:
+ accountType:
+ anyOf:
+ - description: "Light Account V2 Multi-Owner account type"
+ enum:
+ - "la-v2-multi-owner"
+ - description: "(deprecated - backwards compatibility only - reach out to support@alchemy.com with questions/concerns) Modular Account V1 Multi-Owner account type"
+ enum:
+ - "ma-v1-multi-owner"
+ errorMessage: "accountType must be one of: 'sma-b', 'la-v2', 'la-v1.0.1', 'la-v1.0.2', 'la-v1.1.0', 'la-v2-multi-owner', 'ma-v1-multi-owner', or '7702'"
+ initialOwners:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The initial owners of the account. The signer address will be implicitly added if not specified in this list."
+ description: "Multi-owner account types"
+ description: "Smart contract account types"
+ - type: "object"
+ required:
+ - "accountType"
+ properties:
+ accountType:
+ errorMessage: "accountType must be '7702' for Smart EOA accounts"
+ enum:
+ - "7702"
+ description: "Smart EOA account type (EIP-7702)"
+ includeCounterfactualInfo:
+ type: "boolean"
+ default: false
+ description: "Using signer address"
+ - type: "object"
+ required:
+ - "signerPublicKey"
+ properties:
+ signerPublicKey:
+ allOf:
+ - type: "object"
+ required:
+ - "x"
+ - "y"
+ properties:
+ x:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ y:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "type"
+ properties:
+ type:
+ enum:
+ - "webauthn-p256"
+ errorMessage: "signerPublicKey must be a valid WebAuthn public key with type 'webauthn-p256'"
+ id:
+ type: "string"
+ format: "uuid"
+ errorMessage: "Must be a valid UUID v4 string (e.g., '3061cc5f-1f96-48a9-ab45-41faad2dd23b')"
+ creationHint:
+ allOf:
+ - type: "object"
+ properties:
+ createAdditional:
+ description: "If true, will allow creating more than one smart wallet per signer."
+ errorMessage: "createAdditional must be true or omitted"
+ type: "boolean"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ accountType:
+ description: "Modular Account V2 WebAuthn account type"
+ errorMessage: "accountType must be 'mav2-webauthn'"
+ enum:
+ - "mav2-webauthn"
+ includeCounterfactualInfo:
+ type: "boolean"
+ default: false
+ description: "Using WebAuthn signer public key"
+ - type: "object"
+ required:
+ - "accountAddress"
+ properties:
+ accountAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ includeCounterfactualInfo:
+ type: "boolean"
+ default: false
+ description: "Using smart contract account address"
+ errorMessage: "Must provide either 'signerAddress' (with optional 'id', 'creationHint', 'includeCounterfactualInfo') or 'accountAddress' (with optional 'includeCounterfactualInfo')"
+
+ result:
+ name: requestAccountResponse
+ schema:
+ type: "object"
+ required:
+ - "accountAddress"
+ - "id"
+ properties:
+ accountAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ id:
+ type: "string"
+ format: "uuid"
+ errorMessage: "Must be a valid UUID v4 string (e.g., '3061cc5f-1f96-48a9-ab45-41faad2dd23b')"
+ counterfactualInfo:
+ type: "object"
+ required:
+ - "factoryType"
+ - "factoryAddress"
+ - "factoryData"
+ properties:
+ factoryType:
+ anyOf:
+ - description: "Light Account v1.0.1"
+ enum:
+ - "LightAccountV1.0.1"
+ - description: "Light Account v1.0.2"
+ enum:
+ - "LightAccountV1.0.2"
+ - description: "Light Account v1.1.0"
+ enum:
+ - "LightAccountV1.1.0"
+ - description: "Light Account v2.0.0"
+ enum:
+ - "LightAccountV2.0.0"
+ - description: "Light Account v2.0.0 MultiOwner"
+ enum:
+ - "LightAccountV2.0.0-MultiOwner"
+ - description: "Modular Account v1.0.0 MultiOwner"
+ enum:
+ - "MAv1.0.0-MultiOwner"
+ - description: "Modular Account v1.0.0 MultiSig"
+ enum:
+ - "MAv1.0.0-MultiSig"
+ - description: "Modular Account v2.0.0 SMA-B"
+ enum:
+ - "MAv2.0.0-sma-b"
+ - description: "Modular Account v2.0.0 SSV"
+ enum:
+ - "MAv2.0.0-ma-ssv"
+ - description: "Modular Account v2.0.0 WebAuthn"
+ enum:
+ - "MAv2.0.0-ma-webauthn"
+ - description: "Unknown factory type"
+ enum:
+ - "unknown"
+ factoryAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ delegation:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+
+ examples:
+ - name: "wallet_requestAccount example"
+ params:
+ - name: "param0"
+ value:
+ signerAddress: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ result:
+ name: "Response"
+ value:
+ accountAddress: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ id: "3061cc5f-1f96-48a9-ab45-41faad2dd23b"
+
+ - name: wallet_prepareCalls
+ description: This method is used to prepare a user operation for submission. It will return a built user operation and a signature request which needs to be signed by the user before submitting to wallet_sendPreparedCalls
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ type: "object"
+ required:
+ - "calls"
+ - "from"
+ - "chainId"
+ properties:
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ paymasterPermitSignature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+
+ result:
+ name: prepareCallsResponse
+ schema:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "array"
+ data:
+ type: "array"
+ items:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.6.0)"
+ enum:
+ - "user-operation-v060"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "initCode"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ - "paymasterAndData"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ initCode:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterAndData:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ description: "Unsigned User Operation (Entrypoint v0.6.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.6.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.7.0)"
+ enum:
+ - "user-operation-v070"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ factory:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymaster:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ paymasterData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterVerificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterPostOpGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Unsigned User Operation (Entrypoint v0.7.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.7.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "signatureRequest"
+ properties:
+ type:
+ description: "EIP-7702 Authorization Request"
+ enum:
+ - "authorization"
+ data:
+ type: "object"
+ required:
+ - "address"
+ - "nonce"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ type: "object"
+ required:
+ - "type"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eip7702Auth"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Prepared EIP-7702 Authorization"
+ description: "Array of prepared calls"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.6.0)"
+ enum:
+ - "user-operation-v060"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "initCode"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ - "paymasterAndData"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ initCode:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterAndData:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ description: "Unsigned User Operation (Entrypoint v0.6.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.6.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.7.0)"
+ enum:
+ - "user-operation-v070"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ factory:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymaster:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ paymasterData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterVerificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterPostOpGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Unsigned User Operation (Entrypoint v0.7.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.7.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "modifiedRequest"
+ - "data"
+ - "signatureRequest"
+ properties:
+ type:
+ description: "ERC-7597 Permit Signature Request for the paymaster"
+ enum:
+ - "paymaster-permit"
+ modifiedRequest:
+ type: "object"
+ required:
+ - "calls"
+ - "from"
+ - "chainId"
+ properties:
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ paymasterPermitSignature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ signatureRequest:
+ type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ description: "Prepared ERC-7597 Permit Signature Request for the paymaster"
+ - type: "object"
+ properties:
+ details:
+ type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "user-operation"
+ data:
+ type: "object"
+ required:
+ - "hash"
+ - "calls"
+ properties:
+ hash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+
+ examples:
+ - name: "wallet_prepareCalls example"
+ params:
+ - name: "param0"
+ value:
+ calls:
+ - to: "0x1234567890123456789012345678901234567890"
+ data: "0x"
+ from: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ chainId: "0x01"
+ capabilities:
+ paymasterService:
+ policyId: "11111111-2222-3333-4444-555555555555"
+ result:
+ name: "Response"
+ value:
+ type: "user-operation-v070"
+ data:
+ sender: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ nonce: "0x10000000000000000"
+ callData: "0x34fcd5be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
+ paymaster: "0x3f222Df6aB18C1E10d0Ec136503c3B0dfd929048"
+ paymasterData: "0x0000000000000000682d7e7c9fa6f71c28c31141a11ac9eb2bdbe898ddbd42a4f7085f8bb3380ba75dd9231a386e24fe5a84777217c907391e983b5b0f78a4a9d0fd1c8631dd22c10e106b461c"
+ paymasterPostOpGasLimit: "0x0"
+ paymasterVerificationGasLimit: "0x74d3"
+ maxPriorityFeePerGas: "0x60e4b0"
+ maxFeePerGas: "0x1bf52290"
+ callGasLimit: "0x2bb8"
+ verificationGasLimit: "0xc845"
+ preVerificationGas: "0x14b74"
+ chainId: "0x66eee"
+ signatureRequest:
+ type: "personal_sign"
+ data:
+ raw: "0xc69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ rawPayload: "0x2a8073568c8fad3edf15f70c3471a7fff18e1d57c650a501ab47f57a5c1f0e39"
+ feePayment:
+ sponsored: false
+ tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
+ maxAmount: "0x1f34"
+ details:
+ type: "user-operation"
+ data:
+ hash: "0xc69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ calls:
+ - to: "0x1234567890123456789012345678901234567890"
+ data: "0x"
+
+ - name: wallet_sendPreparedCalls
+ description: This method is used after signing the signatureRequest returned from prepareCalls to submit a user operation
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "array"
+ data:
+ type: "array"
+ items:
+ anyOf:
+ - allOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.6.0)"
+ enum:
+ - "user-operation-v060"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "initCode"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ - "paymasterAndData"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ initCode:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterAndData:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ description: "Unsigned User Operation (Entrypoint v0.6.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "signature"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "webauthn-p256"
+ data:
+ type: "object"
+ required:
+ - "signature"
+ - "metadata"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature r, s"
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature as a single hex value, packing r and s sequentially into a 64-byte value."
+ metadata:
+ type: "object"
+ required:
+ - "authenticatorData"
+ - "clientDataJSON"
+ properties:
+ authenticatorData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Authenticator data. Should be formatted as a hex string conforming to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/Authenticator_data."
+ clientDataJSON:
+ type: "string"
+ description: "Client data JSON. Should conform to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorResponse/clientDataJSON."
+ description: "WebAuthn signature metadata"
+ description: "Prepared User Operation (Entrypoint v0.6.0) with signature"
+ - allOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.7.0)"
+ enum:
+ - "user-operation-v070"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ factory:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymaster:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ paymasterData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterVerificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterPostOpGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Unsigned User Operation (Entrypoint v0.7.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "signature"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "webauthn-p256"
+ data:
+ type: "object"
+ required:
+ - "signature"
+ - "metadata"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature r, s"
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature as a single hex value, packing r and s sequentially into a 64-byte value."
+ metadata:
+ type: "object"
+ required:
+ - "authenticatorData"
+ - "clientDataJSON"
+ properties:
+ authenticatorData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Authenticator data. Should be formatted as a hex string conforming to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/Authenticator_data."
+ clientDataJSON:
+ type: "string"
+ description: "Client data JSON. Should conform to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorResponse/clientDataJSON."
+ description: "WebAuthn signature metadata"
+ description: "Prepared User Operation (Entrypoint v0.7.0) with signature"
+ - allOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ properties:
+ type:
+ description: "EIP-7702 Authorization Request"
+ enum:
+ - "authorization"
+ data:
+ type: "object"
+ required:
+ - "address"
+ - "nonce"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "signature"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ description: "Prepared EIP-7702 Authorization with signature"
+ description: "Array of prepared calls with signatures"
+ - allOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.6.0)"
+ enum:
+ - "user-operation-v060"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "initCode"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ - "paymasterAndData"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ initCode:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterAndData:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ description: "Unsigned User Operation (Entrypoint v0.6.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "signature"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "webauthn-p256"
+ data:
+ type: "object"
+ required:
+ - "signature"
+ - "metadata"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature r, s"
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature as a single hex value, packing r and s sequentially into a 64-byte value."
+ metadata:
+ type: "object"
+ required:
+ - "authenticatorData"
+ - "clientDataJSON"
+ properties:
+ authenticatorData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Authenticator data. Should be formatted as a hex string conforming to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/Authenticator_data."
+ clientDataJSON:
+ type: "string"
+ description: "Client data JSON. Should conform to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorResponse/clientDataJSON."
+ description: "WebAuthn signature metadata"
+ description: "Prepared User Operation (Entrypoint v0.6.0) with signature"
+ - allOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.7.0)"
+ enum:
+ - "user-operation-v070"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ factory:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymaster:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ paymasterData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterVerificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterPostOpGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Unsigned User Operation (Entrypoint v0.7.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "signature"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "webauthn-p256"
+ data:
+ type: "object"
+ required:
+ - "signature"
+ - "metadata"
+ properties:
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature r, s"
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Signature as a single hex value, packing r and s sequentially into a 64-byte value."
+ metadata:
+ type: "object"
+ required:
+ - "authenticatorData"
+ - "clientDataJSON"
+ properties:
+ authenticatorData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Authenticator data. Should be formatted as a hex string conforming to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/Authenticator_data."
+ clientDataJSON:
+ type: "string"
+ description: "Client data JSON. Should conform to WebAuthn specification for 'webauthn.get' requests. See example here: https://developer.mozilla.org/en-US/docs/Web/API/AuthenticatorResponse/clientDataJSON."
+ description: "WebAuthn signature metadata"
+ description: "Prepared User Operation (Entrypoint v0.7.0) with signature"
+ - type: "object"
+ properties:
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ callId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+
+ result:
+ name: sendPreparedCallsResponse
+ schema:
+ type: "object"
+ required:
+ - "preparedCallIds"
+ - "details"
+ properties:
+ preparedCallIds:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ details:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "user-operations"
+ data:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "callId"
+ - "hash"
+ properties:
+ callId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ hash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "user-operation"
+ data:
+ type: "object"
+ required:
+ - "hash"
+ properties:
+ hash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+
+ examples:
+ - name: "wallet_sendPreparedCalls example"
+ params:
+ - name: "param0"
+ value:
+ type: "user-operation-v070"
+ data:
+ sender: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ nonce: "0x10000000000000000"
+ callData: "0x34fcd5be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
+ paymaster: "0x3f222Df6aB18C1E10d0Ec136503c3B0dfd929048"
+ paymasterData: "0x0000000000000000682d7e7c9fa6f71c28c31141a11ac9eb2bdbe898ddbd42a4f7085f8bb3380ba75dd9231a386e24fe5a84777217c907391e983b5b0f78a4a9d0fd1c8631dd22c10e106b461c"
+ paymasterPostOpGasLimit: "0x0"
+ paymasterVerificationGasLimit: "0x74d3"
+ maxPriorityFeePerGas: "0x60e4b0"
+ maxFeePerGas: "0x1bf52290"
+ callGasLimit: "0x2bb8"
+ verificationGasLimit: "0xc845"
+ preVerificationGas: "0x14b74"
+ chainId: "0x66eee"
+ signature:
+ type: "secp256k1"
+ data: "0xb1a055089b1ba8387ed435f2bd0afe7ff69f22b928cdfdea1b5323c64d6af387164de3fa6febf031b544de46a84d6fb7f084b9798ddfaba820950c257139a7321b"
+ result:
+ name: "Response"
+ value:
+ preparedCallIds:
+ - "0x0000000000000000000000000000000000000000000000000000000000066eeec69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ details:
+ type: "user-operations"
+ data:
+ - callId: "0x0000000000000000000000000000000000000000000000000000000000066eeec69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ hash: "0xc69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ calls:
+ - to: "0x1234567890123456789012345678901234567890"
+ data: "0x"
+ value: "0x0"
+
+ - name: wallet_createSession
+ description: This method is used to create a session for a given address with specified permissions.
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ allOf:
+ - type: "object"
+ required:
+ - "chainId"
+ - "key"
+ - "permissions"
+ properties:
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ expirySec:
+ type: "integer"
+ maximum: 9999999999
+ description: "Unix timestamp in seconds (0 for no expiry)"
+ key:
+ type: "object"
+ required:
+ - "type"
+ - "publicKey"
+ properties:
+ type:
+ anyOf:
+ - description: "Secp256k1"
+ enum:
+ - "secp256k1"
+ - description: "ECDSA (alias for secp256k1)"
+ enum:
+ - "ecdsa"
+ - description: "Contract"
+ enum:
+ - "contract"
+ publicKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Note that this should be the public key's 20-byte address, not the full 64-byte public key"
+ permissions:
+ type: "array"
+ items:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "native-token-transfer"
+ data:
+ type: "object"
+ required:
+ - "allowance"
+ properties:
+ allowance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "allows transfer of native tokens from the account"
+ title: "native-token-transfer"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "erc20-token-transfer"
+ data:
+ type: "object"
+ required:
+ - "allowance"
+ - "address"
+ properties:
+ allowance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "allows transfer or approval of erc20 tokens from the account"
+ title: "erc20-token-transfer"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "gas-limit"
+ data:
+ type: "object"
+ required:
+ - "limit"
+ properties:
+ limit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "allows the key to spend gas for UOs"
+ title: "gas-limit"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "contract-access"
+ data:
+ type: "object"
+ required:
+ - "address"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "grants access to all functions in a contract"
+ title: "contract-access"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "account-functions"
+ data:
+ type: "object"
+ required:
+ - "functions"
+ properties:
+ functions:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ minItems: 1
+ description: "grants access to functions in the account"
+ title: "account-functions"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "functions-on-all-contracts"
+ data:
+ type: "object"
+ required:
+ - "functions"
+ properties:
+ functions:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ minItems: 1
+ description: "access to a function selector in any address or contract"
+ title: "functions-on-all-contracts"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "functions-on-contract"
+ data:
+ type: "object"
+ required:
+ - "address"
+ - "functions"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ functions:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ minItems: 1
+ description: "grants access to specified functions on a specific contract"
+ title: "functions-on-contract"
+ - type: "object"
+ required:
+ - "type"
+ properties:
+ type:
+ enum:
+ - "root"
+ description: "grants full access to everything"
+ title: "root"
+ minItems: 1
+ - type: "object"
+ required:
+ - "account"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+
+ result:
+ name: createSessionResponse
+ schema:
+ type: "object"
+ required:
+ - "sessionId"
+ - "chainId"
+ - "signatureRequest"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+
+ examples:
+ - name: "wallet_createSession example"
+ params:
+ - name: "param0"
+ value:
+ account: "0xafdABa1E09e82F780721963eba39bA9e6d9FE4d2"
+ chainId: "0x01"
+ key:
+ publicKey: "0x647bbf38CD0E116d1672405aE17a775572a84e03"
+ type: "secp256k1"
+ permissions:
+ - type: "root"
+ expirySec: 1747969653
+ result:
+ name: "Response"
+ value:
+ sessionId: "0xcd8b2d07a6fa76f1e8a5eaa8f3a7883c"
+ chainId: "0x1"
+ signatureRequest:
+ type: "eth_signTypedData_v4"
+ data:
+ domain:
+ chainId: 1
+ verifyingContract: "0xafdABa1E09e82F780721963eba39bA9e6d9FE4d2"
+ types:
+ DeferredAction:
+ - name: "nonce"
+ type: "uint256"
+ - name: "deadline"
+ type: "uint48"
+ - name: "call"
+ type: "bytes"
+ primaryType: "DeferredAction"
+ message:
+ nonce: "0x1030000000000000000"
+ deadline: 1747969653
+ call: "0x1bbf564c00000000000099DE0BF6fA90dEB851E2A2df7d83000000010500000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000001000000000000000000000000647bbf38cd0e116d1672405ae17a775572a84e0300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000007900000000000082B8e2012be914dFA4f62A0573eA0000000101000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000682fe675000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ rawPayload: "0x2a8073568c8fad3edf15f70c3471a7fff18e1d57c650a501ab47f57a5c1f0e39"
+
+ - name: wallet_formatSign
+ description: This method is used to format ECDSA signatures
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "signature"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ capabilities:
+ type: "object"
+ required:
+ - "permissions"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+
+ result:
+ name: formatSignResponse
+ schema:
+ type: "object"
+ required:
+ - "signature"
+ properties:
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+
+ examples:
+ - name: "wallet_formatSign example"
+ params:
+ - name: "param0"
+ value:
+ from: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ chainId: "0x01"
+ signature:
+ type: "ecdsa"
+ data: "0xb1a055089b1ba8387ed435f2bd0afe7ff69f22b928cdfdea1b5323c64d6af387164de3fa6febf031b544de46a84d6fb7f084b9798ddfaba820950c257139a7321b"
+ result:
+ name: "Response"
+ value:
+ signature: "0xb1a055089b1ba8387ed435f2bd0afe7ff69f22b928cdfdea1b5323c64d6af387164de3fa6febf031b544de46a84d6fb7f084b9798ddfaba820950c257139a7321b"
+
+ - name: wallet_getCallsStatus
+ description: This method is used to get the status of calls IDs returned from wallet_sendPreparedCalls.
+ params:
+ - name: callId
+ required: true
+ schema:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The call ID returned from wallet_sendPreparedCalls"
+ title: "callId"
+
+ result:
+ name: getCallsStatusResponse
+ schema:
+ type: "object"
+ required:
+ - "id"
+ - "chainId"
+ - "atomic"
+ - "status"
+ - "details"
+ properties:
+ id:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ atomic:
+ type: "boolean"
+ status:
+ anyOf:
+ - title: "Pending"
+ description: "Batch has been received by the wallet but has not completed execution onchain"
+ enum:
+ - 100
+ - title: "Preconfirmed"
+ description: "Batch has been preconfirmed onchain"
+ enum:
+ - 110
+ - title: "Preconfirmed Onchain Failure"
+ description: "Batch reverted *completely* in a preconfirmation and only changes related to gas charge may have been included onchain"
+ enum:
+ - 115
+ - title: "Preconfirmed Partial Onchain Failure"
+ description: "Batch reverted *partially* in a preconfirmation and some changes related to batch calls may have been included onchain"
+ enum:
+ - 116
+ - title: "Cross-Chain In Progress"
+ description: "Origin confirmed, destination pending"
+ enum:
+ - 120
+ - title: "Confirmed"
+ description: "Batch has been included onchain without reverts, receipts array contains info of all calls"
+ enum:
+ - 200
+ - title: "Offchain Failure"
+ description: "Batch has not been included onchain and wallet will not retry"
+ enum:
+ - 400
+ - title: "Cross-Chain Refunded"
+ description: "Cross-chain operation failed and was refunded"
+ enum:
+ - 410
+ - title: "Onchain Failure"
+ description: "Batch reverted *completely* and only changes related to gas charge may have been included onchain"
+ enum:
+ - 500
+ - title: "Partial Onchain Failure"
+ description: "Batch reverted *partially* and some changes related to batch calls may have been included onchain"
+ enum:
+ - 600
+ receipts:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "logs"
+ - "status"
+ - "blockHash"
+ - "blockNumber"
+ - "gasUsed"
+ - "transactionHash"
+ properties:
+ logs:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "address"
+ - "data"
+ - "topics"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ topics:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "0x1 for success, 0x0 for failure"
+ blockHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ blockNumber:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ gasUsed:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ transactionHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ details:
+ type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "user-operation"
+ data:
+ type: "object"
+ required:
+ - "hash"
+ properties:
+ hash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+
+ examples:
+ - name: "wallet_getCallsStatus example"
+ params:
+ - name: "param0"
+ value: "0x0000000000000000000000000000000000000000000000000000000000066eee1057eff9529142aa3b74adcaacfce5b99a09d80c9f2e38e35ea1f27535e61477"
+ result:
+ name: "Response"
+ value:
+ id: "0x0000000000000000000000000000000000000000000000000000000000066eee1057eff9529142aa3b74adcaacfce5b99a09d80c9f2e38e35ea1f27535e61477"
+ chainId: "0x66eee"
+ atomic: true
+ status: 200
+ details:
+ type: "user-operation"
+ data:
+ hash: "0x1057eff9529142aa3b74adcaacfce5b99a09d80c9f2e38e35ea1f27535e61477"
+ receipts:
+ - status: "0x1"
+ blockHash: "0x7c6f2f548c0ad4434dfb64f49a810bc795dadcd4cbec948bf8d4ae81a10fed5e"
+ blockNumber: "0x8ba5f79"
+ gasUsed: "0x33c65"
+ transactionHash: "0x8ec28074a461102e4dbe43cb4aa9fed3e8c1274f1f1b0d5f6a0919ce7e20f517"
+ logs:
+ - address: "0x00000000000017c61b5bee81050ec8efc9c6fecd"
+ data: "0x0000000000000000000000000000000000000000000000000000000000000000"
+ topics:
+ - "0xd32049610f0cd3babd266cf338d726cc8c34c8ff97356c0f33c13fa59962ac92"
+ - "0x000000000000000000000000f13223cca0ae9786dd91dffe1f4e834c71ccdc2d"
+ - "0x0000000000000000000000003f3503e682bf27330f9e5065043d47207e609e7d"
+ - address: "0x0000000071727de22e5e9d8baf0edac6f37da032"
+ data: "0x00000000000000000000000000000000000017c61b5bee81050ec8efc9c6fecd0000000000000000000000002cc0c7981d846b9f2a16276556f6e8cb52bfb633"
+ topics:
+ - "0xd51a9c61267aa6196961883ecf5ff2da6619c37dac0fa92122513fb32c032d2d"
+ - "0x1057eff9529142aa3b74adcaacfce5b99a09d80c9f2e38e35ea1f27535e61477"
+ - "0x000000000000000000000000f13223cca0ae9786dd91dffe1f4e834c71ccdc2d"
+ - address: "0x0000000071727de22e5e9d8baf0edac6f37da032"
+ data: "0x"
+ topics:
+ - "0xbb47ee3e183a558b1a2ff0874b079f3fc5478b7454eacf2bfc5af2ff5878f972"
+ - address: "0x0000000071727de22e5e9d8baf0edac6f37da032"
+ data: "0x00000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000014ba6c2cd4f0000000000000000000000000000000000000000000000000000000000003451d"
+ topics:
+ - "0x49628fd1471006c1482da88028e9ce4dbb080b815c9b0344d39e5a8e6ec1419f"
+ - "0x1057eff9529142aa3b74adcaacfce5b99a09d80c9f2e38e35ea1f27535e61477"
+ - "0x000000000000000000000000f13223cca0ae9786dd91dffe1f4e834c71ccdc2d"
+ - "0x0000000000000000000000002cc0c7981d846b9f2a16276556f6e8cb52bfb633"
+
+ - name: wallet_getCapabilities
+ description: This method is used to request capabilities from a wallet
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ anyOf:
+ - type: "array"
+ additionalItems: false
+ items:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ title: "Wallet Address"
+ minItems: 1
+ - type: "array"
+ additionalItems: false
+ items:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ title: "Wallet Address"
+ - type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ title: "Chain IDs"
+ minItems: 2
+ errorMessage: "Parameters must be either [walletAddress] or [walletAddress, chainIds]. Wallet address must be a valid hex address (e.g., '0xd46e8dd67c5d32be8058bb8eb970870f07244567'). Chain IDs must be an array of hex chain IDs (e.g., ['0x2105', '0x14A34'])"
+
+ result:
+ name: getCapabilitiesResponse
+ schema:
+ type: "object"
+ description: "Mapping from chain ID (hex) to capabilities"
+ additionalProperties:
+ type: "object"
+ additionalProperties: {}
+
+ examples:
+ - name: "wallet_getCapabilities example"
+ params:
+ - name: "param0"
+ value: "0xd46e8dd67c5d32be8058bb8eb970870f07244567"
+ - name: "param1"
+ value:
+ - "0x2105"
+ - "0x14A34"
+ result:
+ name: "Response"
+ value:
+ 0x0:
+ atomic:
+ status: "supported"
+ paymasterService:
+ supported: true
+ eip7702Auth:
+ supported: true
+
+ - name: wallet_getCrossChainStatus_v0
+ description: This method is used to get the status of a transaction (same-chain or cross-chain).
+ params:
+ - name: callId
+ required: true
+ schema:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The call ID returned from wallet_sendPreparedCalls"
+ title: "callId"
+
+ result:
+ name: getCrossChainStatusResponse
+ schema:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "id"
+ - "status"
+ properties:
+ type:
+ enum:
+ - "same-chain"
+ id:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ anyOf:
+ - title: "Pending"
+ description: "Batch has been received by the wallet but has not completed execution onchain"
+ enum:
+ - 100
+ - title: "Preconfirmed"
+ description: "Batch has been preconfirmed onchain"
+ enum:
+ - 110
+ - title: "Preconfirmed Onchain Failure"
+ description: "Batch reverted *completely* in a preconfirmation and only changes related to gas charge may have been included onchain"
+ enum:
+ - 115
+ - title: "Preconfirmed Partial Onchain Failure"
+ description: "Batch reverted *partially* in a preconfirmation and some changes related to batch calls may have been included onchain"
+ enum:
+ - 116
+ - title: "Cross-Chain In Progress"
+ description: "Origin confirmed, destination pending"
+ enum:
+ - 120
+ - title: "Confirmed"
+ description: "Batch has been included onchain without reverts, receipts array contains info of all calls"
+ enum:
+ - 200
+ - title: "Offchain Failure"
+ description: "Batch has not been included onchain and wallet will not retry"
+ enum:
+ - 400
+ - title: "Cross-Chain Refunded"
+ description: "Cross-chain operation failed and was refunded"
+ enum:
+ - 410
+ - title: "Onchain Failure"
+ description: "Batch reverted *completely* and only changes related to gas charge may have been included onchain"
+ enum:
+ - 500
+ - title: "Partial Onchain Failure"
+ description: "Batch reverted *partially* and some changes related to batch calls may have been included onchain"
+ enum:
+ - 600
+ originTransaction:
+ type: "object"
+ required:
+ - "chainId"
+ - "status"
+ properties:
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ anyOf:
+ - title: "Pending"
+ description: "Batch has been received by the wallet but has not completed execution onchain"
+ enum:
+ - 100
+ - title: "Preconfirmed"
+ description: "Batch has been preconfirmed onchain"
+ enum:
+ - 110
+ - title: "Preconfirmed Onchain Failure"
+ description: "Batch reverted *completely* in a preconfirmation and only changes related to gas charge may have been included onchain"
+ enum:
+ - 115
+ - title: "Preconfirmed Partial Onchain Failure"
+ description: "Batch reverted *partially* in a preconfirmation and some changes related to batch calls may have been included onchain"
+ enum:
+ - 116
+ - title: "Cross-Chain In Progress"
+ description: "Origin confirmed, destination pending"
+ enum:
+ - 120
+ - title: "Confirmed"
+ description: "Batch has been included onchain without reverts, receipts array contains info of all calls"
+ enum:
+ - 200
+ - title: "Offchain Failure"
+ description: "Batch has not been included onchain and wallet will not retry"
+ enum:
+ - 400
+ - title: "Cross-Chain Refunded"
+ description: "Cross-chain operation failed and was refunded"
+ enum:
+ - 410
+ - title: "Onchain Failure"
+ description: "Batch reverted *completely* and only changes related to gas charge may have been included onchain"
+ enum:
+ - 500
+ - title: "Partial Onchain Failure"
+ description: "Batch reverted *partially* and some changes related to batch calls may have been included onchain"
+ enum:
+ - 600
+ receipt:
+ type: "object"
+ required:
+ - "logs"
+ - "status"
+ - "blockHash"
+ - "blockNumber"
+ - "gasUsed"
+ - "transactionHash"
+ properties:
+ logs:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "address"
+ - "data"
+ - "topics"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ topics:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "0x1 for success, 0x0 for failure"
+ blockHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ blockNumber:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ gasUsed:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ transactionHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "type"
+ - "id"
+ - "status"
+ properties:
+ type:
+ enum:
+ - "cross-chain"
+ id:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ anyOf:
+ - title: "Pending"
+ description: "Batch has been received by the wallet but has not completed execution onchain"
+ enum:
+ - 100
+ - title: "Preconfirmed"
+ description: "Batch has been preconfirmed onchain"
+ enum:
+ - 110
+ - title: "Preconfirmed Onchain Failure"
+ description: "Batch reverted *completely* in a preconfirmation and only changes related to gas charge may have been included onchain"
+ enum:
+ - 115
+ - title: "Preconfirmed Partial Onchain Failure"
+ description: "Batch reverted *partially* in a preconfirmation and some changes related to batch calls may have been included onchain"
+ enum:
+ - 116
+ - title: "Cross-Chain In Progress"
+ description: "Origin confirmed, destination pending"
+ enum:
+ - 120
+ - title: "Confirmed"
+ description: "Batch has been included onchain without reverts, receipts array contains info of all calls"
+ enum:
+ - 200
+ - title: "Offchain Failure"
+ description: "Batch has not been included onchain and wallet will not retry"
+ enum:
+ - 400
+ - title: "Cross-Chain Refunded"
+ description: "Cross-chain operation failed and was refunded"
+ enum:
+ - 410
+ - title: "Onchain Failure"
+ description: "Batch reverted *completely* and only changes related to gas charge may have been included onchain"
+ enum:
+ - 500
+ - title: "Partial Onchain Failure"
+ description: "Batch reverted *partially* and some changes related to batch calls may have been included onchain"
+ enum:
+ - 600
+ originTransaction:
+ type: "object"
+ required:
+ - "chainId"
+ - "status"
+ properties:
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ anyOf:
+ - title: "Pending"
+ description: "Batch has been received by the wallet but has not completed execution onchain"
+ enum:
+ - 100
+ - title: "Preconfirmed"
+ description: "Batch has been preconfirmed onchain"
+ enum:
+ - 110
+ - title: "Preconfirmed Onchain Failure"
+ description: "Batch reverted *completely* in a preconfirmation and only changes related to gas charge may have been included onchain"
+ enum:
+ - 115
+ - title: "Preconfirmed Partial Onchain Failure"
+ description: "Batch reverted *partially* in a preconfirmation and some changes related to batch calls may have been included onchain"
+ enum:
+ - 116
+ - title: "Cross-Chain In Progress"
+ description: "Origin confirmed, destination pending"
+ enum:
+ - 120
+ - title: "Confirmed"
+ description: "Batch has been included onchain without reverts, receipts array contains info of all calls"
+ enum:
+ - 200
+ - title: "Offchain Failure"
+ description: "Batch has not been included onchain and wallet will not retry"
+ enum:
+ - 400
+ - title: "Cross-Chain Refunded"
+ description: "Cross-chain operation failed and was refunded"
+ enum:
+ - 410
+ - title: "Onchain Failure"
+ description: "Batch reverted *completely* and only changes related to gas charge may have been included onchain"
+ enum:
+ - 500
+ - title: "Partial Onchain Failure"
+ description: "Batch reverted *partially* and some changes related to batch calls may have been included onchain"
+ enum:
+ - 600
+ receipt:
+ type: "object"
+ required:
+ - "logs"
+ - "status"
+ - "blockHash"
+ - "blockNumber"
+ - "gasUsed"
+ - "transactionHash"
+ properties:
+ logs:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "address"
+ - "data"
+ - "topics"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ topics:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "0x1 for success, 0x0 for failure"
+ blockHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ blockNumber:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ gasUsed:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ transactionHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ destinationTransaction:
+ type: "object"
+ required:
+ - "chainId"
+ - "status"
+ properties:
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ anyOf:
+ - title: "Pending"
+ description: "Batch has been received by the wallet but has not completed execution onchain"
+ enum:
+ - 100
+ - title: "Preconfirmed"
+ description: "Batch has been preconfirmed onchain"
+ enum:
+ - 110
+ - title: "Preconfirmed Onchain Failure"
+ description: "Batch reverted *completely* in a preconfirmation and only changes related to gas charge may have been included onchain"
+ enum:
+ - 115
+ - title: "Preconfirmed Partial Onchain Failure"
+ description: "Batch reverted *partially* in a preconfirmation and some changes related to batch calls may have been included onchain"
+ enum:
+ - 116
+ - title: "Cross-Chain In Progress"
+ description: "Origin confirmed, destination pending"
+ enum:
+ - 120
+ - title: "Confirmed"
+ description: "Batch has been included onchain without reverts, receipts array contains info of all calls"
+ enum:
+ - 200
+ - title: "Offchain Failure"
+ description: "Batch has not been included onchain and wallet will not retry"
+ enum:
+ - 400
+ - title: "Cross-Chain Refunded"
+ description: "Cross-chain operation failed and was refunded"
+ enum:
+ - 410
+ - title: "Onchain Failure"
+ description: "Batch reverted *completely* and only changes related to gas charge may have been included onchain"
+ enum:
+ - 500
+ - title: "Partial Onchain Failure"
+ description: "Batch reverted *partially* and some changes related to batch calls may have been included onchain"
+ enum:
+ - 600
+ receipt:
+ type: "object"
+ required:
+ - "logs"
+ - "status"
+ - "blockHash"
+ - "blockNumber"
+ - "gasUsed"
+ - "transactionHash"
+ properties:
+ logs:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "address"
+ - "data"
+ - "topics"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ topics:
+ type: "array"
+ items:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ status:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "0x1 for success, 0x0 for failure"
+ blockHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ blockNumber:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ gasUsed:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ transactionHash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+
+ examples:
+ - name: "wallet_getCrossChainStatus_v0 example"
+ params:
+ - name: "param0"
+ value: "0x010100066d00682d7e7c9fa6f71c28c31141a11ac9eb2bdbe898ddbd42a4f7085f8bb3380ba75dd9231a386e24fe5a84777217c907391e983b5b0f78a4a9d0fd1c8631dd22c00000000000000000000000000000000000000000000000000000000000a4b1000000000000000000000000000000000000000000000000000000000000000102a8073568c8fad3edf15f70c3471a7fff18e1d57c650a501ab47f57a5c1f0e39"
+ result:
+ name: "Response"
+ value:
+ type: "cross-chain"
+ id: "0x010100066d00682d7e7c9fa6f71c28c31141a11ac9eb2bdbe898ddbd42a4f7085f8bb3380ba75dd9231a386e24fe5a84777217c907391e983b5b0f78a4a9d0fd1c8631dd22c00000000000000000000000000000000000000000000000000000000000a4b1000000000000000000000000000000000000000000000000000000000000000102a8073568c8fad3edf15f70c3471a7fff18e1d57c650a501ab47f57a5c1f0e39"
+ status: 120
+ originTransaction:
+ chainId: "0xa4b1"
+ status: 200
+ receipt:
+ status: "0x1"
+ blockHash: "0x7c6f2f548c0ad4434dfb64f49a810bc795dadcd4cbec948bf8d4ae81a10fed5e"
+ blockNumber: "0x8ba5f79"
+ gasUsed: "0x33c65"
+ transactionHash: "0x8ec28074a461102e4dbe43cb4aa9fed3e8c1274f1f1b0d5f6a0919ce7e20f517"
+ logs: []
+ destinationTransaction:
+ chainId: "0x1"
+ status: 100
+
+ - name: wallet_listAccounts
+ description: This method is used to list all smart accounts for a given signer.
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ allOf:
+ - type: "object"
+ properties:
+ limit:
+ type: "number"
+ minimum: 1
+ maximum: 100
+ default: 100
+ after:
+ type: "string"
+ format: "base64url"
+ - anyOf:
+ - type: "object"
+ required:
+ - "signerAddress"
+ properties:
+ signerAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ - type: "object"
+ required:
+ - "signerPublicKey"
+ properties:
+ signerPublicKey:
+ allOf:
+ - type: "object"
+ required:
+ - "x"
+ - "y"
+ properties:
+ x:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ y:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "type"
+ properties:
+ type:
+ enum:
+ - "webauthn-p256"
+ errorMessage: "signerPublicKey must be a valid WebAuthn public key with type 'webauthn-p256'"
+ - errorMessage: "Must provide either 'signerAddress' or 'signerPublicKey'"
+
+ result:
+ name: listAccountsResponse
+ schema:
+ type: "object"
+ required:
+ - "accounts"
+ - "meta"
+ properties:
+ accounts:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "accountAddress"
+ - "id"
+ properties:
+ accountAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ id:
+ type: "string"
+ format: "uuid"
+ errorMessage: "Must be a valid UUID v4 string (e.g., '3061cc5f-1f96-48a9-ab45-41faad2dd23b')"
+ meta:
+ type: "object"
+ required:
+ - "totalCount"
+ - "after"
+ properties:
+ totalCount:
+ type: "integer"
+ after:
+ anyOf:
+ - type: "string"
+ format: "base64url"
+ - type: "null"
+
+ examples:
+ - name: "wallet_listAccounts example"
+ params:
+ - name: "param0"
+ value:
+ signerAddress: "0x6275B53E98D07c729108A177207634eA22F5A748"
+ result:
+ name: "Response"
+ value:
+ accounts:
+ - accountAddress: "0xafdABa1E09e82F780721963eba39bA9e6d9FE4d2"
+ id: "3d8b3315-4aa1-4c67-8c82-20221e8dbf16"
+ meta:
+ totalCount: 1
+ after: null
+
+ - name: wallet_prepareSign
+ description: This method is used to translate raw data into a signature request
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "signatureRequest"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ description: "SignableMessage for personal_sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ description: "TypedDataDefinition for eth_signTypedData_v4"
+ capabilities:
+ type: "object"
+ required:
+ - "permissions"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+
+ result:
+ name: prepareSignResponse
+ schema:
+ type: "object"
+ required:
+ - "chainId"
+ - "signatureRequest"
+ properties:
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ description: "SignableMessage for personal_sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ description: "TypedDataDefinition for eth_signTypedData_v4"
+
+ examples:
+ - name: "wallet_prepareSign example"
+ params:
+ - name: "param0"
+ value:
+ from: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ chainId: "0x01"
+ signatureRequest:
+ type: "personal_sign"
+ data:
+ raw: "0x48656c6c6f20576f726c6421"
+ result:
+ name: "Response"
+ value:
+ chainId: "0x66eee"
+ signatureRequest:
+ type: "eth_signTypedData_v4"
+ data:
+ domain:
+ chainId: 421614
+ verifyingContract: "0xafdABa1E09e82F780721963eba39bA9e6d9FE4d2"
+ types:
+ ReplaySafeHash:
+ - name: "hash"
+ type: "bytes32"
+ primaryType: "ReplaySafeHash"
+ message:
+ hash: "0xec3608877ecbf8084c29896b7eab2a368b2b3c8d003288584d145613dfa4706c"
+
+ - name: wallet_requestQuote_v0
+ description: This method is used to request swap quotes
+ params:
+ - name: params[0]
+ required: true
+ schema:
+ anyOf:
+ - anyOf:
+ - anyOf:
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ postCalls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ returnRawCalls:
+ description: "Must be 'false' or undefined"
+ type: "boolean"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ - type: "object"
+ required:
+ - "fromAmount"
+ properties:
+ fromAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify exact 'from' amount"
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ postCalls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ returnRawCalls:
+ description: "Must be 'false' or undefined"
+ type: "boolean"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ - type: "object"
+ required:
+ - "minimumToAmount"
+ properties:
+ minimumToAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify minimum 'to' amount"
+ description: "Smart wallet execution"
+ - anyOf:
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ postCalls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "returnRawCalls"
+ properties:
+ returnRawCalls:
+ description: "Must be 'true'"
+ type: "boolean"
+ - type: "object"
+ required:
+ - "fromAmount"
+ properties:
+ fromAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify exact 'from' amount"
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ postCalls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "returnRawCalls"
+ properties:
+ returnRawCalls:
+ description: "Must be 'true'"
+ type: "boolean"
+ - type: "object"
+ required:
+ - "minimumToAmount"
+ properties:
+ minimumToAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify minimum 'to' amount"
+ description: "EOA execution (raw calls)"
+ description: "Same-chain swap"
+ - anyOf:
+ - anyOf:
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "toChainId"
+ properties:
+ toChainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ returnRawCalls:
+ description: "Must be 'false' or undefined"
+ type: "boolean"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ - type: "object"
+ required:
+ - "fromAmount"
+ properties:
+ fromAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify exact 'from' amount"
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "toChainId"
+ properties:
+ toChainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ properties:
+ returnRawCalls:
+ description: "Must be 'false' or undefined"
+ type: "boolean"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ - type: "object"
+ required:
+ - "minimumToAmount"
+ properties:
+ minimumToAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify minimum 'to' amount"
+ description: "Smart wallet execution"
+ - anyOf:
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "toChainId"
+ properties:
+ toChainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "returnRawCalls"
+ properties:
+ returnRawCalls:
+ description: "Must be 'true'"
+ type: "boolean"
+ - type: "object"
+ required:
+ - "fromAmount"
+ properties:
+ fromAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify exact 'from' amount"
+ - allOf:
+ - type: "object"
+ required:
+ - "from"
+ - "chainId"
+ - "fromToken"
+ - "toToken"
+ properties:
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ fromToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ toToken:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ slippage:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "toChainId"
+ properties:
+ toChainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - type: "object"
+ required:
+ - "returnRawCalls"
+ properties:
+ returnRawCalls:
+ description: "Must be 'true'"
+ type: "boolean"
+ - type: "object"
+ required:
+ - "minimumToAmount"
+ properties:
+ minimumToAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Specify minimum 'to' amount"
+ description: "EOA execution (raw calls)"
+ description: "Cross-chain swap"
+ description: "Swap options"
+
+ result:
+ name: requestQuoteResponse
+ schema:
+ allOf:
+ - type: "object"
+ required:
+ - "quote"
+ - "chainId"
+ properties:
+ quote:
+ type: "object"
+ required:
+ - "fromAmount"
+ - "minimumToAmount"
+ - "expiry"
+ properties:
+ fromAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ minimumToAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ expiry:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - allOf:
+ - type: "object"
+ required:
+ - "rawCalls"
+ properties:
+ rawCalls:
+ description: "Always 'false'"
+ type: "boolean"
+ - allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "array"
+ data:
+ type: "array"
+ items:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.6.0)"
+ enum:
+ - "user-operation-v060"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "initCode"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ - "paymasterAndData"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ initCode:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterAndData:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ description: "Unsigned User Operation (Entrypoint v0.6.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.6.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.7.0)"
+ enum:
+ - "user-operation-v070"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ factory:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymaster:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ paymasterData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterVerificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterPostOpGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Unsigned User Operation (Entrypoint v0.7.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.7.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "signatureRequest"
+ properties:
+ type:
+ description: "EIP-7702 Authorization Request"
+ enum:
+ - "authorization"
+ data:
+ type: "object"
+ required:
+ - "address"
+ - "nonce"
+ properties:
+ address:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ type: "object"
+ required:
+ - "type"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eip7702Auth"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Prepared EIP-7702 Authorization"
+ description: "Array of prepared calls"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.6.0)"
+ enum:
+ - "user-operation-v060"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "initCode"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ - "paymasterAndData"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ initCode:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterAndData:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Present"
+ - description: "Absent"
+ enum:
+ - "0x"
+ description: "Unsigned User Operation (Entrypoint v0.6.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.6.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "chainId"
+ - "feePayment"
+ properties:
+ type:
+ description: "User Operation (Entrypoint v0.7.0)"
+ enum:
+ - "user-operation-v070"
+ data:
+ type: "object"
+ required:
+ - "sender"
+ - "nonce"
+ - "callData"
+ - "callGasLimit"
+ - "verificationGasLimit"
+ - "preVerificationGas"
+ - "maxFeePerGas"
+ - "maxPriorityFeePerGas"
+ properties:
+ sender:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ factory:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ factoryData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ callGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ preVerificationGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ maxPriorityFeePerGas:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymaster:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ paymasterData:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterVerificationGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ paymasterPostOpGasLimit:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Unsigned User Operation (Entrypoint v0.7.0)"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signatureRequest:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "personal_sign"
+ data:
+ anyOf:
+ - type: "string"
+ minLength: 1
+ description: "Message"
+ - type: "object"
+ required:
+ - "raw"
+ properties:
+ raw:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw message"
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Personal sign"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ feePayment:
+ type: "object"
+ required:
+ - "sponsored"
+ - "tokenAddress"
+ - "maxAmount"
+ properties:
+ sponsored:
+ type: "boolean"
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ maxAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared User Operation (Entrypoint v0.7.0)"
+ - type: "object"
+ required:
+ - "type"
+ - "modifiedRequest"
+ - "data"
+ - "signatureRequest"
+ properties:
+ type:
+ description: "ERC-7597 Permit Signature Request for the paymaster"
+ enum:
+ - "paymaster-permit"
+ modifiedRequest:
+ type: "object"
+ required:
+ - "calls"
+ - "from"
+ - "chainId"
+ properties:
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ from:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ chainId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ capabilities:
+ type: "object"
+ properties:
+ permissions:
+ anyOf:
+ - type: "object"
+ required:
+ - "context"
+ properties:
+ context:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Permissions context"
+ - type: "object"
+ required:
+ - "sessionId"
+ - "signature"
+ properties:
+ sessionId:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ signature:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Remote permission"
+ paymasterService:
+ allOf:
+ - anyOf:
+ - type: "object"
+ required:
+ - "policyId"
+ properties:
+ policyId:
+ type: "string"
+ description: "The policy ID to use"
+ description: "Single policy ID"
+ - type: "object"
+ required:
+ - "policyIds"
+ properties:
+ policyIds:
+ type: "array"
+ items:
+ type: "string"
+ description: "The policy IDs to use"
+ description: "Multiple policy IDs"
+ - type: "object"
+ properties:
+ onlyEstimation:
+ type: "boolean"
+ description: "If true, will only estimate fees. Skips signature request and gas sponsorship"
+ erc20:
+ anyOf:
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "autoPermit"
+ properties:
+ autoPermit:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which a Permit signature will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to Permit to the paymaster for gas payment"
+ durationSeconds:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The duration of the Permit signature in seconds"
+ description: "Settings for automatically injecting a Permit signature for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for pre-op policies (autoPermit)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (autoPermit)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "preOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ preOpSettings:
+ type: "object"
+ required:
+ - "permitDetails"
+ properties:
+ permitDetails:
+ type: "object"
+ required:
+ - "deadline"
+ - "value"
+ properties:
+ deadline:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The deadline in the signed Permit object"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The value in the signed Permit object"
+ description: "Details of the returned Permit signature"
+ description: "ERC20 Paymaster Settings for pre-op policies (permitDetails)"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with preOpSettings (permitDetails)"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ - "postOpSettings"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ postOpSettings:
+ type: "object"
+ properties:
+ autoApprove:
+ type: "object"
+ required:
+ - "below"
+ - "amount"
+ properties:
+ below:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount below which an approval will be injected"
+ amount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The amount of tokens to approve to the paymaster for gas payment"
+ description: "Settings for automatically injecting an approval for ERC20 gas payment"
+ description: "ERC20 Paymaster Settings for post-op policies"
+ additionalProperties: false
+ description: "ERC20 Paymaster Settings with postOpSettings"
+ - type: "object"
+ required:
+ - "tokenAddress"
+ properties:
+ tokenAddress:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ description: "The address of the token to pay gas with"
+ maxTokenAmount:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "The maximum amount of tokens to allow during ERC20 gas payment"
+ additionalProperties: false
+ description: "No auto approval (requires manual ERC20 approvals to paymaster contract)"
+ description: "ERC20 Paymaster Settings"
+ errorMessage: "ERC20 settings must specify only one of: preOpSettings, postOpSettings, or neither (for manual approvals). Cannot specify both preOpSettings and postOpSettings"
+ webhookData:
+ type: "string"
+ description: "Additional data you can include in the request, such as proof of humanity, if you have enabled custom rules in your Gas Manager policy."
+ gasParamsOverride:
+ type: "object"
+ properties:
+ preVerificationGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ verificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ callGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterVerificationGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ paymasterPostOpGasLimit:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ maxPriorityFeePerGas:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Absolute"
+ - type: "object"
+ required:
+ - "multiplier"
+ properties:
+ multiplier:
+ type: "number"
+ description: "Multiplier"
+ minProperties: 1
+ eip7702Auth:
+ anyOf:
+ - type: "object"
+ required:
+ - "delegation"
+ properties:
+ account:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ delegation:
+ anyOf:
+ - enum:
+ - "ModularAccountV2"
+ - enum:
+ - "0x69007702764179f14F51cdce752f4f775d74E139"
+ description: "Specify EIP-7702 delegation"
+ - description: "Use default EIP-7702 delegation"
+ type: "boolean"
+ nonceOverride:
+ type: "object"
+ required:
+ - "nonceKey"
+ properties:
+ nonceKey:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Override nonce key as hex string"
+ stateOverride:
+ type: "object"
+ description: "Mapping from account address to overrides"
+ additionalProperties:
+ anyOf:
+ - allOf:
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ - anyOf:
+ - type: "object"
+ required:
+ - "state"
+ properties:
+ state:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Full account state"
+ - type: "object"
+ required:
+ - "stateDiff"
+ properties:
+ stateDiff:
+ type: "object"
+ additionalProperties:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account state diff"
+ description: "Account override with full state or state diff"
+ - type: "object"
+ properties:
+ balance:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ nonce:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ code:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Account override without state changes"
+ paymasterPermitSignature:
+ anyOf:
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "secp256k1"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "Secp256k1 signature"
+ - type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "ecdsa"
+ data:
+ anyOf:
+ - type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Hex-encoded signature"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "yParity"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ yParity:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, yParity"
+ - type: "object"
+ required:
+ - "r"
+ - "s"
+ - "v"
+ properties:
+ r:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ s:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ v:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "r, s, v"
+ description: "ECDSA signature (alias for secp256k1)"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ signatureRequest:
+ type: "object"
+ required:
+ - "type"
+ - "data"
+ - "rawPayload"
+ properties:
+ type:
+ enum:
+ - "eth_signTypedData_v4"
+ data:
+ type: "object"
+ required:
+ - "types"
+ - "primaryType"
+ - "message"
+ properties:
+ domain:
+ type: "object"
+ properties:
+ chainId:
+ type: "integer"
+ name:
+ type: "string"
+ salt:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ verifyingContract:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ version:
+ type: "string"
+ types:
+ type: "object"
+ description: "Mapping from type name to array of field definitions"
+ additionalProperties:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "name"
+ - "type"
+ properties:
+ name:
+ type: "string"
+ type:
+ type: "string"
+ primaryType:
+ type: "string"
+ message:
+ type: "object"
+ description: "Mapping from field name to value"
+ additionalProperties: {}
+ rawPayload:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw payload that can be signed if using a signer that supports signing raw bytes (such as an Alchemy Signer)"
+ description: "Typed data"
+ description: "Prepared ERC-7597 Permit Signature Request for the paymaster"
+ - type: "object"
+ properties:
+ details:
+ type: "object"
+ required:
+ - "type"
+ - "data"
+ properties:
+ type:
+ enum:
+ - "user-operation"
+ data:
+ type: "object"
+ required:
+ - "hash"
+ - "calls"
+ properties:
+ hash:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Prepared calls for smart wallet execution"
+ - type: "object"
+ required:
+ - "rawCalls"
+ - "calls"
+ properties:
+ rawCalls:
+ description: "Always 'true'"
+ type: "boolean"
+ calls:
+ type: "array"
+ items:
+ type: "object"
+ required:
+ - "to"
+ properties:
+ to:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid Ethereum address starting with '0x' (e.g., '0xa363219d7C0b8673df17529D469Db9eFF0f35D2A')"
+ data:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ value:
+ type: "string"
+ pattern: "^0x.*$"
+ errorMessage: "Must be a valid hex string starting with '0x'"
+ description: "Raw calls for externally owned account execution"
+
+ examples:
+ - name: "wallet_requestQuote_v0 example"
+ params:
+ - name: "param0"
+ value:
+ from: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ fromToken: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEee"
+ toToken: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
+ minimumToAmount: "0x5F5E100"
+ chainId: "0xa4b1"
+ capabilities:
+ paymasterService:
+ policyId: "11111111-2222-3333-4444-555555555555"
+ postCalls:
+ - to: "0x1234567890123456789012345678901234567890"
+ data: "0x"
+ result:
+ name: "Response"
+ value:
+ rawCalls: false
+ quote:
+ fromAmount: "0x123"
+ minimumToAmount: "0x456"
+ expiry: "0x1234567890"
+ type: "user-operation-v070"
+ data:
+ sender: "0xa363219d7C0b8673df17529D469Db9eFF0f35D2A"
+ nonce: "0x10000000000000000"
+ callData: "0x34fcd5be0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000001234567890123456789012345678901234567890000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
+ paymaster: "0x3f222Df6aB18C1E10d0Ec136503c3B0dfd929048"
+ paymasterData: "0x0000000000000000682d7e7c9fa6f71c28c31141a11ac9eb2bdbe898ddbd42a4f7085f8bb3380ba75dd9231a386e24fe5a84777217c907391e983b5b0f78a4a9d0fd1c8631dd22c10e106b461c"
+ paymasterPostOpGasLimit: "0x0"
+ paymasterVerificationGasLimit: "0x74d3"
+ maxPriorityFeePerGas: "0x60e4b0"
+ maxFeePerGas: "0x1bf52290"
+ callGasLimit: "0x2bb8"
+ verificationGasLimit: "0xc845"
+ preVerificationGas: "0x14b74"
+ chainId: "0x66eee"
+ signatureRequest:
+ type: "personal_sign"
+ data:
+ raw: "0xc69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ rawPayload: "0x2a8073568c8fad3edf15f70c3471a7fff18e1d57c650a501ab47f57a5c1f0e39"
+ feePayment:
+ sponsored: false
+ tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
+ maxAmount: "0x1f34"
+ details:
+ type: "user-operation"
+ data:
+ hash: "0xc69e468aa6fafb5c7298c02841e76f976b433018266af39a5807bc29ea9ad392"
+ calls:
+ - to: "0x1234567890123456789012345678901234567890"
+ data: "0x"