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 + ![Alt text](https://alchemyapi-res.cloudinary.com/image/upload/v{version}/docs/aa-sdk/images/your-image.png) + ``` + + Or for HTML: + + ```html + Description + ``` + +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 + ![Alt text](https://alchemyapi-res.cloudinary.com/image/upload/v{version}/docs/aa-sdk/images/your-image.png) + ``` + + Or using HTML: + + ```html + Description + ``` + +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). + +Audience Claim + +## 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** + + Email Mode 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)** + + Email Mode 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 + + + + + ); +} +``` + +## 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 ; +} +``` + +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. + +smart wallet transaction policies + +## 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. +
+ +
+ Alt text +
+
+ +#### 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. +
+ +
+ Alt text +
+
+ +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**. +
+ +
+ Alt text +
+
+ +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. +
+ +
+ Alt text +
+
+ +## 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 +--- + + + +smart wallets overview + + + + 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. +
+ +
+ Alt text +
+
+ +#### 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. +
+ +
+ Alt text +
+
+ +[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**. +
+ +
+ Alt text +
+
+ +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. +
+ +
+ Alt text +
+
+ +## 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 ( + + ); +} +``` 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 ( + + ); +} +``` + +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"}. + +
+ ) : ( + + )} +
+ ); +} +``` + +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. + +![preview-img](https://alchemyapi-res.cloudinary.com/image/upload/v1755789660/docs/Screenshot_2025-08-21_at_8.20.55_AM_g13fhu.png) + +> 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: + +![preview](https://alchemyapi-res.cloudinary.com/image/upload/v1755749524/docs/Screenshot_2025-08-20_at_9.11.59_PM_p4ah4z.png) + +## 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: + +![code-view](https://alchemyapi-res.cloudinary.com/image/upload/v1755790150/docs/Screenshot_2025-08-21_at_8.29.04_AM_twxtvi.png) + +Once you have signed in, you can mint your own NFT! 🎉 + +![mint-nft-view](https://alchemyapi-res.cloudinary.com/image/upload/v1755789956/docs/Screenshot_2025-08-21_at_8.25.51_AM_peoxem.png) + +## 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
Loading…
; + } + + return ( +
+
Email: {authMethods.email}
+ {authMethods.oauthProviders.map((oauthProvider) => ( +
+ {oauthProvider.providerName}: {oauthProvider.userDisplayName} +
+ ))} + {authMethods.passkeys.map((passkey) => ( +
+ 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 ( + <> + + + + ); +} +``` + +## 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 ; +} +``` + +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 ( + + ); +} +``` + +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 ( + + ); +} +``` + +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 ; +} +``` + +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 ; +} +``` + +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 ( +
+ {connectors.map((connector) => ( + + ))} +
+); +``` + +### 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) + + Auth0 provider list + +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. + + Configure Github auth provider settings Auth0 + +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 + + Auth0 app selection page + +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" + + Settings page in Auth0 with relevant fields + +10. Add these to your Smart Wallets dashboard in the embedded accounts auth config + + Copy fields from Auth0 to the Alchemy accounts 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 ( +
+ + + {qrCodeUrl && ( +
+

Scan this QR code with your authenticator app

+ +

+ After scanning, enter the 6-digit code from your authenticator app + to complete setup. +

+
+ )} + + {/* Display errors if they occur */} + {addMFA.isError && ( +
Error: {addMFA.error.message}
+ )} +
+ ); + } + ``` + + This QR code contains the information needed for apps like Google Authenticator or Authy. Once scanned, the app will generate 6-digit codes that users can use as their second verification step. + + ### Step 3: Confirming the Authenticator App Setup + + After the user scans the QR code, they need to prove it worked by entering a code: + + ```tsx twoslash + import React, { useState } from "react"; + import { useMFA } from "@account-kit/react"; + + function VerifyAuthenticatorComponent({ + multiFactorId, + }: { + multiFactorId: string; + }) { + const { verifyMFA } = useMFA(); + const [code, setCode] = useState(""); + + const handleVerifyAuthenticator = () => { + verifyMFA.mutate( + { + multiFactorId, + multiFactorCode: code, // The TOTP code from the user's authenticator app + }, + { + onSuccess: () => { + // Authenticator setup successful + console.log("MFA setup complete!"); + }, + onError: (error) => { + // Handle error + console.error("Verification failed:", error); + }, + }, + ); + }; + + // For async/await pattern, you can use mutateAsync + const handleVerifyAsync = async () => { + try { + const result = await verifyMFA.mutateAsync({ + multiFactorId, + multiFactorCode: code, + }); + console.log("MFA setup complete!", result); + } catch (error) { + console.error("Verification failed:", error); + } + }; + + return ( +
+ setCode(e.target.value)} + placeholder="Enter 6-digit code" + maxLength={6} + /> + + + {verifyMFA.isError && ( +
Invalid code. Please try again.
+ )} +
+ ); + } + ``` + + ### 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
Loading MFA settings...
; + + return ( +
+

Your Authentication Methods

+ + {factors.length === 0 ? ( +

No authenticator apps configured.

+ ) : ( +
    + {factors.map((factor) => ( +
  • + {factor.multiFactorType === "totp" + ? "Authenticator App" + : factor.multiFactorType} + +
  • + ))} +
+ )} + + {getMFAFactors.isError && ( +
+ Error loading MFA settings: {getMFAFactors.error.message} +
+ )} +
+ ); + } + ``` + + ## 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 ( + + ); +} +``` + + + 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 ; +} +``` + +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. + + +
+