# Architecture Overview
Source: https://docs.getpara.com/v2/concepts/architecture
An introduction to Para's architecture and core components
Para provides a robust and secure architecture for creating and managing universal embedded wallets across various blockchain
ecosystems. This overview introduces the key components and features of Para's design, setting the stage for a deeper
dive into its technical aspects.
## Core Concepts
Para's architecture leverages several key concepts:
1. **Multi-Party Computation (MPC) Key Management**: Para uses a 2-of-2 MPC system for secure key management, comprising
a User Share and a Cloud Key.
2. **Passkey**: This separate key leverages hardware secure enclaves while maintaining blockchain compatibility.
3. **Distributed Key Generation (DKG)**: Ensures that the full private key is never assembled in a single location.
4. **Passkeys and WebAuthn**: Implements the WebAuthn standard for enhanced security.
5. **Permissions Framework**: Allows granular control over transaction signing across multiple applications.
These components work together to provide a secure, flexible, and user-friendly wallet solution that can be embedded in
various applications across different platforms and blockchain ecosystems.
## Explore Para's Architecture
Dive deeper into specific aspects of Para's architecture and functionality:
Learn about Para's innovative approach to key management using MPC and hardware secure enclaves.
Explore the various security features that protect user assets and data in Para wallets.
Understand how Para enables secure wallet recovery in case of device loss or other issues.
Learn about Para's approach to universal embedded wallets and cross-application experiences.
Find answers to common technical questions about Para's architecture and functionality.
By leveraging these architectural components and features, Para provides a comprehensive solution for developers to
implement secure, user-friendly universal embedded wallets in their applications across various platforms and blockchain
ecosystems.
# Key Management System
Source: https://docs.getpara.com/v2/concepts/key-management
An in-depth look at Para's innovative approach to secure key management using MPC and hardware secure enclaves
Para's Key Management System forms the core of its security architecture, employing advanced cryptographic techniques to
safeguard user assets while ensuring usability across various platforms and blockchain ecosystems. At its heart is a
distributed (MPC) system that leverages (DKG) and distributed signing. This innovative approach ensures that user keys are never stored in a single
vulnerable location, and neither applications nor Para itself can access users' private keys, providing a robust
foundation for secure, non-custodial wallet management.
## Key Components
Para's key management system relies on a 2-of-2 MPC system comprised of three main components:
1. MPC Key 1: User Share
2. MPC Key 2: Para Share
3. Passkey
### User Share
The User Share is custodied by the user and acts like a hot wallet. It is accessible in the browser or on the user's
device, providing immediate control over assets while interacting with crypto applications.
### Cloud Share
The Cloud Share is managed by Para and stored securely in cloud hardware-security modules (HSMs). This setup provides a
secure off-device backup of the user's key, safeguarding the assets even in the event of device loss or compromise.
### Passkey
The Passkey is a unique feature of Para's system, designed to bridge the gap between device security capabilities and
blockchain requirements.
Most modern smartphones come with hardware secure enclaves, which are dedicated areas within the device's main processor
used for storing and protecting sensitive data. However, these enclaves primarily support the secp256r1 elliptic curve,
which differs from the secp256k1 curve used by most modern blockchains.
To address this, Para generates a separate Passkey. This key is used to authorize access to the Cloud Share, enabling
biometric authentication and signing on the secp256k1 curve. This process ensures users can leverage their device's
hardware security features while interacting seamlessly with blockchain networks.
## Key Generation and Management Process
1. **Distributed Key Generation**: When a user creates a wallet, Para initiates a DKG process. This generates the User
Share and Cloud Share without ever assembling the full private key in one place.
2. **Passkey Creation**: Simultaneously, an Passkey is generated and stored in the device's secure enclave.
3. **Cloud Share Storage**: The Cloud Share is securely stored in Para's HSMs.
4. **User Share Protection**: The User Share is protected by the user's authentication method (e.g., passkey,
biometrics) and stored securely on the device.
## Security Benefits
This key management system offers several security advantages:
* **No Single Point of Failure**: Since the private key is never fully assembled, there's no single point of
vulnerability.
* **Phishing Resistance**: Even if a user's email or social login is compromised, an attacker would still need physical
access to the user's device to initiate transactions.
* **Device Loss Protection**: If a user loses their device, they can still recover their wallet using the Cloud Share
and proper authentication.
* **Censorship Resistance**: Users have the option to export their Cloud Share, ensuring they maintain control over
their assets .
## Flexible Backup Mechanisms
Para supports flexible backup mechanisms and a key-based permissions system, allowing for customized security setups
based on specific application needs. These can be configured in the
By leveraging this advanced key management system, Para provides a secure, flexible, and user-friendly solution for
embedded wallets, balancing robust security with seamless user experience across various blockchain ecosystems.
# Permissions
Source: https://docs.getpara.com/v2/concepts/permissions
Fine-grained, enforceable control over what applications can do with a user's wallet
Para's permissions system gives developers fine-grained, enforceable control over what their application can do with a user's wallet, all while keeping user consent clear, understandable, and revocable.
Instead of asking users to grant blanket wallet access, Para enables **policy-based permissions** that are:
* Explicit
* Scoped
* Conditional
* Cryptographically enforced server-side
This allows teams to ship powerful wallet experiences without compromising on security or UX.
## Why Permissions Matter
Traditional wallet models force a tradeoff:
* Either prompt users for every action, or
* Grant broad signing power with little transparency
Para replaces this with a system where:
* Apps request only what they need
* Users clearly understand what they're approving
* Every action is checked against an approved policy before it is signed
The result is safer wallets, higher trust, and better conversion.
## Core Concepts
### Policy
A **policy** defines the full set of actions your app may ever request from a user's wallet.
Think of this as your app's permissions contract with users. If something is not included in the policy, it cannot happen.
Policies are:
* App-specific
* Explicit
* Immutable (changes require an updated policy version)
### Scopes
Policies are broken down into **scopes**, which are the user-facing consent items.
Each scope:
* Appears as a checkbox during onboarding or login
* Groups related actions together
* Can be required or optional
Example scopes:
* "Basic wallet actions" (required)
* "Automated transfers" (optional)
Scopes allow teams to progressively request access as users adopt more features.
### Permissions
Behind each scope are one or more **permissions** that describe the exact actions the app can perform, such as:
* Signing messages
* Transferring value
* Calling smart contracts
* Deploying contracts
Permissions are structured in JSON format, and are only activated if the user approves the parent scope they belong to.
### Conditions
Permissions can be constrained with **conditions** that define when an action is allowed.
Examples:
* Transfers only below a certain value
* Contract calls only to allow-listed addresses
* Actions limited to specific chains
Every request is evaluated against these conditions at runtime. Any permission that evaluates to **False** causes the transaction to be rejected.
## How To Configure Policies in Para
### Step 1: Define Your Policy
You start by declaring the full universe of actions your app may need:
* Supported blockchains
* Types of wallet actions
* Any global time bounds (optional)
This policy sets the outer boundary of what your app can ever do.
### Step 2: Define Scopes
Next, you decide how permissions are grouped and presented to users.
For each scope, you define:
* A short, user-friendly name
* A plain-language description
* Whether it is required or optional
Scopes should map cleanly to product features, not technical operations.
### Step 3: Attach Permissions to Scopes
Within each scope, you specify the concrete actions that scope enables:
* Message signing
* Transfers
* Contract interactions
* Deployment
Each permission is explicit and narrow by default.
### Step 4: Add Conditions (Optional)
You can further restrict permissions with conditions such as:
* Value limits
* Address allow-lists
* Function-level contract restrictions
If a request violates a condition, Para blocks it automatically.
## What Users Experience
1. The user sees clear, human-readable consent scopes
2. They approve or decline each scope
3. Para enforces only the approved permissions
4. Any out-of-policy action is rejected before signing
There is no silent permission creep.
## Summary
Para Permissions enable the following capabilities:
* You define intent once
* Users give informed consent
* Para enforces permissions every transaction
* Powerful wallet UX, without third-party control over keys
## Get Started
To learn more about permissions or get set up, get in touch with our team at [hello@getpara.com](mailto:hello@getpara.com)
# Wallet Recovery Process
Source: https://docs.getpara.com/v2/concepts/recovery
A guide to Para's secure wallet recovery mechanism
Para employs a robust recovery mechanism to ensure that wallet access remains resilient to events such as device loss or
phishing attempts. This document outlines the recovery process implemented in Para's infrastructure.
## How Para Enables Recovery
Para's recovery mechanism is designed to maintain user access to funds even if they lose access to their primary device.
This is achieved through a combination of recovery secrets, backup devices, and multi-factor authentication. The
recovery flow is managed through Para's web application, the Para Portal, eliminating the need for individual app
developers to implement their own recovery mechanisms.
When setting up a Para wallet, two key elements are generated:
1. **A unique recovery secret**: This is shared with the user client-side. The application has the option to store this secret. It's important to note that this secret is not related to the 2-of-2 MPC scheme and is solely used for restoring wallet access in case of device loss or theft. Para does not have access to this secret.
2. **A copy of the Cloud Share**: This is shared with the user in the Para Backup Kit. The Cloud Share is part of Para's two-key scheme, providing strong censorship resistance and protection against downtime.
In the event of a lost or stolen device, recovery is possible through two main methods: 1. **Keychain backup**: If
enabled, the key (k1) from the old device can be recovered on a new device. Using this key and the recovery secret,
the user can add a new Passkey (r1) to Para's allow list. 2. **Recovery secret**: If the user has their recovery
secret, they can initiate a recovery attempt via the Para Portal.
After successfully adding a new Passkey, Para prompts the user to perform a key rotation. This process generates an
entirely new set of keys, enhancing the security of the user's account and protecting against potential unauthorized
access to the old keys.
Users have the option to add backup devices (such as a laptop or smartwatch) during the wallet setup process. If a
primary device is lost, the user can log in from one of these backup devices. From there, they can add new devices and
remove lost ones, ensuring uninterrupted access to their wallet.
If the user cannot recover their User Share from their KeyChain backup or a secondary device, they can initiate a key rotation using the recovery key. This process includes:
1. Two-factor authentication (2FA) with their phone number or another multi-factor authentication (MFA) method.
2. This additional step ensures the integrity of the recovery process and protects against impersonation attempts.
## Security Measures
The recovery process requires multiple forms of verification, including the recovery secret and optional 2FA,
enhancing security against unauthorized access attempts.
After recovery, a full key rotation ensures that potentially compromised keys are invalidated, providing an
additional layer of security.
The option to add multiple backup devices provides redundancy and increases the likelihood of successful recovery in
various scenarios.
## Best Practices for Users
Store the recovery secret in a secure, offline location. Never share this secret with anyone, including Para.
Activate two-factor authentication for an additional layer of security during the recovery process.
Add multiple backup devices when possible to increase recovery options.
Periodically verify the ability to access the account from backup devices to ensure they remain functional.
By implementing this comprehensive recovery process, Para ensures that users have a secure and reliable method to regain
access to their wallets, balancing strong security measures with user-friendly processes.
# Security Mechanisms
Source: https://docs.getpara.com/v2/concepts/security
An in-depth exploration of Para's robust security features designed to protect user assets and data
Para employs a multi-layered approach to security, incorporating various mechanisms to protect user assets and data.
This document outlines the key security features implemented in Para's architecture.
## Multi-Party Computation (MPC)
At the core of Para's security is its use of Multi-Party Computation ([MPC](https://blog.getpara.com/what-is-mpc/)) for key management. MPC enhances security by:
* Preventing the entire private key from being in one location
* Eliminating single points of failure
* Enabling secure key generation and transaction signing without exposing the full private key
## Hardware Secure Enclaves
Para leverages hardware secure enclaves available in modern devices for additional security. These enclaves offer:
* A dedicated, isolated environment for sensitive operations
* Hardware-level protection for cryptographic keys
* Secure biometric authentication capabilities
## Passkeys and WebAuthn
Instead of traditional password-based authentication, Para implements the WebAuthn standard to create passkeys. This
approach:
* Eliminates risks associated with password-based authentication
* Leverages device-specific security features
* Provides phishing-resistant authentication
## Distributed Key Generation (DKG)
Para uses Distributed Key Generation to create key shares without ever assembling the full private key. DKG:
* Ensures no single party has access to the complete private key
* Provides protection against key theft during the generation process
* Allows for secure key refresh and rotation
## Permissions Framework
Para implements a sophisticated permissions system to control transaction signing across multiple applications. This
framework:
* Allows granular control over what actions applications can perform
* Mitigates risks associated with compromised applications
* Enables users to manage their wallet's exposure across different apps
## Two-Factor Authentication (2FA)
Para supports 2FA for additional account security, particularly during the wallet recovery process. The 2FA
implementation:
* Is an optional feature that can be enabled by users
* Utilizes time-based one-time passwords (TOTP)
* Adds an extra layer of security for critical operations
## Secure Backup and Recovery
Para provides robust mechanisms for wallet backup and recovery, including:
* Recovery secrets generated during wallet setup
* Support for multiple backup devices
* 48-hour delay for recovery attempts to prevent unauthorized access
* Para Backup Kit for censorship resistance
## Encryption and Secure Communication
All communication between the user's device, Para's servers, and connected applications is encrypted. This includes:
* Use of TLS for all network communications
* End-to-end encryption for sensitive data
* Secure storage of user data with encryption at rest
## Regular Security Audits
Para is committed to maintaining the highest security standards through regular third-party audits. The audit process
includes:
* Periodic audits by reputable security firms
* Comprehensive review of cryptographic implementations
* Continuous monitoring and improvement of security measures
## Censorship Resistance
Para's design ensures that [users maintain control over their assets](https://blog.getpara.com/censorship-resistance-why-its-critical-and-how-were-tackling-it/) even in the event of service disruptions. Measures
include:
* Option for users to export their Cloud Share
* Ability to sign transactions independently if Para services are unavailable
* Decentralized nature of key management prevents single points of failure
By implementing these comprehensive security mechanisms, Para provides a robust framework for protecting user assets and
data. This multi-layered approach ensures that Para-powered wallets remain secure against a wide range of potential
threats while maintaining a seamless user experience.
# Technical FAQ
Source: https://docs.getpara.com/v2/concepts/technical-faq
Frequently asked technical questions about implementing and using Para for developers
As you integrate Para into your applications, you may encounter various technical questions. This FAQ aims to address
the most common inquiries from developers, covering aspects of implementation, security, architecture, and integration.
If you don't find the answer you're looking for, our comprehensive documentation and support team are available to
assist you further.
Para uses the DKLS19 MPC algorithm and leverages an for core functions like distributed key generation, signing ceremonies, and non-custodial wallet generation. This ensures a robust and auditable foundation for Para's key management system.
Para uses the EIP712-specified transaction signature interface. Additionally, Para publishes an EIP-1193 Provider,
which is most commonly used via the Wagmi Connector. This ensures compatibility with a wide range of Ethereum-based
applications and tools.
Para uses sessions as a security measure when signing transactions. By default, session length is 90 minutes.
Developers can implement session refresh logic in their applications to maintain longer sessions if required.
While Para primarily uses MPC for key management, it is designed to work with ERC-4337 (Account Abstraction) out of
the box. This allows developers to leverage Para's security features while also taking advantage of AA capabilities if
desired.
As long as the Cloud Share sent during onboarding is not deleted by the user, they can always refresh keys, export, or
sign transactions independently. This design ensures that Para cannot censor transactions and provides a robust
fallback mechanism. Read our blog post on [censorship resistance](https://blog.getpara.com/censorship-resistance-why-its-critical-and-how-were-tackling-it/) for more details.
Para offers SDKs for: - TypeScript/React for web developers - React Native for mobile developers - Flutter for
cross-platform mobile development These SDKs provide a consistent interface for wallet management and transaction
signing across different platforms.
The biometric key is stored on-device in a secure enclave. For Ethereum-based transactions, Para uses secp256k1 curve
signatures. However, the secure enclave supports the secp256r1 curve. Para generates a secp256r1 key, which is used to
authorize a secp256k1 curve signature for ECDSA signatures, bridging this compatibility gap securely.
All UI elements and copy in the Para flow are fully configurable. Developers can whitelabel the product to match their
application's look and feel. While Para aims for a somewhat consistent user experience, copy, colors, and sizes are
fully customizable. Refer to the for detailed configuration options.
Para supports sign-in via Google, Apple, Twitter/X, Discord, and Facebook. This allows developers to offer a range of
authentication options to their users, potentially increasing adoption and ease of use.
Para's multi-app architecture allows the same wallet to be used across different applications while maintaining
security. It uses a permissions scheme that specifies what types of transactions an application can perform and which
ones require user approval. This is implemented through encrypted User Shares specific to each application and an
allow-list mechanism managed by Para.
Para implements a robust recovery mechanism involving: 1. A recovery secret generated during wallet setup 2. Option to
add backup devices 3. Two-factor authentication for additional security 4. A key rotation process after recovery to
ensure security The recovery process is managed through the Para Portal, reducing the implementation burden on
individual developers.
Para's architecture is designed to be blockchain-agnostic. It offers native support for: - All EVM-compatible chains -
Cosmos ecosystem - Solana Developers can integrate Para with popular libraries like ethers.js, viem, and CosmJS for
interacting with these networks. For full chain support please see our [chain support documentation](https://docs.getpara.com/v2/react/guides/custom-chains#additional-chains).
Universal embedded wallets in Para is achieved through:
1. Associating wallets with user identities (typically email addresses) rather than individual applications
2. Using MPC for secure key sharing across applications
3. Implementing a permissions framework for granular access control
4. Providing seamless authentication when users access new applications
Developers can leverage these features through Para's SDKs and APIs to enable users to access their wallets across different applications seamlessly.
Yes, users can export their private keys with Para. While we don't see many users export their private keys given Para wallets are universal and usable across apps and chains, users are able to do so in [Para Connect](https://connect.getpara.com/).
Multi-sigs require separate private keys to approve a transaction. MPC splits a single private key across multiple parties and parites jointly sign without ever reconstructing the key.
For further technical details or questions not covered here, please refer to our documentation or reach out to our
developer support team at .
# Universal Embedded Wallets
Source: https://docs.getpara.com/v2/concepts/universal-embedded-wallets
Exploring Para's approach to universal embedded wallets: portability and seamlessly cross-app
Para [universal embedded wallets](https://blog.getpara.com/universal-embedded-wallets/) allow users to seamlessly use their wallet across different applications and platforms, providing a more fluid and
user-friendly experience in the crypto ecosystem.
## Overview
In the crypto ecosystem, users often need to access the same wallet across various applications. However, this
convenience can pose security risks if not managed properly. Para's multi-app architecture addresses these challenges by
implementing a sophisticated permissions scheme that balances accessibility with security.
Universal embedded wallet functionality is not about transferring assets between wallets, but rather accessing the same wallet from
different applications and platforms.
## Key Components
Para's approach to universal embedded wallet portability leverages its unique architecture and security features:
Para associates wallets with user identities (typically email addresses) rather than individual applications
A granular permissions system ensures that applications only have the access they need, enhancing security in a
multi-app context
Users can log in to new applications using their Para credentials, automatically gaining access to their existing
universal embedded wallet
Para's MPC-based key management allows for secure key sharing across applications without exposing the full private key.
This implementation ensures that users can easily and securely use their Para universal embedded wallets across multiple applications while
maintaining strong security and privacy controls.
## Benefits
1. * Access the same wallet across multiple applications without complex key exports
2. * No need to manage multiple wallets or perform
3. * Consistent user experience across different platforms
4. * Permissions for transparent security and granular access confrol
1. * Easier onboarding of users who already have a Para universal embedded wallet
2. * Access to richer transaction history and liquidity from shared wallets
3. * Easily craft multi-app experiences and build an ecosystem
4. * request specific permissions based on the application's needs.
## Universal Embedded Wallets vs. Traditional Approaches
To understand the advantages of Para's wallet portability, let's compare it with traditional approaches:
| Feature | Third-Party Wallets | Traditional Embedded Wallets | Universal Embedded Wallets |
| ---------------------------------- | :-----------------: | :--------------------------: | :------------------------: |
| Portable across apps | ✔️ | | ✔️ |
| Smooth in-app UX | | ✔️ | ✔️ |
| Integrated with app functionality | | ✔️ | ✔️ |
| No browser extensions required | | ✔️ | ✔️ |
| Granular permissions per app | | | ✔️ |
| No manual key management for users | | | ✔️ |
This comparison highlights how Para Universal Embedded Wallets combine the best features of both third-party and traditional
embedded wallets, while also offering unique advantages such as granular permissions and simplified key management.
## Security Features
Each application can be granted specific permissions, limiting potential damage if one app is compromised.
The User Share is encrypted specifically for each application, preventing unauthorized access.
Each key sharing process includes a signature verification step to ensure authenticity.
Thanks to MPC, the full private key is never exposed to any single application or stored in one place.
## How Wallet Portability Works
When a user creates a Para universal embedded wallet in one application, it's associated with their identity (e.g., email).
When the user wants to use their wallet in a new application: - They log in with their Para credentials - The new
application requests specific permissions - Upon approval, the application gains access to the user's wallet
Para securely shares the necessary key information with the new application, without exposing the full private key.
The user can now seamlessly use their wallet across all connected applications, with each app respecting its granted permissions.
## Example Use Cases
1. **DeFi Dashboard**: An app that aggregates data from multiple DeFi protocols could request read-only permissions
across various chains.
2. **NFT Marketplace**: Could request permissions specifically for NFT-related transactions on relevant chains.
3. **Cross-Chain DEX**: Might request permissions for swap transactions across multiple chains.
## Preview: Upcoming Enhancements
Para is continuously working on enhancing universal embedded wallet experiences. Future developments may include:
* More granular permission controls
* Enhanced analytics and insights for users across their entire wallet usage
* More seamless cross-chain experiences
By providing true universal embedded wallet portability, Para aims to make the crypto experience more user-friendly and secure, paving the
way for broader adoption and more innovative multi-app ecosystems.
# Account Abstraction
Source: https://docs.getpara.com/v2/general/account-abstraction
Explore account abstraction integration options with Para across different platforms
Account Abstraction (AA) enables a more intuitive and flexible blockchain experience by allowing for programmable accounts with features like gasless transactions, batched operations, and custom authorization logic.
Para serves as the Signer and EOA (Externally Owned Account) for smart wallets and is not a smart wallet itself. Para does not provide gas sponsorship, but you can use any of the supported providers to implement smart wallet functionality for your users.
## Choose Your Platform
Note that client-side support for account abstraction is not currently available for Swift and Flutter platforms. If you need account abstraction functionality in Flutter or Swift applications, it's recommended to use server-side account abstraction integration.
## Supported Providers
Para integrates with several leading Account Abstraction providers:
*
*
*
*
*
*
*
*
# Go Live Checklist
Source: https://docs.getpara.com/v2/general/checklist
A checklist to help you go live with the Para SDK.
Before going live, ensure you've completed the following steps:
* [x] 🔑 Create a account to manage your integration
* [x] Create and configure a `BETA` API Key for testing the integration
* [x] : Get the SDK or Modal Up and Running
* [x] Check out the or sections for help!
* [ ] ⛓ Get signing & on-chain connectivity set up and make sure you're able to sign transactions
* [ ] **\[EVM]**
* [ ] **\[Cosmos]** : Add on the `cosm.js` signer or plug in one of our wallet adapters for popular libraries
* [ ] : Decide if you'll be using Account Abstraction with Para
* [ ] : Connect Para users to your existing infrastructure
* [ ] 🔒 Ensure Security and Session Management works for your app
* [ ] : Decide what login methods you'd like to use
* [ ] Implement Logic
* [ ] Decide if you want to enable
* [ ] 🎨 Make Para your own with by adding the visual finishing touches
* [ ] : Configure Branding, UI and more
* [ ] 🚀 Test and go live!
* [ ] Make a `PRODUCTION` API Key (Note: you'll need to set up billing first)
* [ ] Walk through the
* [ ] Ping us if you'd like help testing anything
## Para Environments: `BETA` vs `PRODUCTION`
Para has two different environments. **NOTE: Wallets are not shared across environments**
`BETA` is intended for you to develop and test against, **not for production builds or real users and funds**
* `BETA` can be used with any plan, including the free tier
* This is the correct environment to use for local or staging builds
* `BETA` users can be deleted for your convenience while testing, and is limited to *50 max users per project*
`PRODUCTION` is intended for any real users/funds
* This is the correct environment to use for release or production builds
* `PRODUCTION` users can NOT be deleted, and have no per-project limit
# Glossary
Source: https://docs.getpara.com/v2/general/glossary
Glossary of key terms.
### Identity and Wallets
**Embedded wallet** – A non-custodial wallet built into an app, often invisible to users. Enables users to control assets without managing keys or apps.
**Passkey** – A device-native authentication method [(based on WebAuthn)](https://docs.getpara.com/v2/concepts/key-management#passkey) used as a more secure and user-friendly alternative to passwords and private keys.
**Private key** – Cryptographic secret that grants control over a wallet. In [MPC setups](https://blog.getpara.com/what-is-mpc/), it’s never reconstructed or stored in full; access is managed through secure, distributed shares.
**Recovery (or Key Recovery)** – The process for regaining access to a wallet, typically complex and manual in crypto. [Solutions like social recovery and embedded wallet recovery with Para simplify this for mainstream users.](https://docs.getpara.com/v2/concepts/recovery)
**Seed phrase** – A series of words that encode a wallet’s private keys. Often used for wallet backup, but vulnerable to phishing and user error. [(Increasingly replaced by passkeys or MPC.)](https://blog.getpara.com/what-is-mpc/)
**Session key** – A short-lived key used to sign actions during an app session without requiring full wallet access.
**Signer** – An entity (user, server, agent) that can authorize transactions for a wallet.
**Wallet address** – A unique identifier for a wallet, similar to a bank account number users can share to receive funds.
### App Infrastructure
**EVM / Solana / Cosmos** – refer to [different blockchains](https://docs.getpara.com/introduction/chain-support), each with its own ecosystem of developer tools, standards, and communities.
**Gas** – The fee paid to execute transactions on a blockchain (like network fees in fintech). In modern apps, gas can be abstracted away from users for smoother UX.
**Onchain / Offchain** – Indicates whether an action occurs on the blockchain or off of it. Onchain means the operation is happening directly on the blockchain, while offchain references to actions that take place on traditional servers or outside the blockchain.
**RPC (Remote Procedure Call)** – The bridge between an app and the blockchain, used to read/write onchain data.
**Rollup** – A type of scaling solution (e.g., [Optimism](https://www.optimism.io/), [Arbitrum](https://arbitrum.io/)) that processes transactions off the main chain and posts a summary onchain. Speeds things up and reduces gas costs.
**Smart contract** – Self-executing code on a blockchain that defines how an app behaves.
### Security and Privacy
**Custody** – Defines who controls the private keys, and therefore access to the funds in a wallet setup. In crypto, custody determines who has actual control. Wallets can either be custodial or non-custodial.
**Custodial** – A third party holds wallet keys or crypto assets on behalf of a user.
**Distributed MPC (Multi-Party Computation)** – A cryptographic method where multiple parties collaboratively compute a result, like signing a transaction, without ever revealing or reconstructing the full private key. It’s typically used alongside [Distributed Key Generation (DKG)](https://docs.getpara.com/concepts/key-management) for securely creating the key shares, and then applied during key signing. Enables secure, non-custodial access.
**Hardware wallet** – A physical device for storing crypto private keys offline.
**Non-custodial** – A setup where users retain full control of their wallet and assets. No third party can access their funds or sign on their behalf.
**Shamir Secret Sharing** – A cryptographic technique for splitting a secret (like a private key) into [multiple pieces](https://blog.getpara.com/what-is-mpc/#:~:text=number%20of%20shares.-,Shamir%20Secret%20Sharing,-2/2%20Shamir). A minimum number of these pieces must be recombined to form the original private key to sign transactins and messages. Less dynamic than MPC.
### Ecosystems & Fintech
**Liquidity** – How easily assets can be bought or sold without affecting price. Critical for user experience in swaps or trading.
**Stablecoin** – A token designed to maintain a stable value (often pegged to the US dollar). Widely used in fintech apps for payments.
**Token** – A digital asset, which can represent anything from currency to ownership in a protocol.
**USDC (USD Coin)** – A dollar-pegged stablecoin issued by [Circle](https://www.circle.com/usdc), backed 1:1 by cash and short-term U.S. government bonds. Widely used in crypto apps and exchanges.
**USDT (Tether)** – Another popular dollar-pegged stablecoin. Issued by [Tether](https://tether.to/), but with less transparency than USDC around its reserves.
# iOS App Store Submission
Source: https://docs.getpara.com/v2/general/ios-app-store-submission
A friendly checklist to get your Para-powered iOS app through App Review without surprises.
Use this guide to cover the App Review items Apple most often flags for Para integrations.
***
## 1. Sign-in options
If your app includes any third-party logins (like Google or Facebook), Apple also expects a privacy-preserving option such as **Sign in with Apple**. This ensures users can sign in without sharing personal data or tracking identifiers.
**Actions:**
* Add **Sign in with Apple** anywhere other providers appear.
* Test it in a **release build** before submission.
If your app only uses first-party sign-ins (email, phone, or passkeys), Apple doesn’t require Sign in with Apple.
***
## 2. Reviewer login flow
Make it effortless for reviewers to log in—especially if your app also supports external wallets.
**Actions:**
* Show standard sign-in options (email, phone, or Apple) first.
* Add a line in onboarding: *“No external wallet required — continue with email, phone, or Apple.”*
* In your **Reviewer Notes**, list exact steps to log in without a wallet.
**Reviewer Notes example:**
```text theme={null}
Use email or phone (OTP), or Sign in with Apple, to log in. No external wallet (e.g., MetaMask) is required to access the app.
```
***
## 3. Wallet-only positioning
If your app is a **non-custodial wallet** and **not an exchange**, make that clear. Reviewers often check for exchange or on-ramp features.
**Add to Reviewer Notes:**
```text theme={null}
The app is a non-custodial wallet using the Para SDK. It does not include exchange, swap, bridge, or on/off-ramp services. No tokens are sold or issued.
Features:
- Creates/imports non-custodial wallets via passkeys or MPC.
- Displays addresses and signs user transactions.
- Uses public RPC endpoints only.
Exclusions:
- No buy/sell/swap/bridge flows.
- No fiat or crypto on-/off-ramp.
- No KYC/AML required (wallet-only app).
Para SDK:
- Handles authentication and signing only.
- Private keys never leave the user’s device.
Request:
- Review under Guideline 3.1.5(i) as a wallet-only app.
```
***
## 4. Account deletion
If users can sign up, Apple requires an **in-app account deletion option** (not just deactivation).
Keep it simple:
* Provide a Delete Account action somewhere obvious (Settings is fine).
* If you need Para to remove the user record as well, reach out to support after you handle your own data.
**Reviewer Notes example:**
```text theme={null}
In-app account deletion is available at Settings → Account → Delete Account. Para is an SDK provider and does not host user accounts. Contact us if Para-level deletion is required.
```
***
## 5. Passkeys & entitlements
If you’re using passkeys or autofill, set up **Associated Domains** in Xcode and host an AASA file.
**Checklist:**
* Add **Associated Domains** capability.
* Include `webcredentials:your.domain`.
* Host `https://your.domain/.well-known/apple-app-site-association`.
This enables secure credential sharing and Apple’s passkey autofill.
***
## 6. Reviewer Notes checklist
Paste these details into **App Store Connect → Reviewer Notes**:
* Login path (e.g., *Continue → Sign in with Apple → Approve prompt*)
* Test credentials or OTP instructions
* Note that the app is wallet-only, not an exchange
* Location of Delete Account in settings
* Any feature flags or regional settings
* Confirmation that backend services are live
***
## 7. Privacy & SDK compliance
Apple now enforces privacy manifest rules for all third-party SDKs.
**Before you submit:**
* Include privacy manifests for every SDK.
* Ensure binary SDKs have valid signatures.
* Declare reasons for any required-reason APIs.
* Update your App Privacy answers to reflect data use accurately.
***
## 8. Export compliance
Para SDK uses standard encryption (TLS, secure enclave, etc.). Answer **Yes** to App Store Connect’s encryption question and select the “standard algorithms” exemption. If your app uses custom cryptography, you may need to upload documentation.
***
## 9. Final pre-submission checklist
### Sign-in & onboarding
* Sign in with Apple (if you offer other OAuth logins)
* Onboarding text: *No external wallet required*
* Associated Domains configured for passkeys
### Privacy & compliance
* In-app Delete Account button visible
* Privacy manifests complete & signed
* App Privacy details updated
* Export compliance questions answered
### Reviewer experience
* Reviewer Notes completed (using template)
* Test credentials provided
* Backend services online
* Privacy policy link included
***
## Related resources
* [Production Deployment](/v2/general/production-deployment)
* [Launch Checklist](/v2/general/checklist)
* [React Native iOS setup](/v2/react-native/setup/react-native#ios) · [Expo iOS](/v2/react-native/setup/expo#ios)
* [Swift setup](/v2/swift/setup#ios) · [Flutter iOS setup](/v2/flutter/setup#ios)
# Migrating from Capsule to Para
Source: https://docs.getpara.com/v2/general/migration-from-capsule
Guide for migrating from @usecapsule/* packages to @getpara/* packages
## Overview
This guide covers the migration process from Capsule to Para SDKs. The migration includes package namespace changes,
method signature updates to use object parameters, and introduces new React hooks for state management.
## Package Changes
All packages have been migrated from the `@usecapsule` namespace to `@getpara`. Update your dependencies by replacing
`@usecapsule` with `@getpara` in your package.json:
```diff theme={null}
{
"dependencies": {
- "@usecapsule/react-sdk": "^3.0.0",
- "@usecapsule/evm-wallet-connectors": "^3.0.0"
+ "@getpara/react-sdk": "^1.0.0",
+ "@getpara/evm-wallet-connectors": "^1.0.0"
}
}
```
All packages have been reset to version 1.0.0 under the new namespace. The functionality and package names remain the
same - only the organization prefix has changed from `@usecapsule` to `@getpara`.
```bash npm theme={null}
npm install @getpara/[package-name] --save-exact
```
```bash yarn theme={null}
yarn add @getpara/[package-name] --exact
```
```bash pnpm theme={null}
pnpm add @getpara/[package-name] --save-exact
```
## Mobile SDK Updates
### Flutter
The Flutter package has moved from `capsule` to `para` on pub.dev:
```diff theme={null}
dependencies:
- capsule: 0.7.0
+ para: ^1.0.0
```
Create instances using `Para()` instead of `Capsule()`. All method signatures remain unchanged.
### Swift
The Swift SDK package is now available at `github.com/getpara/swift-sdk`. The main class has been renamed from
`CapsuleManager` to `ParaManager`, while maintaining the same method signatures:
```diff theme={null}
- let manager = CapsuleManager()
+ let manager = ParaManager()
```
Method signatures and functionality remain identical for both mobile SDKs - only the package names and main class
names have changed.
## Breaking Changes
### Method Updates
All methods have been updated to use object parameters instead of multiple arguments. This change improves
extensibility, type safety, and reflects our commitment to consistent API design.
```typescript theme={null}
// Old
createUser(email: string)
// New
createUser({ email: string })
```
```typescript theme={null}
// Old
createUserByPhone(phone: string, countryCode: string)
// New
createUserByPhone({ phone: string, countryCode: string })
```
```typescript theme={null}
// Old
externalWalletLogin(address: string, type: string, provider?: string, addressBech32?: string)
// New
externalWalletLogin({
address: string,
type: string,
provider?: string,
addressBech32?: string
})
```
```typescript theme={null}
// Old
createWallet(type: WalletType, skipDistribute?: boolean)
// New
createWallet({
type: WalletType,
skipDistribute?: boolean = false
})
```
```typescript theme={null}
// Old
createWalletPerType(skipDistribute?: boolean, types?: WalletType[])
// New. Note: Function name changed, default value added, and types is now required
createWalletPerType({
skipDistribute?: boolean = false,
types: WalletType[]
})
```
```typescript theme={null}
// Old
distributeNewWalletShare(
walletId: string,
userShare?: string,
skipBiometricShareCreation?: boolean,
forceRefreshRecovery?: boolean
)
// New
distributeNewWalletShare({
walletId: string,
userShare?: string,
skipBiometricShareCreation?: boolean = false,
forceRefresh?: boolean = false
})
```
```typescript theme={null}
// Old
createWalletPreGen(
type: WalletType,
pregenIdentifier: string,
pregenIdentifierType?: PregenIdentifierType
)
// New
createPregenWallet({
type: WalletType,
pregenIdentifier: string,
pregenIdentifierType?: PregenIdentifierType
})
```
```typescript theme={null}
// Old - Note: Function name changed
updateWalletIdentifierPreGen(
newIdentifier: string,
walletId: string,
newType?: PregenIdentifierType
)
// New
updatePregenWalletIdentifier({
walletId: string,
newPregenIdentifier: string,
newPregenIdentifierType?: PregenIdentifierType
})
```
```typescript theme={null}
// Old
signMessage(
walletId: string,
messageBase64: string,
timeoutMs?: number,
cosmosSignDocBase64?: string
)
// New
signMessage({
walletId: string,
messageBase64: string,
timeoutMs?: number,
cosmosSignDocBase64?: string
})
```
```typescript theme={null}
// Old
signTransaction(
walletId: string,
rlpEncodedTxBase64: string,
timeoutMs?: number,
chainId: string
)
// New
signTransaction({
walletId: string,
rlpEncodedTxBase64: string,
timeoutMs?: number,
chainId: string
})
```
All methods now use object parameters with optional properties defaulting to reasonable values. This change makes the
SDK more maintainable and easier to extend in the future.
## New Features: React Hooks
Para now includes React hooks for easier state management and SDK interaction. Here's a basic setup:
```typescript theme={null}
import { ParaProvider, ParaModal } from "@getpara/react-sdk";
function App() {
return (
);
}
```
### Available Hooks
Access current account state and connection status
Get current wallet information and state
Create a new Para user account
Check if a user exists by email
Start the login process
Handle user logout
Maintain active user session
Create wallet after passkey verification
Sign messages with connected wallet
Sign transactions with connected wallet
Handle login flow and initial setup
Monitor account creation process
Access Para client instance
Control Para modal visibility
Manage wallet state
## Next Steps
1. Update your package dependencies to use `@getpara/*` packages
2. Migrate method calls to use new object parameters
3. Consider implementing React hooks for simpler state management
4. Review framework-specific integration guides for detailed setup instructions
# Integrate Para Docs MCP with AI Tools
Source: https://docs.getpara.com/v2/general/para-docs-mcp
Connect the Para Docs Mintlify MCP server to AI tools for direct documentation access and enhanced coding assistance
Connect the Para Docs MCP server to AI tools for seamless access to documentation, code examples, and guides. This integration enables AI assistants to search Para Docs directly via the Model Context Protocol.
## Prerequisites
* Active accounts for target AI tools
* Para Docs MCP server URL: `http://docs.getpara.com/mcp`
* AI tool with remote MCP connection support (may be in beta)
## Installation and Setup
Set up connections by adding the MCP server as a custom connector in each tool's settings.
### Configure ChatGPT Connection
1. Open ChatGPT settings from your avatar menu
2. Select **Connectors** in the sidebar
3. Click **Create** to open the New Connector dialog
4. Enter the MCP server URL: `http://docs.getpara.com/mcp`
5. Configure authentication if required (Para Docs MCP uses no security by default)
6. Save and test the connection
### Configure Claude Desktop
1. Navigate to **Settings > Extensions** in Claude Desktop
2. Click **Advanced settings** and locate the Extension Developer section
3. Add a custom connector with the remote MCP URL: `http://docs.getpara.com/mcp`
4. Note: Remote support is in beta; use local STDIO if preferred
5. Verify the connection in Claude's interface
### Configure Claude Code
1. Run the CLI command: `claude mcp add`
2. Follow the wizard to input the MCP server URL: `http://docs.getpara.com/mcp`
3. Select remote MCP support
4. Integrate tools like search and fetch for Para Docs access
5. Restart Claude Code to apply changes
### Configure Cursor
1. Open Cursor settings and navigate to **Models** or **API Keys**
2. Disable unnecessary models if needed
3. Add a custom model or provider, overriding the base URL to `http://docs.getpara.com/mcp`
4. Use agent mode for MCP interactions (similar to VS Code Copilot)
5. Verify by testing a documentation query in the editor
## Usage
Query the MCP server via your AI tool's interface after setup. Provide search terms to the "SearchParaDocs" tool for relevant results.
Ask your AI: "Search Para Docs for \[topic]"
The tool returns titles, snippets, and links to relevant documentation.
Review returned contextual content and follow links for full details.
Check for connection issues and retry if the server is unreachable.
## Example
### Query Example
```text theme={null}
Search Para Docs for how to sign a basic message
```
### Expected Response
The MCP server returns structured results:
```json Response theme={null}
{
"results": [
{
"title": "Sign Messages with Para",
"snippet": "Learn how to sign messages using Para's wallet integration...",
"link": "https://docs.getpara.com/v2/react/guides/web3-operations/sign-with-para"
},
{
"title": "Web3 Operations Guide",
"snippet": "Complete guide for signing transactions and messages...",
"link": "https://docs.getpara.com/v2/react/guides/web3-operations"
}
]
}
```
# Wallet Pregeneration
Source: https://docs.getpara.com/v2/general/pregen
Overview of generating pregenerated wallets for Para
Wallet pregeneration allows you to create wallets in advance of user interactions, which can significantly improve user onboarding speeds and overall experience in your application. With pregeneration, you can create a wallet for any identifier (email, phone number, username, etc.), executing Para's 2-of-2 MPC protocol where your application initially maintains ownership over the user's share of the MPC.
The pregenerated wallet can later be claimed by the user associated with that identifier, transferring ownership of their share to them – if your application permits this option. This flexibility opens up numerous possibilities for streamlining user experiences and creating innovative onboarding flows.
## Choose Your Platform
Para supports wallet pregeneration across all supported platforms. Select your development platform to view platform-specific implementation details:
## Innovative Use Cases
Pregeneration enables powerful new ways to incorporate blockchain into your application:
* **Mass User Onboarding**: Create wallets for your existing user base or email lists instantly
* **Social Integration**: Generate wallets based on social identifiers like Twitter followers
* **Agent-Owned Wallets**: Allow AI agents or bots to create and manage wallets for specific functions
* **Server-Side Operations**: Create app-managed wallets to perform operations on behalf of users
* **Airdrops and Rewards**: Preload funds or NFTs into wallets that users can claim later
* **Staged Onboarding**: Let users experience your application before formally creating their wallet
These use cases represent just a few of the possibilities enabled by wallet pregeneration. The platform-specific guides provide detailed implementation instructions for your chosen environment.
# Deploy Para Integration to Production
Source: https://docs.getpara.com/v2/general/production-deployment
Transition your Para integration from development to production with updated configurations and security settings
Deploy your Para integration to production by updating configurations and ensuring secure settings for real users.
## Prerequisites
You need these components before deploying to production:
* A working Para integration in development/beta environment
* Production API credentials from the [Para Developer Portal](https://developer.getpara.com/)
* Production domain with HTTPS enabled
* Latest Para SDK version installed
## Deployment Steps
Update your Para SDK to the latest version before going live. Use the newest v2.0 release for all projects to benefit from improvements and fixes.
Check your @getpara/\* package versions and update to the latest stable release. Latest SDK versions resolve many integration issues.Align your project with the [current Para release](https://www.npmjs.com/package/@getpara/react-sdk?activeTab=versions)to access recent features and security patches for real users.
Create a production API key in the Para Developer Portal for your live environment.
Replace development credentials with production values in your environment variables.
Update all secrets and URLs in your .env file for production, including callback URLs and API endpoints.
Configure your integration to use the production environment instead of beta/development. Para provides two hosted environments: BETA for testing and PROD for live use.
Update your initialization from:
```javascript Development theme={null}
const para = new ParaWeb(Environment.BETA, PARA_API_KEY);
```
To:
```javascript Production theme={null}
const para = new ParaWeb(Environment.PROD, PARA_API_KEY);
```
Para's Beta and Production are separate environments. Wallets and users are not shared between them. Your production environment starts with no users, and requires a distinct production API key.
Update allowed origin settings in the Para developer portal to include your production domain. This Domain Security setting restricts API usage to specified origins.
Add entries like `https://yourapp.com` and production subdomains to the Allowed Origins list. Remove development URLs like `http://localhost:3000` if no longer needed.
HTTPS is required in production. Configuring allowed origins prevents unauthorized domains from using your Para integration.Allowed Origins are only supported for web integrations, not server or mobile.
Review your Para integration settings for production, especially user communications and UI:
### Verification Redirect URL
Set the verification redirect URL to point to your production application's confirmation page. This URL appears in verification emails Para sends to users. Update from localhost URLs to your live domain.
### Email Notification Preferences
Choose appropriate email settings for production. Para can send welcome emails and optional backup kit instructions to new users.
Configure email templates and branding (logo, app name) for real user emails based on your user experience strategy.
### Branding and UI Configuration
Customize the Para modal or widget to match your production app's branding (colors, fonts, icons). Configure theme options through the Para SDK or developer portal's branding section.
Deploy your updated application with production settings and perform end-to-end testing with real scenarios:
### Test User Sign-Up/Login
Create a new user account using a real email address or phone number. Ensure OTP codes are delivered and users can complete verification and wallet creation.
Test emails like [dev@test.getpara.com](mailto:dev@test.getpara.com) and dummy phone numbers will not work in production. Production requires real contact information and OTP verification.Ensure you are signing transactions on the [appropriate network](https://docs.getpara.com/v2/introduction/chain-support).
### Functionality Check
Verify wallet operations work with production settings. Test retrieving wallet addresses or making test transactions on mainnet to confirm Para's production environment interacts correctly.
Verify session management and security features (persistent login/logout) behave as expected.
### Monitor and Review
Watch browser console and backend logs for errors like misconfigured API keys or CORS issues. If you encounter CORS or CSP errors, verify the origin matches your deployed URL exactly.
## Pricing Information
With [Para Free Tier](https://www.getpara.com/pricing) your first 1,200 monthly active users are free. Paid plans require billing information to upgrade.
## Next Steps
# Telegram Bots & Mini-Apps
Source: https://docs.getpara.com/v2/general/telegram
A user-friendly guide to quickly integrate Telegram Apps
For signing in to Para with Telegram, refer to .
Para supports both Telegram Bots and Mini-Apps.
are a way to program logic natively into the Telegram App interface via text-based prompts and commands. Telegram Bots feel native to the UX of Telegram, but are limited to the UX and functionality of text-based options and menus.
are an easy way to serve hosted web applications from within Telegram. Given Mini-Apps are added functionality to an existing
web app, they are much more flexible but have a UX pattern that deviates from the native Telegram experience.
## Telegram Bot
The most popular way to use Para in a Telegram Bot is to leverage with the .
You have the option of allowing your users to their pregenerated wallets, which can happen directly within Telegram or in a standalone app.
## Mini-App
You can build Mini-Apps two ways:
1. Use the with the password option.
2. Use with the web SDK in a web framework of your choice. Once you have a web example working, use Telegram to create an interface for getting and setting data, like in .
3. You'll need to decide where and how you want users to claim their pregenerated wallets. This can happen within
Telegram Mini-Apps or in a standalone app.
Telegram Storage APIs have some limitations on data size. You may need to implement chunking to work around this. See
this for more info.
# Troubleshooting
Source: https://docs.getpara.com/v2/general/troubleshooting
Something not working quite right? This is the section for you!
Having trouble with your Para integration? You're in the right place. This section contains platform-specific troubleshooting guides to help you resolve common issues across different frameworks and environments.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the for the most up-to-date help
2. Check out the for an interactive LLM using Para Examples Hub
## Choose Your Platform
### Web
### Mobile
## Popular Web Frameworks
If we're missing a troubleshooting guide for a framework you're using, please get in touch! We're constantly expanding our documentation to cover more environments.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please [contact our team](https://join.slack.com/t/para-community/shared_invite/zt-304keeulc-Oqs4eusCUAJEpE9DBwAqrg) for
assistance. To help us resolve your issue quickly, please include the following information in your request:
1
A detailed description of the problem you're encountering.
2
Any relevant error messages or logs.
3
Steps to reproduce the issue.
4
Details about your system or environment (e.g., device, operating system, software version).
Providing this information will enable our team to address your concerns more efficiently.
# User Data Management
Source: https://docs.getpara.com/v2/general/user-data
A comprehensive guide to managing user data when integrating Para into your application
Effective user data management is crucial when integrating Para into your application. This guide covers best practices
for handling user information, including storage, retrieval, and privacy considerations.
## User Data in Para
Para's approach to user data is designed with privacy and security in mind. Here's what you need to know:
Para only collects essential information required for account identification and recovery, typically just the
user's email address.
User data is securely stored and encrypted on Para's servers. However, the most sensitive information - the user's
private keys - are never fully stored in one place due to Para's MPC technology.
As a developer, you have limited direct access to user data stored by Para. This is by design to ensure user
privacy and security.
## Managing User Data in Your Application
While Para handles the core wallet functionality, you may need to manage additional user data in your application. Here
are some best practices:
### Storing User Information
When storing additional user information in your application:
1. Only store what's necessary for your application's functionality.
2. Use secure, encrypted storage methods.
3. Consider using Para's wallet ID as a unique identifier for your users.
Example of storing user data:
```typescript theme={null}
type UserData = {
paraWalletId: string;
username: string;
preferences: Record;
};
// Assume you're using a secure database
async function storeUserData(userData: UserData) {
await database.users.insert(userData);
}
// Usage
const wallets = await para.getWallets();
const walletId = Object.values(wallets)[0].id;
await storeUserData({
paraWalletId: walletId,
username: "user123",
preferences: { theme: "dark" },
});
```
### Retrieving User Information
To retrieve user information:
1. Use Para's methods to get wallet-related information.
2. Fetch additional data from your own storage using the Para wallet ID as a reference.
Example:
```typescript theme={null}
async function getUserData(email: string) {
const wallets = await para.getWallets();
const walletId = Object.values(wallets)[0].id;
// Fetch additional data from your storage
const userData = await database.users.findOne({ paraWalletId: walletId });
return {
walletId,
...userData,
};
}
```
### Updating User Data
When updating user data:
1. Use Para's methods for updating wallet-related information.
2. Update additional data in your own storage.
```typescript theme={null}
async function updateUserPreferences(walletId: string, newPreferences: Record) {
// Update in your storage
await database.users.update({ paraWalletId: walletId }, { $set: { preferences: newPreferences } });
}
```
## Privacy and Security Considerations
When managing user data, always prioritize privacy and security:
Only collect and store data that is absolutely necessary for your application's functionality.
Always encrypt sensitive data, both in transit and at rest.
Implement strict access controls to ensure that only authorized personnel can access user data.
Conduct regular audits of your data management practices to ensure compliance with privacy regulations.
## Compliance with Regulations
Ensure your user data management practices comply with relevant regulations such as GDPR, CCPA, or other applicable
laws. This may include:
* Providing users with the ability to request their data
* Allowing users to delete their data
* Implementing data portability features
Example of a data deletion function:
```typescript theme={null}
async function deleteUserData(walletId: string) {
// Delete from your storage
await database.users.delete({ paraWalletId: walletId });
// Note: Para wallet data cannot be deleted directly through the SDK
// Advise the user to contact Para support for complete account deletion
}
```
Remember that while you can delete user data from your own storage, Para wallet data is managed separately for
security reasons. Users should be directed to Para's official channels for complete account deletion requests.
## Best Practices for User Data Management
1. **Separation of Concerns**: Keep Para-related data separate from your application-specific user data.
2. **Regular Backups**: Implement a robust backup strategy for user data stored in your application.
3. **Transparent Policies**: Clearly communicate your data handling practices to users through privacy policies and
terms of service.
4. **Secure Transmission**: Always use secure, encrypted channels when transmitting user data.
5. **Data Validation**: Implement thorough input validation to prevent injection attacks and ensure data integrity.
## Troubleshooting
If you encounter issues with user data management:
1. Ensure you're using the latest version of the Para SDK.
2. Verify that you're correctly handling asynchronous operations when interacting with Para and your own data storage.
3. Double-check that you're using the correct wallet IDs and other identifiers.
4. Review your error handling to ensure you're catching and addressing all potential exceptions.
# Para Video Resources
Source: https://docs.getpara.com/v2/general/webinars
Check out talks by the Para team about our roadmap, MPC, and in-depth looks at Para's architecture and use cases.
Founder/CEO Nitya Subramanian covers how multi-party computation can enable flexible and programmable transactions at zkParis 2023
Design Lead Jonny Howle covers how embedded wallets, MPC, and account abstractin should be seen as components of a wallet's system that can deliver on progressive disclosure and UX for users.
Nitya Subramanian delves into various approaches key management and trust in blockchain transactions, both on-chain and off-chain at ETHDenver 2024.
Watch these videos to learn how to set up your Developer Portal account
# Chain Support
Source: https://docs.getpara.com/v2/introduction/chain-support
Build on any supported chain.
Para integrates seamlessly with all EVM chains, Solana, and Cosmos chains. To add support for any additional chains within these networks, include its chain ID and RPC endpoint. Breakdown below:
| Networks/RPCs | Chain ID | Supported |
| ---------------------- | ------------ | --------- |
| Arbitrum | 42161 | ✅ |
| Arbitrum Sepolia | 421614 | ✅ |
| Arc Testnet | 5042002 | ✅ |
| Avalanche C-Chain | 43114 | ✅ |
| Avalanche Fuji | 43113 | ✅ |
| Basecamp (Camp) | 123420001114 | ✅ |
| Base | 8453 | ✅ |
| Base Sepolia | 84532 | ✅ |
| Berachain Artio | 80085 | ✅ |
| Camp Testnet V2 | 325000 | ✅ |
| Basecamp (Camp) | 123420001114 | ✅ |
| Celo | 42220 | ✅ |
| Celo Alfajores | 44787 | ✅ |
| Eclipse | 17172 | ✅ |
| Ethereum | 1 | ✅ |
| Ethereum Sepolia | 11155111 | ✅ |
| Flow EVM | 747 | ✅ |
| Fluent | 20993 | ✅ |
| Holesky | 17000 | ✅ |
| Holesky Garnet | 17069 | ✅ |
| Holesky Redstone | 17001 | ✅ |
| Katana | 747474 | ✅ |
| Kaia | 8217 | ✅ |
| Linea | 59144 | ✅ |
| Linea Testnet | 59140 | ✅ |
| Mantle | 5000 | ✅ |
| Monad Testnet | 10143 | ✅ |
| Omni | 166 | ✅ |
| Optimism | 10 | ✅ |
| Optimism Sepolia | 11155420 | ✅ |
| Plasma Testnet | 9746 | ✅ |
| Plume | 98866 | ✅ |
| Polygon | 137 | ✅ |
| Polygon Amoy | 80002 | ✅ |
| Rise Testnet | 11155931 | ✅ |
| Sei | 1329 | ✅ |
| Solana Devnet | 103 | ✅ |
| Soneium | 1868 | ✅ |
| Story | 1514 | ✅ |
| Syndicate Mainnet | 510 | ✅ |
| Tempo Moderato Testnet | 42431 | ✅ |
| Vana | 1480 | ✅ |
| Zora | 7777777 | ✅ |
| Zora Sepolia | 999999999 | ✅ |
| Solana | — | ✅ |
| Noble (Cosmos Chain) | noble-1 | ✅ |
| Initia | interwoven1 | ✅ |
| Initia testnet | initiation-2 | ✅ |
| Osmosis(Cosmos Chain) | osmosis-1 | ✅ |
| XDC | 50 | ✅ |
# Examples Hub
Source: https://docs.getpara.com/v2/introduction/examples
Explore Para's examples hub for integration patterns across web, mobile, and specific use cases
Para provides a comprehensive collection of examples to help you integrate our technology across various platforms, frameworks, and use cases. Our repository contains working code samples for all supported platforms.
Looking for community-created examples? Check out for community-featured examples and integrations from bounties and hackathons covering Account Abstraction, Agents, Pregeneration, and more!
## Web Framework Examples
## Server Examples
## Mobile Examples
Para SDK supports React Native, Flutter, and Swift for native mobile experiences:
## Popular Feature Examples
## Blockchain Integration Examples
## Transaction Signing Libraries
## Specialized Examples
## Community Contributions
## Need Something Specific?
Don't see an example for your use case? Para's team is eager to create new examples to help you integrate with different libraries, third-party features, or providers.
# Framework Support
Source: https://docs.getpara.com/v2/introduction/framework-support
Supported Features Across Para's Frameworks
This table outlines the current, quickly changing status of feature support across Para's SDKs.
We release new frameworks, then iteratively add features based on customer needs. If you don't see a feature you're
looking for here, - we may already be
working on it!
# Migrating from Para v1.x to v2.0
Source: https://docs.getpara.com/v2/introduction/migration-to-alpha
Guide for migrating from Para version 1.x to Para's version 2.0 release
Use this guide for assistance migrating from Para version 1.x to Para's version 2.0 release.
## React SDK
### Summary of breaking changes
1. The `setup-para` CLI tool needs to run before running your app to ensure all `@getpara` libraries are properly polyfilled. It is recommended to do this in your `postinstall` step of your `package.json`, something like:
```bash theme={null}
"postinstall": "yarn setup-para"
```
2. The `ParaProvider` is now required to be used when using the `@getpara/react-sdk`
3. A `queryClient` from `@tanstack/react-query` must be provided.
See the `@tanstack/react-query` [docs](https://tanstack.com/query/latest/docs/framework/react/quick-start) for more information on setting up a `QueryClient`
4. The `appName` prop has moved from a modal prop to a **required** config prop on the `ParaProvider`. This name will be used throughout the modal and in any external wallet providers that may be used.
5. The `ParaModal` no longer needs to be provided separately, it is automatically included with the `ParaProvider` and all modal props can be passed to the `ParaProvider`.
The `ParaModal` can still be provided separately if that is preferred. In this case the `disableEmbeddedModal: true` value should be passed to the `ParaProvider` config.
6. The `ParaModal` props, `isOpen` and `onClose`, are no longer required (though they can be provided if desired). These values are now handled by the `ParaProvider` and developers can use the `useModal` hook to control the modal state.
7. When using external wallets the Para connector libraries no longer need to be provided, just installed. All config values for these connectors can be passed to the `ParaProvider`.
### Migration
Migration to V2 can be done one of two ways:
1. (**PREFERRED**) Remove the `ParaModal` component you are providing and use the modal that the updated `ParaProvider` provides. This option offers the most code reduction and overall simpler developer experience.
2. Continue to use the `ParaModal` component you are providing in your app. This option is a bit quicker than the first but will lead to a poorer developer experience.
Remove your `` and use the one built into ``.
**Starting Code:**
```tsx Starting Code (Existing Provider) theme={null}
{...REST_OF_YOUR_APP}
```
```tsx Starting Code (Without Existing Provider) theme={null}
const Para = new Para("YOUR_API_KEY")
<>
{...REST_OF_YOUR_APP}
>
```
**After Migration:**
```tsx After Migration {7-11} theme={null}
{...REST_OF_YOUR_APP}
{/* Note: is removed from here */}
```
Continue using your own `` by disabling the built-in one.
**Starting Code:**
```tsx Starting Code (Existing Provider) theme={null}
{...REST_OF_YOUR_APP}
```
```tsx Starting Code (Without Existing Provider) theme={null}
const Para = new Para("YOUR_API_KEY")
<>
{...REST_OF_YOUR_APP}
>
```
**After Migration:**
```tsx After Migration {8-10, 16} theme={null}
{...REST_OF_YOUR_APP}
```
### Adding External Wallets
If you're using external wallets with Para currently, those configs can now be passed to the `ParaProvider` and the connectors will be instantiated for you. Assuming you already followed the migration steps above, a successful migration would look like:
```tsx Starting Code [expandable] theme={null}
{
SELECTED_CHAIN_STATE_UPDATER_FN();
}}
multiChain
walletConnect={{ options: { projectId: 'YOUR_WALLETCONNECT_PROJECT_ID' } }}
>
{...REST_OF_YOUR_APP}
```
```tsx After Migration {11-41} [expandable] theme={null}
{
SELECTED_CHAIN_STATE_UPDATER_FN();
},
chains: YOUR_COSMOS_CHAINS,
},
// grazProviderProps={}
},
solanaConnector: {
config: {
endpoint: YOUR_SOLANA_ENDPOINT,
chain: YOUR_SOLANA_NETWORK,
},
},
walletConnect: {
projectId: 'YOUR_WALLETCONNECT_PROJECT_ID',
},
}}
>
{...REST_OF_YOUR_APP}
```
Notes on the external wallet migration:
1. All wallets are provided by default, if you wish for them all to be provided you can remove the `wallets` value in the `externalWalletConfig`
2. If you only wish to use EVM wallets only the `evmConnector` config needs to be passed, the other connectors will be skipped in that case.
## Pregen Wallet Methods
Methods dealing with pregen wallets now use a simpler object-based notation to specify the type and identifier the wallet belongs to.
```tsx Email theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: 'email@email.com',
pregenIdentifierType: 'EMAIL'
});
// 2.x
await para.createPregenWallet({ pregenId: { email: 'email@email.com' } });
```
```tsx Phone theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: '+13105551234',
pregenIdentifierType: 'PHONE'
});
// 2.x
await para.createPregenWallet({ pregenId: { phone: '+13105551234' } });
```
```tsx Farcaster theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: 'FarcasterUsername',
pregenIdentifierType: 'FARCASTER'
});
// 2.x
await para.createPregenWallet({ pregenId: { farcasterUsername: 'FarcasterUsername' } });
```
```tsx Telegram theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: '1234567890',
pregenIdentifierType: 'TELEGRAM'
});
// 2.x
await para.createPregenWallet({ pregenId: { telegramUserId: '1234567890' } });
```
```tsx Discord theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: 'DiscordUsername',
pregenIdentifierType: 'DISCORD'
});
// 2.x
await para.createPregenWallet({ pregenId: { discordUsername: 'DiscordUsername' } });
```
```tsx X (Twitter) theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: 'XUsername',
pregenIdentifierType: 'TWITTER'
});
// 2.x
await para.createPregenWallet({ pregenId: { xUsername: 'XUsername' } });
```
```tsx Custom ID theme={null}
// 1.x
await para.createPregenWallet({
pregenIdentifier: 'my-custom-id',
pregenIdentifierType: 'CUSTOM_ID'
});
// 2.x
await para.createPregenWallet({ pregenId: { customId: 'my-custom-id' } });
```
## Common Enums (optional)
Enum types used in certain methods, while still available, are now replaced with string union types. You will not be required to import these enums for methods that accept them.
```tsx {4} theme={null}
import { WalletType } from '@getpara/web-sdk';
// Either is allowed
para.createWallet({ type: WalletType.EVM });
para.createWallet({ type: 'EVM' });
```
### Type Definitions
Ethereum Virtual Machine compatible wallet
Solana wallet
Cosmos wallet
DKLS wallet scheme
ED25519 wallet scheme
CGGMP wallet scheme
Google OAuth
Apple OAuth
Twitter/X OAuth
Discord OAuth
Facebook OAuth
Telegram OAuth
Farcaster OAuth
## Auth Objects
User identity attestations are now represented as auth objects with a single key and value, representing both the type of attestation and the relevant identifier. These objects are used for methods that require an attestation of this type, primarily those for authentication and pregenerated wallet management.
### Auth Object Examples
```tsx Email theme={null}
const auth = { email: 'email@test.com' };
await para.signUpOrLogIn({ auth });
```
```tsx Phone theme={null}
const auth = { phone: '+13105551234' };
await para.signUpOrLogIn({ auth });
```
```tsx Farcaster theme={null}
const pregenId = { farcasterUsername: 'MyUsername' };
await para.createPregenWallet({ pregenId, type: 'EVM' });
```
```tsx Telegram theme={null}
const pregenId = { telegramUserId: '1234567890' };
await para.createPregenWallet({ pregenId, type: 'EVM' });
```
```tsx Discord theme={null}
const pregenId = { discordUsername: 'MyUsername' };
await para.createPregenWallet({ pregenId, type: 'EVM' });
```
```tsx X/Twitter theme={null}
const pregenId = { xUsername: 'MyUsername' };
await para.createPregenWallet({ pregenId, type: 'EVM' });
```
```tsx Custom Pregen ID theme={null}
const pregenId = { customId: 'my-custom-id' };
await para.createPregenWallet({ pregenId, type: 'EVM' });
```
Phone number auth objects expect a string in international format, beginning with a `+` and containing only numbers without spaces or extra characters, i.e.: `+${number}`. If your UI deals in separated country codes and national phone numbers, you may use the exported `formatPhoneNumber` function to combine them into a correctly formatted string.
```tsx theme={null}
import { formatPhoneNumber } from '@getpara/web-sdk';
await para.signUpOrLogIn({ auth: { phone: '+13105551234' } });
// or, if your country code and national number are distinct:
await para.signUpOrLogIn({ auth: { phone: formatPhoneNumber('3105551234', '1') } });
```
## Cancelable Methods (optional)
This feature is available in the following SDKs:
* `@getpara/web-sdk`
* `@getpara/react-sdk`
* `@getpara/react-native-sdk`
For methods that wait for user action, such as `waitForLogin`, you may now pass a callback that is invoked on each polling interval, as well as a callback to indicate whether the method should be canceled and another invoked upon cancelation.
```tsx theme={null}
let i = 0, popupWindow: Window;
await para.waitForLogin({
isCanceled: () => popupWindow?.closed,
onPoll: () => {
console.log(`Waiting for login, polled ${++i} times...`)
},
onCancel: () => {
console.log('Login canceled after popup window closed!');
}
});
```
## New Authentication Flow
The primary methods for authenticating via phone, email address, or third-party services have been overhauled and greatly simplified. If you are using a custom authentication UI, refer to the [Custom Authentication UI](/v2/react/guides/custom-ui-hooks) page for detailed instructions and code samples. For new developers, the [Para Modal](/v2/react/overview) is the preferred option to handle user authentication in your app.
## Modified Core Methods
We've streamlined and improved several core methods in version 2.0.0. The following sections outline what's changed and what actions you need to take.
These changes are required when upgrading to version 2.0.0. Make sure to update your code accordingly to avoid breaking your application.
* `checkIfUserExists`
* `initiateUserLogin`
* `createUser`
* `checkIfUserExistsByPhone`
* `initiateUserLoginForPhone`
* `createUserByPhone`
`signUpOrLogIn`
Modify your current authentication flow to use the new simplified `signUpOrLogIn` method as detailed on our Custom Authentication UI page.
This change simplifies the authentication process by consolidating multiple methods into a single, more intuitive function.
* `waitForLoginAndSetup`
* `waitForAccountCreation`
* `waitForPasskeyAndCreateWallet`
* `waitForLogin`
* `waitForSignup`
* `waitForWalletCreation`
Modify your current authentication flow to use the new simplified methods as detailed on our Custom Authentication UI page.
* `createPregenWallet`
* `createPregenWalletPerType`
* `updatePregenWalletIdentifier`
* `hasPregenWallet`
* `claimPregenWallets`
Modified to accept a single `pregenId` argument
Update your functions to use the new notation.
```javascript Before theme={null}
para.createPregenWallet({
pregenIdentifier: 'email@email.com',
pregenIdentifierType: 'EMAIL'
})
```
```javascript After theme={null}
para.createPregenWallet({
pregenId: {
email: 'email@email.com'
}
})
```
* `initiateFarcasterLogin`
* `waitForFarcasterStatus`
`verifyFarcaster`
Use the simplified `verifyFarcaster` method as detailed on our Custom Authentication UI page.
* `getOAuthUrl`
* `waitForOAuth`
`verifyOAuth`
Use the simplified `verifyOAuth` method as detailed on our Custom Authentication UI page.
`touchSession`
Destructuring is simpler, returning an object of type `SessionInfo` rather than `{ data: SessionInfo }`
Update existing usages to destructure the return value correctly.
* `check2FAStatus` (Deprecated)
* `enable2FA`
* `verify2FA`
* `setup2FA`
* `setup2fa` (Replaces `check2FAStatus`)
* `enable2fa`
* `verify2fa`
* Replace calls to `check2FAStatus` with `setup2fa`, which will now either return `{ isSetup: true }` or `{ isSetup: false, uri: string }` depending on the current user's settings
* Update function instances to use the new spelling with lowercase "fa"
React Native, Flutter, Swift
`login`
`loginWithPasskey`
Update function instances to use the new name.
# Migrate to Para
Source: https://docs.getpara.com/v2/introduction/migration-to-para
Already have some users in another auth or wallet system? Para makes it easy to migrate!
## Overview
Para makes it easy to migrate users and/or wallets from another system. As with any migration, there are many nuances to
consider, and a variety of options for how to approach a migration. First, you'll need to determine what type of
integration you want to complete.
## Migration Scope
New pregenerated wallets will be created for your existing users, which they can claim when they log in. Please see
our [GitHub repository](https://github.com/getpara/examples-hub/tree/2.0.0/web/) for a sample code snippet and
instructions on how to leverage pregenerated wallets.
In addition to creating new wallets, assets can be transferred either in a batch (if migrating from a custodial system) or just-in-time as users claim their wallet and log in. In this method, assets can be ported over, but users will receive a new wallet address with Para.
If you are following this path, please get in touch with Para as the specifics of a migration path and options vary based on your current provider.
Para can also support importing a private key if your provider offers an export private key option. Using this path, users can keep the same wallet address they currently have.
This feature is experimental and availability varies based on the provider currently being used. If you would like to follow this path, please get in touch with Para for more information and to get access.
## Migration Timing
For each of these options, there are two paths to migrating:
Move users over proactively. This option works best if: - You don't have assets to migrate - You're migrating with a
smart contract account setup - You're migrating from a custodian that has a batch export feature
As users onboard, migrate over their account and/or assets. This option works best if you need users to: - Sign a
transaction to onboard - Take an action such as exporting a private key and importing it into Para's system
Migrations can be tricky business! We're here to help, please get in touch and consider us your thought partners in
this process.
# Welcome
Source: https://docs.getpara.com/v2/introduction/welcome
Welcome to the Para Developer Docs! If you're looking to learn more about integrating Para or discover advanced features, you've come to the right place!
## Choose Your Framework
Para provides SDKs for various frameworks to help you integrate quickly and easily. Pick your preferred framework below to get started with the integration guide.
Looking to pregenerate wallets? Check out
## Integration Guides
Para supports any EVM, Solana, or Cosmos chain and is compatible with popular signing libraries. We also have adapters for wallet connector libraries.
## Additional Resources
Para provides a range of resources to help you troubleshoot and enhance your integration. Explore our examples hub for sample apps, or check out our troubleshooting guides for framework-specific solutions.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the for the most up-to-date help
2. Check out the for an interactive LLM using Para Examples Hub
# What's New in Para v2.0
Source: https://docs.getpara.com/v2/introduction/whats-new
A high-level overview of new features and improvements in Para v2.0
## Overview
Para v2.0 represents a significant evolution in our SDK architecture and developer experience. This release introduces streamlined authentication flows, improved component structure, and enhanced wallet management while maintaining backward compatibility where possible.
## New Features
*We'll keep adding features as they get rolled out, check back in here for the latest!*
* **Guest Mode**: Let users ["Continue as Guest"](/v2/react/guides/customization/guest-mode) and provision them a wallet to experience your app before they sign up.
## Key Improvements
### Simplified Authentication
The v2.0 release dramatically simplifies authentication flows by consolidating multiple methods into single, intuitive interfaces:
* **Unified Entry Point**: The new `signUpOrLogIn` method replaces separate user existence checks, login initiation, and user creation methods
* **Standardized Auth State System**: A consistent state machine approach for tracking authentication progress
* **Enhanced OAuth Flow**: Streamlined third-party authentication with platforms like Google, Apple, and Discord
* **Improved Social Login**: Simplified Farcaster and Telegram login experiences
### Enhanced React Integration
React developers will find substantial improvements to the integration experience:
* **Single Provider Pattern**: The `ParaProvider` now handles all configuration needs
* **Built-in Modal**: Modal functionality is now included within the provider by default
* **React Query Integration**: Leverages the power of TanStack Query for predictable state management
* **Powerful Hooks**: New purpose-built hooks that encapsulate common authentication and wallet operations
### Streamlined Wallet Management
Wallet operations have been refined for better developer experience:
* **Simplified Pregen Wallet API**: More intuitive API for creating and managing pregenerated wallets
* **Improved Wallet Type System**: Better TypeScript support with string union types replacing enums
* **Consistent Identity Representation**: New auth object pattern for representing user identities
### Cancellable Operations
Long-running operations now support cancellation, improving the user experience:
* **Interactive Polling**: Long-running operations support cancellation callbacks
* **Progress Events**: Operations can emit progress events for better UI feedback
* **Error Recovery**: Improved error handling for interrupted operations
## Developer Experience Improvements
Beyond the technical enhancements, Para v2.0 brings significant developer experience improvements:
* **Object Parameter Pattern**: All methods now use a consistent object parameter pattern, improving code readability and extensibility
* **Reduced Boilerplate**: Common authentication flows require significantly less code
* **Better TypeScript Support**: Enhanced type definitions and more precise typing
* **Simplified Configuration**: External wallet configuration is now consolidated in one place
## Looking Forward
Para v2.0 represents our commitment to continuously improving the developer experience. We welcome your feedback on these changes.
For detailed technical information on migrating your existing Para integration to v2.0, please refer to our
# Web Examples
Source: https://docs.getpara.com/v2/react/examples
Explore Para's web integration examples across popular frameworks
Para offers a comprehensive collection of web examples to help you integrate our technology across your preferred
frontend frameworks. Our examples-hub repository follows a consistent naming convention with folders structured as
(e.g., `with-react-vite` or `with-vue-vite`).
Each framework folder contains lightweight, standalone examples that focus on specific Para SDKs or features,
demonstrating minimal implementation requirements. These examples are designed to showcase individual capabilities with
clean, focused code that you can easily adapt to your specific application needs and use cases.
## Para Web Examples
Browse our web examples showcasing Para integration with popular frameworks:
## Signing Examples
Explore different signing implementations with Para:
## Wallet Pregeneration
Para also supports specialized web integration scenarios:
## Specialized Integrations
Discover how Para integrates with platform-specific web applications:
## Need Something Specific?
Don't see an example for your use case? Para's team is eager to create new examples to help you integrate with different libraries, third-party features, or providers.
# Balance Display
Source: https://docs.getpara.com/v2/react/guides/customization/balances
Learn how to configure and display balances in the Para Modal, including Aggregated and Custom Asset modes, and use the useProfileBalance hook for programmatic access.
The Para Modal provides comprehensive balance display capabilities, allowing users to view their wallet balances across multiple assets and networks. You can configure how balances are displayed and aggregated, and programmatically access balance data using the `useProfileBalance` hook.
## Balance Display Configuration
You can customize how balances are displayed and calculated through the `paraModalConfig.balances` property in your `ParaProvider` configuration. This setting will impact both the Para Modal's balance display and instances where you use the `useProfileBalance` hook.
Para supports two primary balance display modes, `AGGREGATED` and `CUSTOM_ASSET`. You can also specify whether to fetch balances for both `MAINNET_AND_TESTNET` (default) or one or the other only (`MAINNET` or `TESTNET`).
### Defining Custom Networks and Assets
To include custom assets when fetching connected wallet balances, you will need to supply implementation metadata for each asset, including:
* Each asset must include basic metadata, including `name`, `symbol`, and `logoUrl` (optional).
* Each asset must include an `implementations` array for each network you wish to query for balances.
* Each implementation object must include:
* Price data:
* **For a fixed price:**
* Include a `price` object with the asset's price in the format `{ value: number; currency: 'USD' }`.
* **For a volatile price:**
* Include a `priceUrl` string. This endpoint must respond to GET requests with a JSON object with the asset's current price in the format `{ value: number; currency: 'USD' }`.
* Network information:
* **For a custom EVM network:**
* Include a `network` object specifying the network's `name`, `evmChainId`, and an `rpcUrl` where asset balances can be queried.
* If the network is a testnet, include `isTestnet: true`.
* Include a `nativeTokenSymbol` if the network's native token is not Ethereum (ETH).
* Optional: Include a `logoUrl` for the network.
* Optional: Include an `explorer` object for the network, including:
* The explorer's display name `name`.
* The explorer's homepage URL `url`.
* An explorer URL template `txUrlFormat` for broadcast transactions, using `{HASH}` as the placeholder for the transaction hash. Example: `https://etherscan.io/tx/{HASH}`
* **If the asset is the network's native token:**
* No additional configuration is needed.
* **If the asset is an ERC-20 token:**
* Include a `contractAddress` string.
* **For a standard EVM or Solana network:**
* Include a `network` string, matching one of the networks enumerated in the `TNetwork` type. For example, `'ETHEREUM'` or `'SOLANA'`.
* Include a `contractAddress` string.
Refer to the code snippets below for example configurations.
### Aggregated Mode (Default)
Aggregated Mode automatically aggregates balances across all detected chains and assets and displays them in USD value. If you supply additional assets and price data, these totals will be included in the calculation.
The aggregated total will include most commonly used assets across many networks. If you want to only include totals from your customized tokens, set `excludeStandardAssets` to `true`.
An example configuration with additional assets is below:
```tsx theme={null}
',
// A price endpoint for the asset:
priceUrl: '',
implementations: [
// A custom EVM network where the asset is the native token:
{
network: {
name: 'Custom Chain',
evmChainId: '12345',
rpcUrl: 'https://rpc.customexplorer.com',
logoUrl: 'https://cdn.customexplorer.com/logo.png',
nativeTokenSymbol: 'CUSTOM',
explorer: {
name: 'Custom Chain Explorer',
url: 'https://customexplorer.com',
txUrlFormat: 'https://customexplorer.com/tx/{HASH}',
}
},
},
// A custom EVM network where the asset is an ERC-20 token:
{
network: {
name: 'Another Custom Chain',
evmChainId: '12345',
rpcUrl: '',
logoUrl: '',
explorer: {
name: 'Another Custom Chain Explorer',
url: 'https://anothercustomexplorer.com',
txUrlFormat: 'https://anothercustomexplorer.com/tx/{HASH}',
}
},
contractAddress: '0x...',
},
// An implementation of the token on Solana:
{
network: 'SOLANA',
contractAddress: 'Ep4r...',
},
// A testnet ERC-20 implementation of the token:
{
network: {
name: 'Custom Chain Testnet',
evmChainId: '12346',
rpcUrl: '',
logoUrl: '',
nativeTokenSymbol: 'CUSTOM',
explorer: {
name: 'Custom Chain Testnet Explorer',
url: 'https://testnet.customexplorer.com',
txUrlFormat: 'https://testnet.customexplorer.com/tx/{HASH}',
},
isTestnet: true,
},
contractAddress: '0x...',
},
],
},
{
name: 'Custom Stablecoin',
symbol: 'CSTABLE',
logoUrl: '',
// A fixed price for the asset
price: {
value: 1,
currency: 'USD',
},
networks: [
// A custom network where the asset is an ERC-20 token:
{
network: {
name: 'Custom Chain',
evmChainId: '12345',
rpcUrl: '',
logoUrl: '',
nativeTokenSymbol: 'CUSTOM',
explorer: {
name: 'Custom Chain Explorer',
url: 'https://customexplorer.com',
txUrlFormat: 'https://customexplorer.com/tx/{HASH}',
}
},
contractAddress: '0x...',
},
],
},
]
}
}}
>
{children}
```
### Custom Asset Mode
In Custom Asset Mode, the Para Modal will only display balances of a chosen asset, with no fiat currency conversion. This is ideal for cases where your app uses a particular token that may not have price information available.
Like in Aggregated Mode, you will need to supply implementation metadata for the asset, including any custom network definitions so that its balances can be queried for the session's connected wallets. However, in this mode, you do not need to include a price object or a price URL.
```tsx theme={null}
',
networks: [
// A custom network where the asset is the native token:
{
network: {
name: 'Custom Chain',
evmChainId: '12345',
rpcUrl: '',
logoUrl: '',
nativeTokenSymbol: 'CUSTOM',
explorer: {
name: 'Custom Chain Explorer',
url: 'https://customexplorer.com',
txUrlFormat: 'https://customexplorer.com/tx/{HASH}',
}
},
},
// A known network where the asset is an ERC-20 token:
{
contractAddress: '0x...',
network: 'ETHEREUM',
}
],
},
},
}}
>
{children}
```
## useProfileBalance Hook
The `useProfileBalance` hook allows you to query the current aggregated or custom asset balance of all wallets in the current session.
Balances are normally cached on the server for five minutes. You can supply a `refetchTrigger` to the hook to manually refetch balances when desired, using a unique number or string.
```tsx theme={null}
import { useProfileBalance } from "@getpara/react-sdk";
function BalanceDisplay() {
const [refetchTrigger, setRefetchTrigger] = useState(0);
const { data: profileBalance, isLoading, error } = useProfileBalance({
// Balances will be refetched whenever `refetchTrigger` changes
refetchTrigger,
});
if (isLoading) return
Loading balances...
;
if (error) return
Error loading balances: {error.message}
;
if (!profileBalance) return
No balance data available
;
return (
Total Balance: ${profileBalance.value.value.toFixed(2)}
);
}
```
Depending on the display type, the `ProfileBalance` object returned by `useProfileBalance` has the following structure:
```tsx theme={null}
{
value: {
value: 200,
currency: 'USD',
},
wallets: [
{
type: 'EVM',
address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: {
value: 200,
currency: 'USD',
},
assets: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'eth',
name: 'Ethereum',
symbol: 'ETH',
rpcUrl: 'https://eth.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/eth.png',
explorer: {
name: 'Etherscan',
url: 'https://etherscan.io',
txUrlFormat: 'https://etherscan.io/tx/{HASH}',
},
price: {
value: 4000,
currency: 'USD',
}
},
quantity: 0.03,
value: {
value: 120,
currency: 'USD',
},
networks: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'ethereum',
evmChainId: '1',
name: 'Ethereum',
rpcUrl: 'https://eth.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/eth.png',
explorer: {
name: 'Etherscan',
url: 'https://etherscan.io',
txUrlFormat: 'https://etherscan.io/tx/{HASH}',
}
},
quantity: 0.02,
value: {
value: 80,
currency: 'USD',
},
},
{
metadata: {
internalId: 'BASE',
zerionId: 'base',
evmChainId: '8453',
name: 'Base',
rpcUrl: 'https://base.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/base.png',
explorer: {
name: 'Basescan',
url: 'https://basescan.org',
txUrlFormat: 'https://basescan.org/tx/{HASH}',
}
},
quantity: 0.01,
value: {
value: 40,
currency: 'USD',
},
}
]
},
{
metadata: {
internalId: 'USDC',
zerionId: 'usdc',
name: 'USD Coin',
symbol: 'USDC',
price: {
value: 1,
currency: 'USD',
}
},
quantity: 80,
value: {
value: 80,
currency: 'USD',
},
networks: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'ethereum',
evmChainId: '1',
name: 'Ethereum',
rpcUrl: 'https://eth.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/eth.png',
explorer: {
name: 'Etherscan',
url: 'https://etherscan.io',
txUrlFormat: 'https://etherscan.io/tx/{HASH}',
}
},
quantity: 50,
value: {
value: 50,
currency: 'USD',
},
contractAddress: '0x...',
},
{
metadata: {
internalId: 'BASE',
zerionId: 'base',
evmChainId: '8453',
name: 'Base',
rpcUrl: 'https://base.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/base.png',
explorer: {
name: 'Basescan',
url: 'https://basescan.org',
txUrlFormat: 'https://basescan.org/tx/{HASH}',
}
},
quantity: 20,
value: {
value: 20,
currency: 'USD',
},
contractAddress: '0x...',
},
{
metadata: {
internalId: 'SOLANA',
name: 'Solana',
},
quantity: 10,
value: {
value: 10,
currency: 'USD',
},
contractAddress: '0x...',
}
]
}
],
networks: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'ethereum',
evmChainId: '1',
name: 'Ethereum',
},
value: {
value: 130,
currency: 'USD',
},
assets: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'ethereum',
evmChainId: '1',
name: 'Ethereum',
symbol: 'ETH',
rpcUrl: 'https://eth.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/eth.png',
explorer: {
name: 'Etherscan',
url: 'https://etherscan.io',
txUrlFormat: 'https://etherscan.io/tx/{HASH}',
}
},
quantity: 0.02,
value: {
value: 80,
currency: 'USD',
},
contractAddress: '0x...',
},
{
metadata: {
internalId: 'USDC',
zerionId: 'usdc',
name: 'USD Coin',
symbol: 'USDC',
price: {
value: 1,
currency: 'USD',
},
},
quantity: 50,
value: {
value: 50,
currency: 'USD',
},
contractAddress: '0x...',
}
]
},
{
metadata: {
internalId: 'BASE',
zerionId: 'base',
evmChainId: '8453',
name: 'Base',
logoUrl: 'https://cdn.zerion.io/base.png',
rpcUrl: 'https://base.llamarpc.com',
explorer: {
name: 'Basescan',
url: 'https://basescan.org',
txUrlFormat: 'https://basescan.org/tx/{HASH}',
}
},
value: {
value: 60,
currency: 'USD',
},
assets: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'eth',
evmChainId: '1',
name: 'Ethereum',
logoUrl: 'https://cdn.zerion.io/eth.png',
},
quantity: 0.01,
value: {
value: 40,
currency: 'USD',
},
contractAddress: '0x...',
},
{
metadata: {
internalId: 'USDC',
zerionId: 'usdc',
name: 'USD Coin',
symbol: 'USDC',
logoUrl: 'https://cdn.zerion.io/usdc.png',
price: {
value: 1,
currency: 'USD',
},
},
quantity: 20,
value: {
value: 20,
currency: 'USD',
},
contractAddress: '0x...',
}
]
},
{
metadata: {
internalId: 'SOLANA',
name: 'Solana',
rpcUrl: 'https://api.mainnet-beta.solana.com',
logoUrl: 'https://cdn.zerion.io/solana.png',
explorer: {
name: 'Solana Explorer',
url: 'https://explorer.solana.com/',
txUrlFormat: 'https://explorer.solana.com/tx/{HASH}',
}
},
value: {
value: 10,
currency: 'USD',
},
assets: [
{
metadata: {
internalId: 'USDC',
zerionId: 'usdc',
name: 'USD Coin',
symbol: 'USDC',
price: {
value: 1,
currency: 'USD',
},
},
quantity: 10,
value: {
value: 10,
currency: 'USD',
},
contractAddress: '0x...',
}
]
}
]
}
]
}
```
```tsx theme={null}
{
wallets: [
{
type: 'EVM',
address: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
assets: [
{
metadata: {
name: 'Custom Asset',
symbol: 'CUSTOM',
customAssetId: 'custom_CUSTOM',
logoUrl: '',
},
quantity: 1.5,
networks: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'ethereum',
name: 'Ethereum',
evmChainId: '1',
rpcUrl: 'https://eth.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/eth.png',
explorer: {
name: 'Etherscan',
url: 'https://etherscan.io',
txUrlFormat: 'https://etherscan.io/tx/{HASH}',
}
},
quantity: 1.0,
contractAddress: '0x...',
},
{
metadata: {
customNetworkId: 'custom_12345',
name: 'Custom Chain',
evmChainId: '12345',
rpcUrl: 'https://rpc.customexplorer.com',
logoUrl: 'https://cdn.customexplorer.com/logo.png',
nativeTokenSymbol: 'CUSTOM',
explorer: {
name: 'Custom Chain Explorer',
url: 'https://customexplorer.com',
txUrlFormat: 'https://customexplorer.com/tx/{HASH}',
}
},
quantity: 0.5,
contractAddress: '0x...',
}
]
}
],
networks: [
{
metadata: {
internalId: 'ETHEREUM',
zerionId: 'ethereum',
name: 'Ethereum',
evmChainId: '1',
rpcUrl: 'https://eth.llamarpc.com',
logoUrl: 'https://cdn.zerion.io/eth.png',
explorer: {
name: 'Etherscan',
url: 'https://etherscan.io',
txUrlFormat: 'https://etherscan.io/tx/{HASH}',
}
},
assets: [
{
metadata: {
name: 'Custom Asset',
symbol: 'CUSTOM',
customAssetId: 'custom_CUSTOM',
},
quantity: 1.5,
contractAddress: '0x...',
}
]
},
{
metadata: {
customNetworkId: 'custom_12345',
name: 'Custom Chain',
evmChainId: '12345',
rpcUrl: 'https://rpc.customexplorer.com',
logoUrl: 'https://cdn.customexplorer.com/logo.png',
explorer: {
name: 'Custom Chain Explorer',
url: 'https://customexplorer.com',
txUrlFormat: 'https://customexplorer.com/tx/{HASH}',
}
},
assets: [
{
metadata: {
name: 'Custom Asset',
symbol: 'CUSTOM',
customAssetId: 'custom_CUSTOM',
},
quantity: 0.5,
contractAddress: '0x...',
}
]
}
]
}
]
}
```
# Developer Portal Email Branding
Source: https://docs.getpara.com/v2/react/guides/customization/developer-portal-email-branding
Customize the appearance of Welcome and OTP emails with your brand.
Customize the visual appearance of Welcome and OTP emails sent to your users during authentication. These branding settings apply specifically to automated emails, not your Para modal interface. If you're looking to customize the modal appearance, refer to the .
## Brand Colors
Configure your brand colors to match your company's visual identity in all email communications.
### Color Settings
* **Foreground**: Primary brand color for buttons, links, and highlights
* **Background**: Background color for email content areas
* **Accent (Optional)**: Secondary color for borders and accents
- Use your primary brand color for the foreground to maintain consistency
- Ensure sufficient contrast between foreground and background colors
## Typography
Select fonts that align with your brand identity and ensure readability across email clients.
## Logo Assets
Upload your company's visual assets to personalize email headers and branding.
* **Icon**: 80px × 80px square format for email headers
* **Logo**: Maximum width 200px for email signatures and footers
* **Format**: PNG or SVG recommended for best quality
* **Background**: Transparent backgrounds work best
## Social Links
Add your company's social media and website links to email footers. These links will appear in all automated emails, providing users with easy access to your online presence.
### Available Links
* **Website URL**: Your company's main website
* **X/Twitter Profile**: Twitter or X profile link
* **LinkedIn Profile**: Company or founder LinkedIn page
* **GitHub Profile**: Company or project GitHub repository
## Email Impact
These branding settings specifically affect:
### Welcome Emails
* Sent to new users during initial registration
* Display your logo, brand colors, and company links
* Include verification links with your custom styling
### OTP Emails
* Sent during two-factor authentication
* Use your brand colors and typography
* Include your logo for brand recognition
Email branding settings do NOT affect your Para modal interface. For modal customization, see the .
## Next Steps
# Developer Portal Payment Integration
Source: https://docs.getpara.com/v2/react/guides/customization/developer-portal-payments
Configure payment providers and transaction features for crypto onramps and offramp.
Configure payment providers and transaction features to enable crypto buying, selling, and wallet funding for your users. These settings control which payment options appear in your Para integration.
These configurations will show up in the Para modal interface post user authentication. You can open t he `ParaModal` at anytime in your application to allow users to interact with these payment features.
## Buy Crypto Configuration
Enable and configure crypto purchasing options for your users.
### Enable Buy Crypto
Toggle the buy crypto feature to allow users to purchase cryptocurrency directly through your application.
### Asset Selection
Choose which cryptocurrencies users can purchase:
* **All Available Assets**: Enable all supported cryptocurrencies
* **Custom Selection**: Limit to specific assets that match your application's needs
### Default Asset Configuration
Set the default cryptocurrency and amount that pre-populates when users initiate a purchase:
1. **Select Asset**: Choose from your enabled cryptocurrency list
2. **Default Amount**: Set a suggested purchase amount (e.g., \$50.00)
3. **Status**: Enable or disable the default asset feature
## Crypto Withdrawal (Offramp)
Allow users to sell cryptocurrency for fiat currency.
## Payment Provider Configuration
Configure which payment providers appear in your application's onramp interface.
Click and drag to reorder payment providers. The order determines which provider appears first in the user interface.
### Available Providers
* **Stripe**: Credit/debit card payments with global support
* **Ramp**: Comprehensive fiat onramp with bank transfers and cards
* **Moonpay**: Multi-currency support with global payment methods
### Provider Priority
Payment providers display in the order shown. Users will see enabled providers as options when purchasing cryptocurrency, with the first enabled provider typically appearing as the default.
### Integration Requirements
Each payment provider requires separate integration setup:
1. **Stripe**: No API Key Required, just enable **Stripe** in the Developer Portal.
2. **Ramp**: First complete onboarding and KYB with Ramp, then enter your Ramp API Key in the Developer Portal.
3. **Moonpay**: No API Key Required, just enable **Moonpay** in the Developer Portal.
Ramp requires a Ramp production api key. Learn more at
## Receive Funds Feature
Enable wallet address display and QR code generation for receiving cryptocurrency. When enabled, users can receive funds directly into their wallets by sharing their wallet addresses or QR codes.
## Next Steps
# Developer Portal Security Settings
Source: https://docs.getpara.com/v2/react/guides/customization/developer-portal-security
Configure authentication methods, session management, and transaction permissions
Configure security settings to control user authentication methods, session duration, and transaction permissions. These settings balance security with user experience for your Para integration.
## Authentication Methods
Configure which authentication methods your users can use to access their wallets.
This configuration does not affect Mobile SDK frameworks. Mobile SDKs use native mobile passkeys with an option to use passwords via Webviews.
### One Click Login
**Available since Alpha 2.0 v68**\
The most seamless authentication experience for users:
* **Instant Wallets**: Create a real, non-custodial wallet the moment a user inputs their email or social login
* **Fast Performance**: Wallets created in seconds
* **Progressive Onboarding**: Passkeys or other auth can be enforced later
### Passkeys
Enable passkey authentication for enhanced security and user experience:
* **Biometric Authentication**: Fingerprint, Face ID, or Windows Hello
* **Hardware Security Keys**: FIDO2-compatible security keys
* **Platform Authenticators**: Built-in device authenticators
### Passwords
Configure traditional password authentication:
* **Password Requirements**: Set complexity requirements
* **Recovery Options**: Enable password reset functionality
* **Multi-Factor Authentication**: Optional additional security layer
### PINs
Enable simple PIN-based authentication for quick access:
* **4 Digit Codes:** Users can create a short numeric code, familiar from mobile banking and device unlock screens
– **Flexibility:** Allow PINs as the primary method or alongside passkey/passwords so users can choose whatever feels easiest
– **Legacy Devices:** Provides a lightweight option on devices that don't support passkeys
You can enable both passkeys and passwords to give users choice:
* **Passkeys Preferred**: Passkeys will be suggested first during registration
* **Fallback Support**: Users can choose passwords if passkeys aren't available
If you're creating a PWA we recommend using passwords so that the user stays within the installed app. Passkeys will open a new browser tab.
## Session Management
Control how long users stay authenticated before requiring re-authentication.
### Security Considerations
**Shorter Sessions (2 Hours - 1 Day):**
* Enhanced security for sensitive applications
* Reduced risk if device is compromised
* Better for shared or public devices
**Longer Sessions (1 Week - 1 Month):**
* Improved user experience with fewer logins
* Better for personal devices and trusted environments
* Consider implementing automatic session refresh
### Custom Session Length
For custom durations:
1. Select "Custom" option
2. Enter duration in minutes
3. Consider your application's specific security needs
4. Balance security with user experience
## Transaction Permissions
Configure how users confirm transactions and manage permissions.
### Transaction Pop-ups
Control whether users see confirmation dialogs for transactions:
* **Enabled**: Users manually confirm each transaction via popup
* **Disabled**: Transactions proceed without additional confirmation
By default, transaction pop-ups are disabled allow for faster transaction signing. We recommend adding careful transaction validation in your application logic to ensure users are aware of the transactions they are signing.
# Developer Portal Setup
Source: https://docs.getpara.com/v2/react/guides/customization/developer-portal-setup
Configure your Para integration through the developer portal with environment settings, network configuration, and domain management.
Configure your Para integration through the developer portal at . This portal allows you to manage your application's settings, including environment configurations, supported networks, and security options.
## Environment Configuration
Set up your development environment to match your application's framework and package manager preferences. This will provide you with tailored integration examples and installation commands or visit the for a general overview.
### Framework Selection
Choose your primary framework to receive tailored integration examples:
* **React**: For React applications and Next.js projects
* **Vue**: For Vue.js applications and Nuxt projects
* **Svelte**: For Svelte and SvelteKit applications
* **React Native**: For mobile applications
* **Flutter**: For cross-platform mobile development
### Package Manager
Select your preferred package manager for installation commands:
* **npm**: Default Node.js package manager
* **yarn**: Alternative package manager with workspace support
* **pnpm**: Fast, disk space efficient package manager
## Network Configuration
Configure which blockchain networks your application supports and require users to connect.
This will configure which wallet options are available during user authentication. Your app will receive wallets for each required network.
### Available Networks
* **Ethereum (EVM)**: Includes Ethereum Layer 2 networks
* **Solana**: Solana blockchain network
* **Cosmos**: Cosmos ecosystem networks
### Network Requirements
Toggle "Required" for networks where users must connect at least one wallet of that type during login. This ensures users have the necessary wallet connections for your application's functionality.
## Domain Security
Configure allowed origins to secure your API requests and prevent unauthorized access.
This prevents unauthorized domains from making API requests to your Para integration. Allowed Origins are only supported for web integrations, not server or mobile.
### Origin URLs
Add your application's domains to the allowed origins list:
```
www.yourapp.com, app.yourapp.com, localhost:3000
```
### Security Best Practices
* Add only necessary domains to minimize attack surface
* Include all environments (development, staging, production)
* Separate multiple domains with commas
* Use HTTPS in production environments
## Email Configuration
Configure email settings for user verification and communication.
Configure the theming of emails sent to your users on the page.
### Verification URL
Set the redirect URL displayed in verification emails that users receive. This should point to your application where users complete the authentication process.
### Email Types
Choose which emails Para sends to your users:
* **Welcome Email Only**: Send only welcome emails to new users
* **Welcome Email + Backup Kit**: Include backup kit instructions for new wallets
* **No Email**: Disable all automated emails (except OTP emails)
## Next Steps
# Export Private Key
Source: https://docs.getpara.com/v2/react/guides/customization/export-private-key
You can allow your users to export the private key for their wallets if they choose, letting them take custody over their assets..
## Integration Methods
The `useExportPrivateKey` hook will automatically open a popup window where your user can reauthenticate their session and then view and copy the private key for one of their connected wallets.
The hook can only be used from within your app's `ParaProvider` context. By default, the key exported will be that for the currently selected wallet, available from the `useWallet` or `useWalletState` hooks.
```tsx App.tsx theme={null}
import { useExportPrivateKey, useWallet} from "@getpara/react-sdk";
export function App() {
const { data: activeWallet } = useWallet();
const { mutate: exportPrivateKey, isPending } = useExportPrivateKey();
return (
);
}
```
If you are using a custom UI implementation or you would like to control how the portal URL is managed, you can use the client's `exportPrivateKey` method directly.
```tsx AppContent.tsx theme={null}
import { useClient, useWallet} from "@getpara/react-sdk";
export function App() {
const para = useClient();
const { data: activeWallet } = useWallet();
const [isPending, setIsPending] = useState(false);
return (
);
}
```
## Limitations
Currently, private key export is only available for embedded EVM or Cosmos wallets which are not pregenerated or guest mode wallets.
The key exported will work as either an EVM or Cosmos private key, depending on what wallet it is imported into.
# Guest Mode
Source: https://docs.getpara.com/v2/react/guides/customization/guest-mode
Allow users to use your app via guest wallets without signing up.
Guest Mode allows you to provision one or more wallets for a new user so they can start using your app without going through the sign-up process.
With Guest Mode enabled, you can offer an option in the Para Modal for your users to get started with a guest account without signing up. When users eventually sign up, any previously created guest wallets will immediately be linked to their account.
## Integration Methods
There are two primary ways to implement Guest Mode:
The easiest way to implement Guest Mode is via the Para Modal. Simply set the corresponding configuration setting (`isGuestModeEnabled`) in your `ParaProvider` configuration to `true`.
This setting adds a "Continue as Guest" option to the modal sign-in screen, which closes the modal and performs wallet setup in the background. If the modal is reopened, guest users will see a special version of the account screen, from which they can proceed to finish signing up and then claim their existing wallets.
```tsx App.tsx {13} theme={null}
import { ParaProvider, Environment } from "@getpara/react-sdk";
function App() {
return (
{
console.log('Guest wallets created!', event.detail);
}
}}
>
);
}
```
If you are using a custom UI implementation or you would like to enter Guest Mode before your user opens the Para Modal, you can use the `useCreateGuestWallets` hook to create guest wallets programmatically:
```tsx AppContent.tsx theme={null}
import { useCreateGuestWallets } from "@getpara/react-sdk";
// This component must be wrapped within a `ParaProvider` to function properly
function AppContent() {
const { createGuestWallets, isPending, isError, error } = useCreateGuestWallets();
const onClickGuestLoginButton = () => {
createGuestWallets(
undefined,
{
onSuccess: (wallets) => {
console.log('Guest wallets created, app is now in Guest Mode:', wallets);
},
onError: (error) => {
console.error('Error creating guest wallets:', error);
},
onSettled: () => {
console.log('Guest wallets creation process settled.');
}
}
)
};
// ...
}
```
### Tracking Guest Wallet Creation
Whether you use the modal or a custom solution, you can monitor and reflect guest wallet creation status by using the `useCreateGuestWalletsState` hook. For example, you will likely want to block any signing-related interface actions until the wallets have been created.
```tsx AppContent.tsx theme={null}
import { useCreateGuestWalletsState } from "@getpara/react-sdk";
function AppContent() {
const { isPending: isCreatingGuestWallets, error } = useCreateGuestWalletsState();
if (error) {
console.error('Error creating guest wallets:', error);
return
Error creating guest wallets.
;
}
return (
{isCreatingGuestWallets ? (
Creating guest wallets...
) : (
Guest wallets created successfully!
)}
)
}
```
Due to implementation details of React Query, the `useCreateGuestWallets` hook will not reliably reflect the status of the Para Modal's guest wallet creation process. To monitor the modal operation's status from the rest of your app, you *must* instead use `useCreateGuestWalletsState`. The hook's return type resembles React Query's `UseMutationReturnType` type and has most of the same fields.
## Limitations
Currently, guest wallets are prevented from buying or selling crypto through the integrated onramp providers. If a guest wallet is funded, a message is presented to the user in the Para Modal account screen encouraging them to sign up and retain access to their funds. You are, of course, free to fund guest wallets if you desire, but note that you must be careful to maintain user access to the wallets to ensure no funds are lost.
We recommend using `getUserShare` and `setUserShare` to save and restore the user share for a guest wallet, just as you would for a pregenerated wallet.
# Modal Customization
Source: https://docs.getpara.com/v2/react/guides/customization/modal
Learn how to customize the appearance of the Para modal to match your application's design.
The Para Modal provides extensive customization options through the `paraModalConfig` prop passed to the `ParaProvider`. You can customize everything from colors and themes to authentication flows and security features.
## Basic Setup
Pass the `paraModalConfig` object to your `ParaProvider` to customize the modal:
```tsx theme={null}
{children}
```
## Configuration Options
A full list of available configuration options for the `paraModalConfig` prop available on the `ParaProvider` component:
## Logo Configuration
```tsx theme={null}
paraModalConfig={{
logo: "https://yourdomain.com/logo.png"
}}
```
For optimal display, use a logo image with dimensions of 372px × 160px.
## Authentication Options
### OAuth Methods
```tsx theme={null}
paraModalConfig={{
oAuthMethods: ["GOOGLE", "TWITTER", "DISCORD", "APPLE"]
}}
```
Available OAuth providers:
* `GOOGLE` - Google OAuth
* `TWITTER` - Twitter/X OAuth
* `APPLE` - Apple OAuth
* `DISCORD` - Discord OAuth
* `FACEBOOK` - Facebook OAuth
* `FARCASTER` - Farcaster OAuth
* `TELEGRAM` - Telegram OAuth
### Email and Phone Login
```tsx theme={null}
paraModalConfig={{
disableEmailLogin: false,
disablePhoneLogin: true, // Only allow email and OAuth
oAuthMethods: ["GOOGLE", "TWITTER"]
}}
```
### Default Authentication Identifier
```tsx theme={null}
paraModalConfig={{
defaultAuthIdentifier: "user@example.com" // or "+15555555555"
}}
```
Phone numbers should be in international format: `+15555555555`
## Authentication Layout
```tsx theme={null}
paraModalConfig={{
authLayout: ["AUTH:CONDENSED", "EXTERNAL:FULL"]
}}
```
Available layout options:
* `AUTH:FULL` - Full authentication component
* `AUTH:CONDENSED` - Condensed authentication component
* `EXTERNAL:FULL` - Full external wallet component
* `EXTERNAL:CONDENSED` - Condensed external wallet component
Use our to visualize different layout configurations before implementing them.
## Step Override
```tsx theme={null}
paraModalConfig={{
currentStepOverride: "ACCOUNT_MAIN" // or "account_main"
}}
```
Authentication Steps:
* `AUTH_MAIN` - Main authentication options
* `AUTH_MORE` - Additional authentication methods
* `AWAITING_OAUTH` - OAuth authentication in progress
* `VERIFICATIONS` - Email/phone verification
Wallet Creation Steps:
* `BIOMETRIC_CREATION` - Biometric setup
* `PASSWORD_CREATION` - Password creation
* `SECRET` - Recovery secret display
* `AWAITING_WALLET_CREATION` - Wallet creation in progress
Account Management Steps:
* `ACCOUNT_MAIN` - Main account view
* `ACCOUNT_PROFILE` - Profile management
* `CHAIN_SWITCH` - Network selection
External Wallet Steps:
* `EX_WALLET_MORE` - External wallet options
* `EX_WALLET_SELECTED` - Selected external wallet
Funds Management Steps:
* `ADD_FUNDS_BUY` - Buy crypto interface
* `ADD_FUNDS_RECEIVE` - Receive crypto interface
* `ADD_FUNDS_WITHDRAW` - Withdraw crypto interface
Security Steps:
* `SETUP_2FA` - Two-factor authentication setup
* `VERIFY_2FA` - Two-factor authentication verification
Setting an invalid step or a step that requires previous steps to be completed may cause unexpected behavior. Ensure the step override makes sense in your authentication flow.
## Security Features
### Two-Factor Authentication
### Recovery Secret
```tsx theme={null}
paraModalConfig={{
twoFactorAuthEnabled: true,
recoverySecretStepEnabled: true
}}
```
## Guest Mode
```tsx theme={null}
paraModalConfig={{
isGuestModeEnabled: true
}}
```
## Theme Configuration
### Basic Theme Example
```tsx theme={null}
paraModalConfig={{
theme: {
foregroundColor: "#333333",
backgroundColor: "#FFFFFF",
accentColor: "#007AFF",
mode: "light",
borderRadius: "md",
font: "Arial, sans-serif"
}
}}
```
### Advanced Theme with Custom Palette
```tsx theme={null}
paraModalConfig={{
theme: {
foregroundColor: "#333333",
backgroundColor: "#FFFFFF",
accentColor: "#007AFF",
mode: "light",
customPalette: {
text: {
primary: "#333333",
secondary: "#666666",
subtle: "#999999",
inverted: "#FFFFFF",
error: "#FF3B30"
},
modal: {
surface: {
main: "#FFFFFF",
footer: "#F2F2F7"
},
border: "#E5E5EA"
},
button: {
primary: {
background: "#007AFF",
hover: "#0056CC",
text: "#FFFFFF"
}
}
}
}
}}
```
Set the `mode` correctly based on your background color to ensure all components remain visible and accessible.
You can use custom fonts by importing them in your global CSS and specifying the font family in the `font` property.
## Account Linking
```tsx theme={null}
paraModalConfig={{
supportedAccountLinks: [
"EMAIL",
"PHONE",
"GOOGLE",
"TWITTER",
"EXTERNAL_WALLET"
]
}}
```
## Event Callbacks
### Modal Step Changes
### Modal Close
```tsx theme={null}
paraModalConfig={{
onModalStepChange: (stepInfo) => {
console.log('Modal step changed:', stepInfo);
},
onClose: () => {
console.log('Modal closed');
}
}}
```
## Password & PIN Screen Theme Limitations
Password and PIN authentication screens are rendered in an iframe and use the Developer Portal theme settings, not your `paraModalConfig.theme`. This means:
* Theme colors may differ between the modal and password screens if configurations don't match
* Dynamic theme changes at runtime won't affect the iframe
* You must configure matching themes in both your code and the Developer Portal for consistency
## Advanced Configuration
### Custom Modal Behavior
### On-Ramp Configuration
## Complete Example
Here's a comprehensive example showcasing multiple configuration options:
```tsx theme={null}
{
console.log('Step changed:', stepInfo);
},
onClose: () => {
console.log('Modal closed');
}
}}
>
{children}
```
## Examples and Tools
Test your modal configuration with our interactive tools:
## Next Steps
Now that you've configured your Para modal, explore additional customization options:
# Cosmos Wallets
Source: https://docs.getpara.com/v2/react/guides/external-wallets/cosmos
Learn how to combine the Para Modal with Cosmos wallets.
This guide will walk you through the process of integrating Cosmos Wallets into your Para Modal and Para-enabled
application, allowing you to onboard new users and connect with existing users who may already have external wallets
like Keplr, Leap, Cosmostation and more.
## Prerequisites
Before integrating wallet connections, ensure you have an existing Para project with the Para Modal set up. If you
haven't set up Para yet, follow one of our Framework Setup guides like this guide.
### Setting up Cosmos Wallets
Para integrates with leading Cosmos wallets. Our integration leverages a modified fork of the React library.
#### Supported Wallets
Para supports the following Cosmos wallets:
* - A secure and user-friendly wallet for the Cosmos ecosystem
* - The interchain wallet for the Cosmos ecosystem
* - A comprehensive wallet and validator operator for Cosmos SDK-based blockchains
Import the necessary wallet connectors and chain configurations:
```typescript main.tsx theme={null}
import { useState } from "react";
import {
ParaProvider,
ExternalWallet,
} from "@getpara/react-sdk";
import { cosmoshub, osmosis } from "graz/chains";
import { type ChainInfo } from "keplr-wallet/types";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
```
If you encounter type issues with `graz/chains`, you'll need to generate the chain files. You can:
1. Add a postinstall script to your package.json:
```json theme={null}
{
"scripts": {
"postinstall": "graz --generate"
}
}
```
2. Or run `npx graz --generate` manually after installation
Set up your Cosmos chain configurations with the necessary RPC and REST endpoints. You can extend the existing chain configurations with your custom endpoints:
```typescript main.tsx theme={null}
const cosmosChains: ChainInfo[] = [
{
...cosmoshub,
rpc: "https://rpc.cosmos.directory/cosmoshub", // Replace with your RPC endpoint
rest: "https://rest.cosmos.directory/cosmoshub", // Replace with your REST endpoint
},
{
...osmosis,
rpc: "https://rpc.cosmos.directory/osmosis",
rest: "https://rest.cosmos.directory/osmosis",
},
];
```
Similar to the Cosmos and Solana providers, configure the `ParaProvider` component by wrapping your application content in the `ParaProvider` component. Pass in the required configuration props:
```typescript main.tsx theme={null}
export const App = () => {
const [chainId, setChainId] = useState(cosmoshub.chainId);
return (
{
setChainId({ selectedChainId: chainId });
},
chains: cosmosChains,
},
// grazProviderProps={}
},
walletConnect: {
projectId: 'your_walletconnect_project_id',
},
}>{
/* Your app content */}
);
};
```
For the ParaCosmosProvider wrapping you don't need to include a QueryClientProvider as the provider already includes
one.
### External Wallet Configuration
The `ParaProvider` is built on top of
and supports all of its configuration options.
## External Wallets with Linked Embedded Wallets
You can also provision linked embedded wallets for external wallets.
In this case, the external wallet would be the Para auth method for the user’s embedded wallet (instead of an email or social login). Embedded wallets would be created according to your API key settings.
To enable this, you can include the `createLinkedEmbeddedForExternalWallets` prop to indicate which external wallets this setting should be applied to.
## Advanced Provider Pattern
Setting up a dedicated provider component that encapsulates all the necessary providers and modal state management is
considered a best practice. This pattern makes it easier to manage the modal state globally and handle session
management throughout your application.
### Server-Side Rendering Considerations
When using Next.js or other SSR frameworks, proper client-side initialization is crucial since web3 functionality relies
on browser APIs. There are two main approaches:
1. Using the `'use client'` directive in Next.js 13+:
* Add the directive at the component level where browser APIs are needed
* Ensures the CosmosProvider component and its dependencies only run on the client
* Maintains better code splitting and page performance
2. Using dynamic imports:
* Lazily loads the provider component
* Automatically handles client-side only code
* Provides fallback options during loading
## Configuring the Para Modal
After setting up your providers you need to configure the ParaModal component to display the external wallets and
authentication options to your users. You need to pass in the `externalWallets` and `authLayout` configuration options
to the ParaModal component to control which of the wallets show in the modal that were specified in the provider
configuration.
### Set the modal props
```typescript theme={null}
paraModalConfig={{
authLayout: ["AUTH_FULL","EXTERNAL_FULL"]
theme: {
mode: "light",
foregroundColor: "#000000",
backgroundColor: "#FFFFFF",
accentColor: "#007AFF"
}
logo: yourLogoUrl
// ... other modal config
}}
```
#### Modal Props Config
Modal prop options for customizing the Para Modal are included below. For advanced customization options, refer to
.
## External Wallet Verification
External wallet verification adds a verification step during external connection to ensure the user owns the wallet.
Enabling this feature establishes a valid Para session, which you can later use in your app to securely validate wallet ownership.
To enable this, set the following option on your `externalWalletConfig` of your `ParaProvider`:
```
externalWalletConfig={{
includeWalletVerification: true,
...REST_OF_CONFIG
}}
```
## Connection Only Wallets
Connection only external wallets bypass all Para functionality (account creation, user tracking, etc.) when connecting an external wallet. To enable this, set the following option on your `externalWalletConfig` of your `ParaProvider`:
```
externalWalletConfig={{
connectionOnly: true,
...REST_OF_CONFIG
}}
```
Since connection only wallets bypass Para, most Para functionality will be unavailable. This includes linked embedded wallets, external wallet verification, on & off ramping, etc.
## Examples
For an example of what the Para External Wallets Modal might look like in your application, check out our live demo:
For an example code implementation using Cosmos Wallets, check out our GitHub repository:
## Next Steps
Now that you have integrated Cosmos wallets into your Para Modal, you can explore more advanced features like signing
using the Para SDK with popular libraries like `CosmJS`.
# EVM Wallets
Source: https://docs.getpara.com/v2/react/guides/external-wallets/evm
Learn how to combine the Para Modal with EVM wallets.
This guide will walk you through the process of integrating EVM Wallets into your Para Modal and Para-enabled
application, allowing you to onboard new users and connect with existing users who may already have external wallets
like MetaMask, Coinbase Wallet and more.
## Prerequisites
Before integrating wallet connections, ensure you have an existing Para project with the Para Modal set up. If you
haven't set up Para yet, follow one of our Framework Setup guides like this guide.
## How It Works
Para's EVM wallet integration is powered by , the popular React hooks library for Ethereum. When you configure the `ParaProvider` with external wallet support, Para automatically creates and manages the Wagmi provider internally. This means:
* **No manual Wagmi setup required** - Para handles all Wagmi provider configuration
* **All Wagmi hooks available** - Use any Wagmi hook in your application alongside Para hooks
* **Unified configuration** - Configure chains and settings through Para's `externalWalletConfig`
* **Automatic connector management** - Para controls wallet connectors based on your configuration
## Setting up EVM Wallets
Setup is simple - just wrap your app in a provider and pass the appropriate props and configuration options to the
provider. Once configured, the Para modal and wallet options will automatically appear in the modal when opened.
Para provides seamless integration with popular EVM wallets including
, , , , , , , , , , and .
**Safe App Registration**: To use Safe as an external wallet, you'll need to register your application as a Safe App in the . This is required because Safe apps need to run within Safe's context to ensure proper security and functionality. The registration process involves providing your app's details and undergoing a review by the Safe team.
### Import components
Import the wallet connectors and supporting components you need. Adjust the imports based on which wallets you want to support:
```typescript main.tsx theme={null}
import {
ParaProvider,
ExternalWallet,
} from "@getpara/react-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { sepolia, celo, mainnet, polygon } from "wagmi/chains";
```
### Configure the providers
Configure the `ParaProvider` component by wrapping your application content in the `QueryClientProvider` and `ParaProvider` components. Pass in the required configuration props:
```typescript main.tsx theme={null}
const queryClient = new QueryClient();
export const App = () => {
return (
{/* Your app content */}
);
};
```
The `ParaProvider` automatically creates and manages the Wagmi provider internally. You only need to provide the `QueryClientProvider` - Para handles all Wagmi setup for you.
### External Wallet Configuration
**WalletConnect (Reown) Setup:** You'll need a WalletConnect project ID if you're using their connector. Get one from
the .
You can use an empty string for testing, but this isn't recommended for production.
The `ParaProvider` extends Wagmi's provider functionality, giving you access to all in your application. Place the provider near the root of your component tree for optimal performance.
## Accessing the Wagmi Configuration
Para provides two methods to access the Wagmi configuration depending on your use case:
### During Runtime (Inside ParaProvider)
Use the `getWagmiConfig` function when you need the configuration inside the ParaProvider context but outside of React hooks:
```typescript theme={null}
import { getWagmiConfig } from "@getpara/evm-wallet-connectors";
// Use anywhere inside ParaProvider context
const wagmiConfig = getWagmiConfig();
// Now you can use with @wagmi/core actions
import { getAccount } from '@wagmi/core'
const account = getAccount(wagmiConfig)
```
### Before ParaProvider Initialization
If you need the Wagmi config before the ParaProvider mounts (e.g., for server-side operations or early initialization), use `createParaWagmiConfig`:
```typescript theme={null}
import { createParaWagmiConfig } from "@getpara/evm-wallet-connectors";
import ParaWeb from "@getpara/react-sdk";
// Initialize your Para client
// IMPORTANT: This client MUST be provided to the paraClientConfig object of the ParaProvider!
const para = new ParaWeb(YOUR_ENV, YOUR_API_KEY)
// Initialize config early
const wagmiConfig = createParaWagmiConfig(para, {
chains: [mainnet, polygon],
// ... other wagmi config options
});
// ParaProvider will automatically use this pre-created config
// when it mounts later
```
The `createParaWagmiConfig` function creates the same configuration that ParaProvider would create internally. This enables you to use the config with `@wagmi/core` actions before your React app renders.
## Server-Side Rendering (SSR) Considerations
When using Next.js or other SSR frameworks, proper client-side initialization is crucial since web3 functionality relies on browser APIs. **SSR management is the developer's responsibility**, and Para provides the tools to handle it effectively.
### Handling Hydration Issues
If you encounter hydration errors related to `@getpara/evm-wallet-connectors`, this indicates that Wagmi's store hydration is happening too eagerly. Since Para uses Wagmi internally, all apply:
1. **Enable SSR mode** in your configuration:
```typescript theme={null}
externalWalletConfig={{
evmConnector: {
config: {
chains: [mainnet],
ssr: true, // Enable SSR support
},
},
// ... rest of config
}}
```
2. **Use dynamic imports** for client-side only rendering:
```typescript theme={null}
import dynamic from 'next/dynamic'
const ParaProvider = dynamic(
() => import('@getpara/react-sdk').then(mod => mod.ParaProvider),
{ ssr: false }
)
```
3. **Add the `'use client'` directive** in Next.js 13+:
```typescript theme={null}
'use client'
import { ParaProvider } from '@getpara/react-sdk'
export function Providers({ children }) {
// Provider implementation
}
```
### Cookie-Based Persistence
For advanced SSR scenarios where you want to persist wallet connection state across server renders, implement :
```typescript theme={null}
import { cookieStorage, createStorage } from 'wagmi'
externalWalletConfig={{
evmConnector: {
config: {
chains: [mainnet],
ssr: true,
storage: createStorage({
storage: cookieStorage,
}),
},
},
// ... rest of config
}}
```
Cookie-based persistence requires proper cookie handling on your server. This is entirely managed by the developer - Para provides the configuration options, but implementation depends on your server framework.
## External Wallets with Linked Embedded Wallets
You can also provision linked embedded wallets for external wallets.
In this case, the external wallet would be the Para auth method for the user's embedded wallet (instead of an email or social login). Embedded wallets would be created according to your API key settings.
To enable this, you can include the `createLinkedEmbeddedForExternalWallets` prop to indicate which external wallets this setting should be applied to.
## Advanced Provider Pattern
Setting up a dedicated provider component that encapsulates all the necessary providers and modal state management is
considered a best practice. This pattern makes it easier to manage the modal state globally and handle session
management throughout your application.
## Configuring the Para Modal
After setting up your providers you need to configure the ParaModal component to display the external wallets and
authentication options to your users. You need to pass in the `externalWallets` and `authLayout` configuration options
to the ParaModal component to control which of the wallets show in the modal that were specified in the provider
configuration.
### Set the modal props
```typescript theme={null}
paraModalConfig={{
authLayout: ["AUTH_FULL","EXTERNAL_FULL"]
theme: {
mode: "light",
foregroundColor: "#000000",
backgroundColor: "#FFFFFF",
accentColor: "#007AFF"
}
logo: yourLogoUrl
// ... other modal config
}}
```
#### Modal Props Config
Modal prop options for customizing the Para Modal are included below. For advanced customization options, refer to
.
## External Wallet Verification via SIWE
External wallet verification via Sign in With Ethereum adds a verification step during external connection to ensure the user owns the wallet.
Enabling this feature establishes a valid Para session, which you can later use in your app to securely validate wallet ownership.
To enable this, set the following option on your `externalWalletConfig` of your `ParaProvider`:
```
externalWalletConfig={{
includeWalletVerification: true,
...REST_OF_CONFIG
}}
```
## Connection Only Wallets
Connection only external wallets bypass all Para functionality (account creation, user tracking, etc.) when connecting an external wallet. To enable this, set the following option on your `externalWalletConfig` of your `ParaProvider`:
```
externalWalletConfig={{
connectionOnly: true,
...REST_OF_CONFIG
}}
```
Since connection only wallets bypass Para, most Para functionality will be unavailable. This includes linked embedded wallets, external wallet verification, on & off ramping, etc.
## Examples
For an example of what the Para External Wallets Modal might look like in your application, check out our live demo:
For an example code implementation using EVM Wallets, check out our GitHub repository:
## Next Steps
Now that you have integrated EVM wallets into your Para Modal, you can explore more advanced features like signing using
the Para SDK with popular libraries like `Ethers.js`.
# Multichain Wallets
Source: https://docs.getpara.com/v2/react/guides/external-wallets/multichain
Learn how to combine EVM, Solana, and Cosmos wallets with the Para Modal.
This guide will walk you through the process of integrating multiple blockchain wallets into your Para Modal and
Para-enabled application. By combining EVM, Solana, and Cosmos wallet support, you can provide users with a seamless
multi-chain experience.
## Prerequisites
Before integrating wallet connections, ensure you have an existing Para
project with the Para Modal set up. If you haven't set up Para yet, follow one
of our Framework Setup guides like this guide.
## Setting up Multichain Support
Supporting multiple blockchain ecosystems is simple, all you need to do is install the necessary wallet connectors and
configure them within your Para Provider. Instructions for each connector can be found in the respective guides for each
ecosystem:
Multi chain wallets can only be connected to one chain at a time. Any wallets
that Para is setup to support across ecosystems will give the give the user a
choice of ecosystem selection before they connect their wallet.
## Examples
Check out our live demo of the Para Modal to configure all wallets:
For a code implementation, check out our GitHub repository:
## Next Steps
Now that you have integrated multichain wallet support, explore chain-specific features and integrations:
# Solana Wallets
Source: https://docs.getpara.com/v2/react/guides/external-wallets/solana
Learn how to combine the Para Modal with Solana wallets.
This guide will walk you through the process of integrating Solana Wallets into your Para Modal and Para-enabled
application, allowing you to onboard new users and connect with existing users who may already have external wallets
like Phantom, Backpack and more.
## Prerequisites
Before integrating wallet connections, ensure you have an existing Para project with the Para Modal set up. If you
haven't set up Para yet, follow one of our Framework Setup guides like this
guide.
## Setting up Solana Wallets
Setup is simple - just wrap your app in a provider and pass the appropriate props and configuration options to the
provider. Once configured, the Para modal and wallet options will automatically appear in the modal when opened.
Para provides seamless integration with popular Solana wallets including
, , , and .
### Import components
Import the wallet connectors and supporting components you need. Adjust the imports based on which wallets you want to support:
```typescript main.tsx theme={null}
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { clusterApiUrl } from "@solana/web3.js";
import {
ParaProvider,
ExternalWallet,
} from "@getpara/react-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
```
### Configure the Solana network
Set up your Solana network configuration. Choose the appropriate network for your deployment environment:
```typescript main.tsx theme={null}
const solanaNetwork = WalletAdapterNetwork.Devnet;
const endpoint = clusterApiUrl(solanaNetwork);
```
### Configure the providers
Configure the `ParaProvider` component by wrapping your application content in the `QueryClientProvider` and `ParaProvider` components. Pass in the required configuration props:
```typescript main.tsx theme={null}
export const App = () => {
return (
{/* Your app content */}
);
};
```
#### External Wallet Configuration
## External Wallets with Linked Embedded Wallets
You can also provision linked embedded wallets for external wallets.
In this case, the external wallet would be the Para auth method for the user’s embedded wallet (instead of an email or social login). Embedded wallets would be created according to your API key settings.
To enable this, you can include the `createLinkedEmbeddedForExternalWallets` prop to indicate which external wallets this setting should be applied to.
## Advanced Provider Pattern
Setting up a dedicated provider component that encapsulates all the necessary providers and modal state management is
considered a best practice. This pattern makes it easier to manage the modal state globally and handle session
management throughout your application.
### Server-Side Rendering Considerations
When using Next.js or other SSR frameworks, proper client-side initialization is crucial since web3 functionality relies
on browser APIs. There are two main approaches:
1. Using the `'use client'` directive in Next.js 13+:
* Add the directive at the component level where browser APIs are needed. If using a custom provider, add the
directive to the top of the provider file.
* Ensures the Web3Provider component and its dependencies only run on the client side
2. Using dynamic imports:
* In Next.js, use the `dynamic` function to import the provider component with `{ ssr: false }`.
* Lazily loads the provider component
* Automatically handles client-side only code
* Provides fallback options during loading
## Configuring the Para Modal
After setting up your providers you need to configure the ParaModal component to display the external wallets and
authentication options to your users. You need to pass in the `externalWallets` and `authLayout` configuration options
to the ParaModal component to control which of the wallets show in the modal that were specified in the provider
configuration.
### Set the modal props
```typescript theme={null}
paraModalConfig={{
authLayout: ["AUTH_FULL","EXTERNAL_FULL"]
theme: {
mode: "light",
foregroundColor: "#000000",
backgroundColor: "#FFFFFF",
accentColor: "#007AFF"
}
logo: yourLogoUrl
// ... other modal config
}}
```
#### Modal Props Config
Modal prop options for customizing the Para Modal are included below. For advanced customization options, refer to
.
## External Wallet Verification
External wallet verification adds a verification step during external connection to ensure the user owns the wallet.
Enabling this feature establishes a valid Para session, which you can later use in your app to securely validate wallet ownership.
To enable this, set the following option on your `externalWalletConfig` of your `ParaProvider`:
```
externalWalletConfig={{
includeWalletVerification: true,
...REST_OF_CONFIG
}}
```
## Connection Only Wallets
Connection only external wallets bypass all Para functionality (account creation, user tracking, etc.) when connecting an external wallet. To enable this, set the following option on your `externalWalletConfig` of your `ParaProvider`:
```
externalWalletConfig={{
connectionOnly: true,
...REST_OF_CONFIG
}}
```
Since connection only wallets bypass Para, most Para functionality will be unavailable. This includes linked embedded wallets, external wallet verification, on & off ramping, etc.
## Examples
For an example of what the Para External Wallets Modal might look like in your application, check out our live demo:
For an example code implementation using Solana Wallets, check out our GitHub repository:
## Next Steps
Now that you have integrated Solana wallets into your Para Modal, you can explore more advanced features like signing using the Para SDK with popular libraries like `Web3js`.
# Migrating from Reown to Para
Source: https://docs.getpara.com/v2/react/guides/migration-from-reown
Step-by-step guide for migrating your Reown (WalletConnect) application to Para SDK
Migrate your existing Reown (WalletConnect) application to Para's unified wallet system. Para simplifies wallet management by providing a single `ParaProvider` component that handles both embedded and external wallets while maintaining full Wagmi compatibility.
## Installation
Update your dependencies to replace Reown packages with Para SDK:
```bash Terminal theme={null}
npm uninstall @reown/appkit @reown/appkit-adapter-wagmi
npm install @getpara/react-sdk --save-exact
```
For a full list of dependencies, refer to the [Quick Start Guide](/v2/react/quickstart).
## Setup
### Configure Constants
Create a constants file for your Para configuration:
```typescript src/config/constants.ts theme={null}
import { Environment } from "@getpara/react-sdk";
export const API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY ?? ""; // Grab your API key from developer.getpara.com
export const ENVIRONMENT =
(process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as Environment) || Environment.BETA;
if (!API_KEY) {
throw new Error("Missing NEXT_PUBLIC_PARA_API_KEY environment variable");
}
```
### Create Providers
Set up the Para provider to replace Reown's WagmiProvider:
```tsx src/context/ParaProvider.tsx theme={null}
"use client";
import { ParaProvider as Provider } from "@getpara/react-sdk";
import { API_KEY, ENVIRONMENT } from "@/config/constants";
import { mainnet, polygon, sepolia } from "wagmi/chains";
import { cookieStorage, createStorage } from "wagmi";
export function ParaProvider({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
### Update Root Layout
Replace your Reown context provider with the ParaProvider:
```tsx src/app/layout.tsx theme={null}
import type { Metadata } from "next";
import "@getpara/react-sdk/styles.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@/context/ParaProvider";
const queryClient = new QueryClient();
export const metadata: Metadata = {
title: "Your App",
description: "Powered by Para",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
## Usage
### Connect Button
Replace Reown's `` with Para's modal hooks:
```tsx src/components/ConnectButton.tsx theme={null}
"use client";
import { useAccount, useModal, useWallet } from "@getpara/react-sdk";
export function ConnectButton() {
const { openModal } = useModal();
const { data: wallet } = useWallet();
const { isConnected } = useAccount();
if (isConnected && wallet?.address) {
return (
);
}
return (
);
}
```
### Using Wagmi Hooks
`ParaProvider` maintains full Wagmi compatibility. Your existing Wagmi code continues to work within the Para context.
```tsx src/components/Balance.tsx theme={null}
import { useAccount, useBalance } from "wagmi";
export function Balance() {
const { address } = useAccount();
const { data } = useBalance({ address });
if (!data) return null;
return (
Balance: {data.formatted} {data.symbol}
);
}
```
## Migration Checklist
* Remove `@reown/appkit` packages
* Install `@getpara/react-sdk`
* Keep Wagmi and Viem packages
* Run postinstall script
* Set environment variables
* Update Next.js config (remove Webpack externals)
* Import Para styles
* Configure chain list
* Replace `` with custom component
* Update provider hierarchy
* Test wallet connections
* Verify transaction flows
* Connect external wallets
* Test embedded login (email/social)
* Verify chain switching
* Check transaction signing
## Next Steps
Explore Para's React hooks for wallet interactions
Customize the Para modal appearance and behavior
Configure additional external wallet support
Learn about Para's session management
# Migrating from Thirdweb to Para
Source: https://docs.getpara.com/v2/react/guides/migration-from-thirdweb
Step-by-step guide for migrating your Thirdweb application to Para SDK
Migrate your existing Thirdweb application to Para's unified wallet system. Para provides similar wallet connection capabilities with additional features like embedded wallets and session management while maintaining a simple integration.
## Installation
Replace Thirdweb with Para SDK:
```bash Terminal theme={null}
npm uninstall thirdweb
npm install @getpara/react-sdk @tanstack/react-query wagmi@^2 viem
```
For a full list of dependencies, refer to the [Quick Start Guide](/v2/react/quickstart).
## Configuration Changes
### Before: Thirdweb Client
Your existing Thirdweb client configuration:
```typescript lib/client.ts theme={null}
import { createThirdwebClient } from "thirdweb";
export const client = createThirdwebClient({
clientId: process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID!,
});
```
### After: Para Configuration
Replace with Para configuration:
```typescript src/config/constants.ts theme={null}
import { Environment } from "@getpara/react-sdk";
export const API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY ?? "";
export const ENVIRONMENT =
(process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as Environment) || Environment.BETA;
if (!API_KEY) {
throw new Error("Missing NEXT_PUBLIC_PARA_API_KEY environment variable");
}
```
## Provider Migration
### Before: Thirdweb Provider
Your existing Thirdweb provider in the layout:
```tsx app/layout.tsx theme={null}
import type { Metadata } from "next";
import { ThirdwebProvider } from "thirdweb/react";
export const metadata: Metadata = {
title: "Your App",
description: "Your App Description",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{children}
);
}
```
### After: Para Provider Setup
Create a Para provider component:
```tsx src/context/ParaProvider.tsx theme={null}
"use client";
import { ParaProvider as Provider } from "@getpara/react-sdk";
import { API_KEY, ENVIRONMENT } from "@/config/constants";
import { mainnet, polygon, arbitrum } from "wagmi/chains";
export function ParaProvider({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
Update your layout:
```tsx src/app/layout.tsx theme={null}
import type { Metadata } from "next";
import "@getpara/react-sdk/styles.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@/context/ParaProvider";
const queryClient = new QueryClient();
export const metadata: Metadata = {
title: "Your App",
description: "Your App Description",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
## Connect Button Migration
### Before: Thirdweb ConnectButton
```tsx app/page.tsx theme={null}
"use client";
import { ConnectButton } from "thirdweb/react";
import { client } from "@/lib/client";
export default function Home() {
return (
);
}
```
### After: Para Connect Button
```tsx src/components/ConnectButton.tsx theme={null}
"use client";
import { useAccount, useModal, useWallet } from "@getpara/react-sdk";
export function ConnectButton() {
const { openModal } = useModal();
const { data: wallet } = useWallet();
const { isConnected } = useAccount();
if (isConnected && wallet?.address) {
return (
);
}
return (
);
}
```
Use it in your page:
```tsx src/app/page.tsx theme={null}
import { ConnectButton } from "@/components/ConnectButton";
export default function Home() {
return (
);
}
```
## Hook Migration
### Account Information
```tsx Thirdweb theme={null}
import { useActiveAccount } from "thirdweb/react";
function Account() {
const account = useActiveAccount();
return (
Address: {account?.address}
);
}
```
```tsx Para theme={null}
import { useAccount } from "@getpara/react-sdk";
function Account() {
const { address } = useAccount();
return (
Address: {address}
);
}
```
### Wallet Connection Status
```tsx Thirdweb theme={null}
import { useActiveWalletConnectionStatus } from "thirdweb/react";
function ConnectionStatus() {
const status = useActiveWalletConnectionStatus();
return (
Status: {status}
);
}
```
```tsx Para theme={null}
import { useAccount } from "@getpara/react-sdk";
function ConnectionStatus() {
const { isConnected, isConnecting } = useAccount();
return (
);
}
```
### Disconnect Wallet
```tsx Thirdweb theme={null}
import { useDisconnect } from "thirdweb/react";
function DisconnectButton() {
const { disconnect } = useDisconnect();
return (
);
}
```
```tsx Para theme={null}
import { useDisconnect } from "wagmi";
function DisconnectButton() {
const { disconnect } = useDisconnect();
return (
);
}
```
## Smart Contract Interaction
Para uses Wagmi for contract interactions, providing a different approach than Thirdweb:
### Reading Contract Data
```tsx Thirdweb theme={null}
import { getContract } from "thirdweb";
import { useReadContract } from "thirdweb/react";
import { client } from "@/lib/client";
const contract = getContract({
client,
chain: ethereum,
address: "0x...",
});
function ContractRead() {
const { data } = useReadContract({
contract,
method: "function balanceOf(address) returns (uint256)",
params: ["0x..."],
});
return
{data?.toString()}
;
}
```
```tsx Para theme={null}
import { useReadContract } from "wagmi";
const abi = [
{
name: "balanceOf",
type: "function",
inputs: [{ name: "owner", type: "address" }],
outputs: [{ name: "balance", type: "uint256" }],
},
] as const;
function ContractRead() {
const { data } = useReadContract({
address: "0x...",
abi,
functionName: "balanceOf",
args: ["0x..."],
});
return
{data?.toString()}
;
}
```
### Writing to Contract
```tsx Thirdweb theme={null}
import { prepareContractCall } from "thirdweb";
import { useSendTransaction } from "thirdweb/react";
function ContractWrite() {
const { mutate: sendTransaction } = useSendTransaction();
const handleTransfer = () => {
const transaction = prepareContractCall({
contract,
method: "function transfer(address, uint256)",
params: ["0x...", 100n],
});
sendTransaction(transaction);
};
return ;
}
```
```tsx Para theme={null}
import { useWriteContract } from "wagmi";
function ContractWrite() {
const { writeContract } = useWriteContract();
const handleTransfer = () => {
writeContract({
address: "0x...",
abi,
functionName: "transfer",
args: ["0x...", 100n],
});
};
return ;
}
```
## Feature Comparison
| Feature | Thirdweb | Para |
| ------------------ | ------------ | ----------- |
| Wallet Connection | ✅ | ✅ |
| Social Login | Via Auth SDK | ✅ Built-in |
| Email/Phone Login | Via Auth SDK | ✅ Built-in |
| Smart Contracts | Custom SDK | Wagmi hooks |
| Chain Switching | ✅ | ✅ |
| Session Management | Limited | ✅ Advanced |
| Gas Sponsorship | Via Engine | ✅ Built-in |
## Advanced Features
### Social Login
Para includes social login without additional configuration:
```tsx src/context/ParaProvider.tsx theme={null}
paraModalConfig={{
oAuthMethods: ["GOOGLE", "APPLE", "DISCORD", "TWITTER", "FACEBOOK"],
disableEmailLogin: false,
disablePhoneLogin: false,
}}
```
### Multi-Chain Support
```tsx src/context/ParaProvider.tsx theme={null}
import { mainnet, polygon, arbitrum, optimism, base } from "wagmi/chains";
externalWalletConfig={{
evmConnector: {
config: {
chains: [mainnet, polygon, arbitrum, optimism, base],
},
},
}}
```
### Custom Theme
```tsx src/context/ParaProvider.tsx theme={null}
paraModalConfig={{
theme: {
mode: "dark",
foregroundColor: "#FFFFFF",
backgroundColor: "#1A1A1A",
accentColor: "#6366F1",
borderRadius: "medium",
font: "Inter",
},
logo: "/logo.svg",
}}
```
## Migration Checklist
* Remove `thirdweb` package
* Install `@getpara/react-sdk`, `wagmi`, `viem`
* Add `@tanstack/react-query`
* Run Para postinstall script
* Replace Thirdweb client with Para configuration
* Update environment variables
* Configure supported chains
* Set up WalletConnect project ID if needed
* Replace `ThirdwebProvider` with `ParaProvider`
* Update `ConnectButton` implementation
* Migrate hooks to Para/Wagmi equivalents
* Update contract interaction code
* Test wallet connections
* Verify contract interactions
* Check chain switching
* Test social login if enabled
## Common Patterns
### Balance Display
```tsx src/components/Balance.tsx theme={null}
import { useAccount, useBalance } from "wagmi";
import { formatEther } from "viem";
export function Balance() {
const { address } = useAccount();
const { data } = useBalance({ address });
if (!data) return null;
return (
{formatEther(data.value)} {data.symbol}
);
}
```
### Transaction History
```tsx src/components/TransactionHistory.tsx theme={null}
import { useWallet } from "@getpara/react-sdk";
export function TransactionHistory() {
const { data: wallet } = useWallet();
// Para provides transaction history through the wallet object
const transactions = wallet?.transactions || [];
return (
{transactions.map((tx) => (
{tx.hash}
))}
);
}
```
## Next Steps
Learn about Wagmi hooks for contract interactions
Explore Para's React hooks
Implement session management
# Migrating from Web3Modal to Para
Source: https://docs.getpara.com/v2/react/guides/migration-from-walletconnect
Step-by-step guide for migrating your Web3Modal (WalletConnect) application to Para SDK
Migrate your existing Web3Modal application to Para's unified wallet system. Since both Web3Modal and Para use Wagmi internally, the migration involves replacing the Web3Modal provider with Para's `ParaProvider` while maintaining your existing Wagmi configuration.
## Installation
Replace Web3Modal packages with Para SDK while keeping Wagmi:
```bash Terminal theme={null}
npm uninstall @web3modal/wagmi
npm install @getpara/react-sdk
npm run postinstall
```
For a full list of dependencies, refer to the [Quick Start Guide](/v2/react/quickstart).
## Configuration Changes
### Before: Web3Modal Config
Your existing Web3Modal configuration likely looks like this:
```typescript config/index.ts theme={null}
import { cookieStorage, createStorage } from 'wagmi';
import { mainnet } from 'wagmi/chains';
import { http, createConfig } from 'wagmi';
import { walletConnect, injected, coinbaseWallet } from 'wagmi/connectors';
export const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;
if (!projectId) throw new Error('Project ID is not defined');
const metadata = {
name: 'Web3Modal Example',
description: 'Web3Modal Example',
url: 'https://web3modal.com',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
};
export const config = createConfig({
chains: [mainnet],
transports: {
[mainnet.id]: http(),
},
connectors: [
walletConnect({ projectId, metadata, showQrModal: false }),
injected({ shimDisconnect: true }),
coinbaseWallet({
appName: metadata.name,
appLogoUrl: metadata.icons[0],
}),
],
ssr: true,
storage: createStorage({
storage: cookieStorage,
}),
});
```
### After: Para Config
Transform your configuration for Para:
```typescript src/config/constants.ts theme={null}
import { Environment } from "@getpara/react-sdk";
export const API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY ?? "";
export const ENVIRONMENT =
(process.env.NEXT_PUBLIC_PARA_ENVIRONMENT as Environment) || Environment.BETA;
export const PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID ?? "";
if (!API_KEY) {
throw new Error("Missing NEXT_PUBLIC_PARA_API_KEY environment variable");
}
```
## Provider Migration
### Before: Web3Modal Provider
Your existing Web3Modal provider:
```tsx context/Web3ModalProvider.tsx theme={null}
'use client';
import React, { ReactNode } from 'react';
import { config, projectId } from '@/config';
import { createWeb3Modal } from '@web3modal/wagmi/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { State, WagmiProvider } from 'wagmi';
const queryClient = new QueryClient();
createWeb3Modal({
wagmiConfig: config,
projectId,
enableAnalytics: true,
});
export default function Web3ModalProvider({
children,
initialState,
}: {
children: ReactNode;
initialState?: State;
}) {
return (
{children}
);
}
```
### After: Para Provider
Replace with Para's provider, passing your Wagmi config:
```tsx src/context/ParaProvider.tsx theme={null}
"use client";
import { ParaProvider as Provider } from "@getpara/react-sdk";
import { API_KEY, ENVIRONMENT, PROJECT_ID } from "@/config/constants";
import { mainnet } from "wagmi/chains";
import { cookieStorage, createStorage, http } from "wagmi";
export function ParaProvider({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
## Layout Update
### Before: Web3Modal Layout
```tsx app/layout.tsx theme={null}
import './globals.css';
import type { Metadata } from 'next';
import { headers } from 'next/headers';
import { cookieToInitialState } from 'wagmi';
import { config } from '@/config';
import Web3ModalProvider from '@/context/Web3ModalProvider';
export const metadata: Metadata = {
title: 'Your App',
description: 'Your App Description',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
const initialState = cookieToInitialState(config, headers().get('cookie'));
return (
{children}
);
}
```
### After: Para Layout
```tsx src/app/layout.tsx theme={null}
import type { Metadata } from "next";
import "@getpara/react-sdk/styles.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@/context/ParaProvider";
const queryClient = new QueryClient();
export const metadata: Metadata = {
title: "Your App",
description: "Your App Description",
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
## Connect Button Migration
### Before: Web3Modal Button
```tsx components/ConnectButton.tsx theme={null}
'use client';
import { useWeb3Modal } from '@web3modal/wagmi/react';
export default function ConnectButton() {
const { open } = useWeb3Modal();
return ;
}
```
### After: Para Button
```tsx src/components/ConnectButton.tsx theme={null}
"use client";
import { useAccount, useModal, useWallet } from "@getpara/react-sdk";
export function ConnectButton() {
const { openModal } = useModal();
const { data: wallet } = useWallet();
const { isConnected } = useAccount();
if (isConnected && wallet?.address) {
return (
);
}
return (
);
}
```
## Configuration Mapping
Para maintains full Wagmi compatibility. Your existing Wagmi hooks and configuration continue to work seamlessly.
| Web3Modal Config | Para Config | Location |
| ------------------------ | -------------------------------- | ---------------------- |
| `projectId` | `walletConnect.projectId` | `externalWalletConfig` |
| `wagmiConfig.chains` | `evmConnector.config.chains` | `externalWalletConfig` |
| `wagmiConfig.transports` | `evmConnector.config.transports` | `externalWalletConfig` |
| `wagmiConfig.ssr` | `evmConnector.config.ssr` | `externalWalletConfig` |
| `wagmiConfig.storage` | `evmConnector.config.storage` | `externalWalletConfig` |
| `metadata.name` | `config.appName` | Root config |
| `metadata.description` | `config.description` | Root config |
## Advanced Configuration
### Multiple Chains
```tsx src/context/ParaProvider.tsx theme={null}
import { mainnet, polygon, arbitrum, optimism } from "wagmi/chains";
// In ParaProvider
externalWalletConfig={{
evmConnector: {
config: {
chains: [mainnet, polygon, arbitrum, optimism],
transports: {
[mainnet.id]: http(),
[polygon.id]: http(),
[arbitrum.id]: http(),
[optimism.id]: http(),
},
},
},
}}
```
### Custom RPC Endpoints
```tsx src/context/ParaProvider.tsx theme={null}
import { http } from "wagmi";
// In ParaProvider
externalWalletConfig={{
evmConnector: {
config: {
transports: {
[mainnet.id]: http("https://your-custom-rpc.com"),
},
},
},
}}
```
### Social Login Support
Para adds embedded wallet support automatically:
```tsx src/context/ParaProvider.tsx theme={null}
paraModalConfig={{
authLayout: ["AUTH:FULL", "EXTERNAL:FULL"],
oAuthMethods: ["GOOGLE", "APPLE", "DISCORD", "TWITTER"],
disableEmailLogin: false,
disablePhoneLogin: false,
}}
```
## Using Wagmi Hooks
Your existing Wagmi code continues to work without changes:
```tsx src/components/AccountInfo.tsx theme={null}
import { useAccount, useBalance, useEnsName } from "wagmi";
export function AccountInfo() {
const { address } = useAccount();
const { data: balance } = useBalance({ address });
const { data: ensName } = useEnsName({ address });
if (!address) return null;
return (
{ensName || address}
{balance?.formatted} {balance?.symbol}
);
}
```
## Migration Checklist
* Remove `@web3modal/wagmi` and `@web3modal/wagmi/react`
* Install `@getpara/react-sdk`
* Keep `wagmi`, `viem`, and `@tanstack/react-query`
* Run the Para postinstall script
* Move Wagmi config to `externalWalletConfig.evmConnector.config`
* Add Para API key to environment variables
* Migrate metadata to Para's config object
* Keep WalletConnect project ID for external wallets
* Replace Web3ModalProvider with ParaProvider
* Remove cookie state handling (Para manages internally)
* Keep QueryClientProvider wrapper
* Import Para styles
* Replace `useWeb3Modal` with `useModal`
* Update connect button component
* Keep all Wagmi hook usage unchanged
* Test wallet connections
## Common Issues
**SSR Hydration**: Para handles SSR internally. Remove manual cookie state management from your layout.
**Connector Configuration**: Para manages wallet connectors internally. Remove explicit connector imports from Wagmi.
## Next Steps
Explore Para's React hooks for wallet interactions
Customize the Para modal appearance
Configure multi-chain support
# Permissions
Source: https://docs.getpara.com/v2/react/guides/permissions
Configure user-facing transaction confirmation dialogs
Permission prompts give applications the option to show users a Para-managed dialog to manually approve or deny any transaction or message signing events.
This feature is required when interacting with wallets created outside your app, but can also be enabled for all wallets/transactions in your application. We recommend enabling this feature if you prefer not to implement transaction approval/display flows, or if you want users to explicitly approve every transaction on an embedded wallet.
## How it Works
Permission prompts are mandatory in the following scenarios:
* **External Wallet Interaction**: When a user attempts to sign a message or execute a transaction using their wallet in an application that is different from where the wallet was created.
* **Multi-Application Transactions**: After a wallet has been used across multiple applications, any subsequent transactions will prompt the user for approval. This ensures that the user is aware of and consents to all interactions involving their wallet, regardless of the application being used.
To enable permission prompts for all transactions/wallets, activate "Always show transaction prompts" for your API key in the
## User Interaction
When a transaction or message signing event is initiated, users will see a popup containing the following details:
* **Message Details**: An encoded string representing the message.
* **Transaction Details**:
* `From`: The wallet address initiating the transaction.
* `To`: The recipient wallet address.
* `Amount`: The number of tokens being transferred.
* `Action`: The specific action being performed (e.g., token transfer, minting an NFT).
* `Conversion Rates`: Relevant exchange rates if applicable.
* `Chain Information`: Information about the blockchain being used.
## Technical Details
This feature is enabled by default when using the `signMessage` or `signTransaction` functions, either directly or
through supported signer libraries (e.g., Ethers, Cosmos).
There is a default 30-second timeout for approvals. If this does not work for your use case, please reach out to the
Para team for instructions on overriding this value.
### Transaction Events and Statuses
* **On Approval**: If the user approves the transaction or no approval is necessary, the `signMessage/signTransaction`
function will return a `SuccessfulSignatureRes` result, which will contain the signature.
* **On Denial or Timeout**: If the user denies the transaction or the timeout is reached, a `TransactionReviewError`
will be thrown that includes the `transactionReviewUrl` that must be handled by the implementing application.
## Error Handling
When implementing permission prompts, various errors can arise during the signing process. It's important to handle
these errors gracefully to ensure a smooth user experience. Below are common scenarios and recommended handling
strategies:
### 1. Transaction Denied
**Description**: The user denies the transaction or message signing request.
**Error Handling**:
```tsx theme={null}
try {
await client.sendTokens(...);
} catch (error) {
if (error instanceof TransactionReviewDenied) {
console.warn("The transaction was denied by the user.");
}
}
```
### 2. Timeout Reached
**Description**: The user does not respond to the popup within the configured timeout period. This returns additional
properties of `transactionReviewUrl` and `pendingTransactionId`:
* `pendingTransactionId` - Can be used in conjunction with the `getPendingTransaction` function available via the
CorePara class (or WebPara, by extension). If it does not exist, that means the user has denied the transaction
request.
* `transactionReviewUrl` - Can be used to open a popup if desired, which will present the user with the sign message /
transaction popup.
**Error Handling**:
```tsx theme={null}
try {
await client.sendTokens(...);
} catch (error) {
if (error instanceof TransactionReviewTimeout) {
console.warn("The transaction timed out. You can retry or direct the user to review.");
}
}
```
## Next Steps
To learn more about signing messages and transactions, check out the following guides:
# Wallet Pregeneration
Source: https://docs.getpara.com/v2/react/guides/pregen
Learn how to create and manage pregenerated wallets for users with Para's SDK
## Overview
Wallet Pregeneration allows you to create wallets for users before they set up a wallet with Para. This feature gives
you control over when users claim and take ownership of their wallet. This guide will walk you through the process of
creating, managing, and claiming pregenerated wallets using Para's SDK.
Pregenerated wallets can be associated with an email address, a phone number, a Twitter or Discord username, or a custom
ID of your choosing (for example, an external database ID). A Para user can attempt to claim any current pregenerated
wallets in their app storage.
* For email or phone, the user will need to have the same email address or phone number linked to their account.
* For Discord or Twitter, the user will need to have authenticated via those services on your application with the same
username.
* For a custom ID, the ID in question cannot have been claimed by another user. After a successful claim, only the first
user to claim a custom ID will thereafter be allowed to claim pregenerated wallets with that ID on your application.
## Creating a Pregenerated Wallet
Before creating a wallet for a user, it's a good practice to check if one already exists.
### Check if a pregenerated wallet exists
```typescript theme={null}
const hasWallet = await para.hasPregenWallet({
pregenId: { email: 'user@example.com' },
});
```
### Create a pregenerated wallet if needed
```typescript theme={null}
if (!hasWallet) {
const pregenWallet = await para.createPregenWallet({
type: 'EVM',
pregenId: { email: "user@example.com" },
});
console.log("Pregenerated Wallet ID:", pregenWallet.id);
}
```
### Method Parameters
### PregenAuth Type Definition
The identifier can be an email or phone number, a third-party user ID (for Farcaster, Telegram, Discord, or X), or a custom ID relevant to your application. Choose an identifier that works best for your application architecture.
## Storing and Managing User Share
After creating a pregenerated wallet, it's crucial to securely store the user share. This share is part of Para's 2/2
MPC protocol and remains the application's responsibility until the wallet is claimed.
To retrieve the user share for a pregenerated wallet, use the `getUserShare` method:
```typescript theme={null}
const userShare: string = await para.getUserShare();
```
You must securely store this user share in your backend, associated with the user's identifier. If this share is lost,
the wallet becomes permanently inaccessible.
### Best Practices for Storing the UserShare
While temporarily managing the `UserShare`, it's important that you take extra care with how you store this information.
If you ever face a situation where data becomes compromised across your systems, reach out to the Para team so we can
work on possible key rotation. However, keep in mind that Para does not store backups of this share in case of data
loss.
To mitigate this category of risks, we've compiled a few best practices:
* Encrypt `UserShares` in-transit and at-rest.
* Ensure your database has backups and periodic replicas to mitigate against data deletion risks.
* Complete a fire drill prior to going live, testing scenarios such as:
* You are unable to access your DB
* Your DB is deleted
* An internal team member's credentials are compromised
Para is happy to offer pre-launch security reviews for teams in the Growth tier or above. Let us know if you need
help!
This share management is temporary - once the user claims their wallet, Para will handle the share security through the
user's authentication methods.
## Using a Pregenerated Wallet
Before using a pregenerated wallet for signing operations, you must first load the user share into your Para client
instance. Retrieve the `UserShare` from your secure storage and load it into Para using the `setUserShare` method:
```typescript theme={null}
// Load the user share you previously stored securely
await para.setUserShare(userShare);
```
Once the share is loaded, the wallet becomes available for signing operations, just like any other Para wallet:
```typescript theme={null}
// Sign a message directly
const messageBase64 = btoa("Hello, World!");
const signature = await para.signMessage({
walletId,
messageBase64,
});
```
You can perform this operation using either `@getpara/server-sdk` or `@getpara/react-sdk`/`@getpara/web-sdk` depending
on your application architecture. The Para client that has the user share loaded is the one that can perform signing
operations.
### Using with Ecosystem Libraries
Once the `userShare` is set, your Para client functions like any standard wallet. You can now easily integrate with
popular blockchain libraries to perform transactions and other operations.
For detailed integration guides with blockchain ecosystems, see:
*
*
*
## Claiming a Pregenerated Wallet
Claiming pregenerated wallets must be done client-side with the Para Client SDK. The Server SDK does not support the
key rotation operations required for wallet claiming.
Claiming transfers ownership of a pregenerated wallet to a user's Para account. This process requires:
1. The user must be fully authenticated with Para
2. The wallet's user share must be loaded into the Para client
3. The wallet's identifier must match the authenticated user's identifier
### Prerequisites for Claiming
#### Claiming with the modal
### Define the function `fetchPregenWalletsOverride`
This function retrieves the `userShare` associated with the passed `pregenId` from your backend.
```typescript theme={null}
async function fetchPregenWalletsOverride(opts: { pregenId: PregenAuth }) => Promise<{ userShare?: string }> {
// logic to call backend and retrieve stored user share for given `pregenId`
return { userShare };
}
```
### Pass `fetchPregenWalletsOverride` as a field in `opts` to the `paraClientConfig`
This step allows our frontend logic to automatically fetch the correct `userShare` for claiming when the user is first creating an account through the modal flow.
```typescript theme={null}
{children}
```
#### Claiming manually
### Authenticate the user
Ensure the user has completed Para's authentication flow and has an active session.
```typescript theme={null}
// User must be authenticated through Para's systems
if (!(await para.isFullyLoggedIn())) {
// Redirect to authentication flow or show login modal
}
```
### Load the wallet's user share
Since this is a client side only operation, you need to securely send the `UserShare` from your server to the client. Once received, load it into the Para client:
```typescript theme={null}
// Load the previously stored user share into the Para client
await para.setUserShare(userShare);
```
### Update wallet identifier if needed
Ensure the pregenerated wallet's identifier matches the authenticated user's identifier:
```typescript theme={null}
// If you originally created the wallet with a custom identifier (like a UUID),
// update it to match the user's actual email or phone before claiming
await para.updatePregenWalletIdentifier({
walletId,
newPregenId: { email: user.email },
});
```
### Claiming the Wallet
Once prerequisites are met, you can claim the wallet:
```typescript theme={null}
// Claim all pregenerated wallets associated with this user
const recoverySecret = await para.claimPregenWallets();
// Or claim specific wallets by identifier
const recoverySecret = await para.claimPregenWallets({
pregenId: { email: user.email },
});
```
If the userShare is already loaded into the Para client before authentication occurs, and if the pregenerated wallet's
identifier matches the authenticating user, the wallet will be automatically claimed during authentication.
### Controlling When Wallets are Claimed
You have control over when users claim their pregenerated wallets:
* **Delayed Claiming**: Only load the userShare and update the identifier when you're ready for the user to claim the
wallet. This allows your application to continue using the wallet on behalf of the user.
* **Immediate Claiming**: If you want immediate claiming upon user authentication, load the userShare before
authentication and ensure the identifiers match.
* **No Claiming**: Keep using different identifiers for the wallet than the user's actual identifier to prevent
automatic claiming.
This flexibility lets you design the optimal user experience for your application.
## Core Pregeneration Methods
## Best Practices and Considerations
Pregenerated wallets are app-specific until claimed. Before claiming, they can only be used within your application through the `UserShare`. After a user claims the wallet, it becomes part of their Para account, allowing them to use it across different Para-integrated applications. This transition from app-managed to user-managed is a key consideration in your implementation strategy.
Choose identifiers that align with your application architecture: - **Email/Phone**: Most common for user-facing
applications - **OAuth Identifiers**: Useful for social login integrations (Discord, Twitter) - **Custom IDs**: Ideal
for internal user management systems Consider your user onboarding flow when choosing identifiers. If you use custom
IDs initially, you'll need to update them to match the user's actual identifier (email/phone) before claiming can
occur.
The user share is critical security information that must be protected: - **Encryption**: Always encrypt user shares
both in transit and at rest - **Database Security**: Implement proper access controls for your share database -
**Backups**: Maintain regular database backups to prevent data loss - **Disaster Recovery**: Create processes for
handling compromise scenarios - **Key Rotation**: Have a plan for working with Para if key rotation becomes necessary
Consider implementing a fire drill before launching to test scenarios like database loss, access issues, or credential
compromise. Para offers security reviews for teams on Growth tier and above.
Plan your user experience around wallet claiming: - **Delayed Claiming**: Keep control of wallets until users are
ready for full ownership - **Automatic Claiming**: Configure for immediate claiming during authentication -
**Progressive Onboarding**: Start users with app-managed wallets, then transition to self-custody - **Educational
Elements**: Help users understand the transition from app-managed to self-custody The claiming process should feel
seamless and intuitive to users while giving you flexibility in your application architecture.
Be deliberate about the wallet types you create:
* **Match Blockchain Needs**: Select wallet types (EVM, Solana, Cosmos) based on your application's blockchain requirements
* **Multiple Types**: Consider creating multiple wallet types if your application spans multiple blockchains
* **Default Selection**: If your app supports multiple chains, create wallets for all required types during pregeneration
* **User Guidance**: Provide clear information about which blockchain networks are supported by each wallet
Understand the operational boundaries:
* **Server-side**: Create pregen wallets, store user shares, sign transactions (with loaded shares)
* **Client-side only**: Create and claim wallets, load user shares, sign transactions
Design your architecture with these constraints in mind, especially when planning how user shares will flow from your server to client during the claiming process. Implement secure methods to transfer the user share from your server to the client when needed for wallet claiming.
## Reference Example
For complete examples demonstrating the usage of pregeneration methods, refer to our examples repository:
# Web Session Management
Source: https://docs.getpara.com/v2/react/guides/sessions
Overview of authentication session management in Para for web applications
Para provides a comprehensive set of methods for managing authentication sessions in web applications. These sessions are crucial for secure transaction signing and other authenticated operations. Proper session management helps maintain security while ensuring a seamless user experience.
## Session Configuration
The Para session length is `2 hours` by default, but can be configured to up to 30 days through the . A user signing a message or transaction extends the session by the duration of the session length.
### Security Considerations
**Shorter Sessions (2 Hours - 1 Day):**
* Enhanced security for sensitive applications
* Reduced risk if device is compromised
* Better for shared or public devices
**Longer Sessions (1 Week - 1 Month):**
* Improved user experience with fewer logins
* Better for personal devices and trusted environments
* Consider implementing automatic session refresh
### Custom Session Length
For custom durations:
1. Select "Custom" option in the Developer Portal
2. Enter duration in minutes
3. Consider your application's specific security needs
4. Balance security with user experience
## Session Management Topics
Explore the different aspects of session management in Para:
## Quick Start
Here's a basic example of checking and maintaining a session:
```typescript theme={null}
const para = new Para(apiKey);
// Check if session is active
const isActive = await para.isSessionActive();
if (!isActive) {
// Handle expired session - route to authentication
} else {
// Extend the session
await para.keepSessionAlive();
}
```
# JWT Token Management
Source: https://docs.getpara.com/v2/react/guides/sessions-jwt
Issuing and verifying JWT tokens for Para session attestation
Once a user is signed in, you can request a Para JWT token. This token will provide attestations for the user's ID, their identity, any wallets they have provisioned via your application, and any connected wallets in their current session.
## Requesting a JWT Token
You can request a JWT token using either the client method or the React hook. Both approaches return the token itself as well as the JWKS key ID (`kid`) for the keypair that signed it.
### Client Method
```typescript TypeScript theme={null}
import { ParaWeb } from '@getpara/web-sdk';
const para = new ParaWeb('your-api-key');
const { token, keyId } = await para.issueJwt();
```
```tsx React Hook theme={null}
import { useIssueJwt } from '@getpara/react-sdk';
function JwtTokenManager() {
const { issueJwt, issueJwtAsync, isPending, error } = useIssueJwt();
const [tokenInfo, setTokenInfo] = useState<{ token: string; keyId: string } | null>(null);
const handleIssueToken = async () => {
try {
const result = await issueJwtAsync();
setTokenInfo({
token: result.token,
keyId: result.keyId
});
await sendTokenToBackend(result.token);
} catch (err) {
console.error("Failed to issue JWT:", err);
}
};
return (
);
}
```
### React Hook
The token's expiry will be determined by your customized session length, or else will default to 30 minutes. Issuing a token, like most authenticated API operations, will also renew and extend the session for that duration.
## Token Structure
Depending on the user in question, a decoded token payload might resemble the following:
```json Email theme={null}
{
"data": {
"userId": "d5358219-38d3-4650-91a8-e338131d1c5e",
"wallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"connectedWallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"email": "email@example.com",
"authType": "email",
"identifier": "email@example.com",
"oAuthMethod": "google" // or: undefined | "x" | "discord" | "facebook" | "apple"
},
"iat": 1745877709,
"exp": 1745879509,
"sub": "d5358219-38d3-4650-91a8-e338131d1c5e"
}
```
```json Phone theme={null}
{
"data": {
"userId": "d5358219-38d3-4650-91a8-e338131d1c5e",
"wallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"connectedWallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"phone": "+13105551234",
"authType": "phone",
"identifier": "+13105551234"
},
"iat": 1745877709,
"exp": 1745879509,
"sub": "d5358219-38d3-4650-91a8-e338131d1c5e"
}
```
```json Telegram theme={null}
{
"data": {
"userId": "d5358219-38d3-4650-91a8-e338131d1c5e",
"wallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"connectedWallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"telegramUserId": "1234567890",
"authType": "telegram",
"identifier": "1234567890"
},
"iat": 1745877709,
"exp": 1745879509,
"sub": "d5358219-38d3-4650-91a8-e338131d1c5e"
}
```
```json Farcaster theme={null}
{
"data": {
"userId": "d5358219-38d3-4650-91a8-e338131d1c5e",
"wallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"connectedWallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"farcasterUsername": "FarcasterUsername",
"authType": "farcaster",
"identifier": "FarcasterUsername"
},
"iat": 1745877709,
"exp": 1745879509,
"sub": "d5358219-38d3-4650-91a8-e338131d1c5e"
}
```
```json External Wallet theme={null}
{
"data": {
"userId": "d5358219-38d3-4650-91a8-e338131d1c5e",
"wallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"connectedWallets": [
{
"id": "de4034f1-6b0f-4a98-87a5-e459db4d3a03",
"type": "EVM",
"address": "0x9dd3824f045c77bc369485e8f1dd6b452b6be617",
"publicKey": "0x0465434f76c8321f386856c44e735fd365a09d42c1da03489184b651c2052ea1c7b19c54722ed828458c1d271cc590b0818d8c7df423f71e92683f9e819095a8c6"
},
{
"id": "d70f64e4-266a-457e-9cea-eeb42341a975",
"type": "SOLANA",
"address": "EEp7DbBu5yvgf7Pr9W17cATPjCqUxY8K8R3dFbg53a3W",
"publicKey": ""
}
],
"externalWalletAddress": "0xaD6b78193b78e23F9aBBB675734f4a2B3559598D",
"authType": "externalWallet",
"identifier": "0xaD6b78193b78e23F9aBBB675734f4a2B3559598D",
"externalWallet": {
"address": "0xaD6b78193b78e23F9aBBB675734f4a2B3559598D",
"type": "EVM",
"provider": "MetaMask"
}
},
"iat": 1745877709,
"exp": 1745879509,
"sub": "d5358219-38d3-4650-91a8-e338131d1c5e"
}
```
## JWKS Verification
Para's JSON Web Keys Set (JWKS) file(s) are available at the following URLs:
| Environment | JWKS URL |
| ----------- | ------------------------------------------------------- |
| SANDBOX | `https://api.sandbox.getpara.com/.well-known/jwks.json` |
| BETA | `https://api.beta.getpara.com/.well-known/jwks.json` |
| PROD | `https://api.getpara.com/.well-known/jwks.json` |
## Best Practices
* **Session Verification**: For security-critical operations, verify JWT tokens on both client and server sides
* **Token Expiry**: Be aware that tokens expire based on your session configuration and plan accordingly
* **Secure Storage**: Never store JWT tokens in insecure locations like localStorage for sensitive applications
# Session Lifecycle
Source: https://docs.getpara.com/v2/react/guides/sessions-lifecycle
Managing the lifecycle of authentication sessions in Para
Learn how to check session status, maintain active sessions, and handle session expiration in Para web applications.
## Checking Session Status
Use `isSessionActive()` to verify whether a user's session is currently valid before performing authenticated operations.
Example usage:
```typescript theme={null}
const para = new Para(apiKey);
try {
const isActive = await para.isSessionActive();
if (!isActive) {
// Handle expired session
// See options below for session refresh
}
} catch (error) {
console.error("Session check failed:", error);
}
```
## Maintaining Active Sessions
Use `keepSessionAlive()` to extend an active session's validity without requiring full reauthentication.
Example usage:
```typescript theme={null}
const para = new Para(apiKey);
try {
const success = await para.keepSessionAlive();
if (!success) {
// Handle failed session maintenance
// Consider initiating a full authentication flow
}
} catch (error) {
console.error("Session maintenance failed:", error);
}
```
### Automatic Session Management with React
If you're using the React SDK and the `ParaProvider` component, you can leverage automatic session management:
```typescript theme={null}
// The ParaProvider will automatically keep sessions alive by default
// To disable automatic session management
```
When using the ParaProvider component from the React SDK, it automatically keeps sessions alive in the background by calling `keepSessionAlive()` periodically. You can disable this behavior by setting the `disableAutoSessionKeepAlive` prop to `true` if you prefer to manage sessions manually.
## Refreshing Expired Sessions
Para provides the `refreshSession()` method when a session has expired.
It's currently recommended to initiate a full authentication flow rather than using `refreshSession()` when sessions expire. The refresh flow is being improved in upcoming releases.
For most applications, when a session expires, it's better to guide users through a complete authentication process:
```typescript theme={null}
const para = new Para(apiKey);
// When session expires, initiate a full authentication
if (!(await para.isSessionActive())) {
//route to authentication page
}
```
## Best Practices
* **Proactive Session Management**: Always check session status before operations that require authentication
* **Regular Session Extension**: For long user sessions, periodically call `keepSessionAlive()` or leverage the `ParaProvider` automatic session management
* **Graceful Expiration Handling**: Provide a smooth re-authentication flow when sessions expire instead of showing errors
# Pregenerated Wallet Sessions
Source: https://docs.getpara.com/v2/react/guides/sessions-pregen
Session management for Para pregenerated wallets
When using pregenerated wallets, session management works differently as these wallets don't require traditional authentication.
## How Sessions Work with Pregenerated Wallets
For pregenerated wallets, the session is considered always active as long as the `UserShare` is loaded in the Para client instance. Traditional session expiration doesn't apply in this scenario.
```typescript theme={null}
const para = new Para(apiKey);
// Set a pregenerated wallet
await para.setUserShare(userShare);
// Session checks will return true as long as userShare is loaded
const isActive = await para.isSessionActive(); // Always true for pre-gen wallets
```
## Session Management Methods for Pre-Generated Wallets
When a UserShare is loaded via `setUserShare()`, the session remains active indefinitely. Methods like `isSessionActive()` will return true as long as the UserShare remains loaded in the Para client instance.
## Learn More
## Best Practices
* **UserShare Management**: Ensure the UserShare remains loaded in the Para instance for continuous session availability
* **Security**: Store UserShares securely and never expose them in client-side code
* **Session Verification**: Remember that `isSessionActive()` will always return true for loaded pregenerated wallets
# Session Transfer
Source: https://docs.getpara.com/v2/react/guides/sessions-transfer
Exporting Para sessions for server-side operations
Learn how to securely transfer session state from your client application to your server for performing operations on behalf of authenticated users.
## Exporting Sessions for Server-Side Operations
Use `exportSession()` when you need to transfer session state to your server for performing operations on behalf of the user.
By default, the exported session includes user signers which allow for server-side signing. If you don't need signing capabilities on your server, use the `excludeSigners` option to enhance security.
## Example Usage
Example client-side export:
```typescript theme={null}
const para = new Para(apiKey);
// After user authentication
// Full session with signing capabilities
const fullSession = para.exportSession();
// OR
// Session without signing capabilities (recommended if signing not needed)
const secureSession = para.exportSession({ excludeSigners: true });
// Send to your server
```
## Importing Sessions
For cases where you need to import a previously exported session back into a Para client instance:
```typescript theme={null}
const para = new Para(apiKey);
// Import a previously exported session
await para.importSession(exportedSessionString);
// Session is now active and ready for operations
const isActive = await para.isSessionActive(); // Should return true
```
## Server-Side Implementation
To learn more about handling session on the server, check out the following guide:
## Best Practices
* **Security-First Approach**: When exporting sessions to servers, use `excludeSigners: true` unless server-side signing is explicitly needed
* **Secure Transmission**: Always use HTTPS when transmitting exported sessions to your server
* **Session Validation**: Verify the session validity on your server before performing any operations
# Claim Staking Rewards
Source: https://docs.getpara.com/v2/react/guides/web3-operations/cosmos/claim-rewards
Claim accumulated staking rewards from delegated validators
Withdraw your accumulated staking rewards from validators on Cosmos chains using CosmJS.
## Prerequisites
## Claim Rewards
```typescript theme={null}
import { useCosmosClient } from "./useCosmosClient";
import { coins } from "@cosmjs/stargate";
import { MsgWithdrawDelegatorReward } from "cosmjs-types/cosmos/distribution/v1beta1/tx";
function ClaimRewards() {
const { signingClient } = useCosmosClient("https://rpc.cosmos.network");
const claimStakingRewards = async () => {
if (!signingClient) return;
const accounts = await signingClient.signer.getAccounts();
const delegator = accounts[0].address;
const validator = "cosmosvaloper1..."; // Replace with your validator
const msgWithdrawReward = {
typeUrl: "/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward",
value: MsgWithdrawDelegatorReward.fromPartial({
delegatorAddress: delegator,
validatorAddress: validator,
}),
};
const fee = {
amount: coins(5000, "uatom"),
gas: "200000",
};
try {
const result = await signingClient.signAndBroadcast(
delegator,
[msgWithdrawReward],
fee,
"Claiming rewards via Para"
);
console.log("Rewards claimed:", result.transactionHash);
console.log("Gas used:", result.gasUsed);
} catch (error) {
console.error("Failed to claim rewards:", error);
}
};
return (
);
}
```
## Next Steps
# Configure RPC Nodes with Cosmos Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/cosmos/configure-rpc
Set up and configure Cosmos RPC endpoints and clients using CosmJS
Learn how to configure custom RPC endpoints for different Cosmos-based chains when using CosmJS with Para.
## Prerequisites
## Configure Chain-Specific RPC
```typescript theme={null}
import { useCosmosClient } from "./useCosmosClient";
const CHAIN_CONFIGS = {
cosmos: {
rpc: "https://rpc.cosmos.network",
prefix: "cosmos"
},
osmosis: {
rpc: "https://osmosis-rpc.polkachu.com",
prefix: "osmo"
},
celestia: {
rpc: "https://celestia-rpc.publicnode.com",
prefix: "celestia"
},
dydx: {
rpc: "https://dydx-dao-rpc.polkachu.com",
prefix: "dydx"
}
};
function MultiChainExample() {
const cosmosClient = useCosmosClient(CHAIN_CONFIGS.cosmos.rpc, CHAIN_CONFIGS.cosmos.prefix);
const osmosisClient = useCosmosClient(CHAIN_CONFIGS.osmosis.rpc, CHAIN_CONFIGS.osmosis.prefix);
const checkChainStatus = async () => {
if (!cosmosClient.publicClient || !osmosisClient.publicClient) return;
const cosmosHeight = await cosmosClient.publicClient.getHeight();
const osmosisHeight = await osmosisClient.publicClient.getHeight();
console.log("Cosmos block height:", cosmosHeight);
console.log("Osmosis block height:", osmosisHeight);
};
return (
);
}
```
## Next Steps
# Execute Transactions with Cosmos Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/cosmos/execute-transactions
Interact with Cosmos modules and execute complex transactions using CosmJS
Execute custom messages and interact with Cosmos modules using CosmJS with Para wallets.
## Prerequisites
## Execute Custom Messages
```typescript theme={null}
import { useCosmosClient } from "./useCosmosClient";
import { coins } from "@cosmjs/stargate";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
function CustomTransaction() {
const { signingClient } = useCosmosClient("https://rpc.cosmos.network");
const executeCustomMessage = async () => {
if (!signingClient) return;
const accounts = await signingClient.signer.getAccounts();
const sender = accounts[0].address;
const msgSend = {
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: MsgSend.fromPartial({
fromAddress: sender,
toAddress: "cosmos1...", // Replace with recipient
amount: coins(1000000, "uatom"),
}),
};
const fee = {
amount: coins(5000, "uatom"),
gas: "200000",
};
try {
const result = await signingClient.signAndBroadcast(
sender,
[msgSend],
fee,
"Custom message via Para"
);
console.log("Transaction hash:", result.transactionHash);
console.log("Code:", result.code);
} catch (error) {
console.error("Transaction failed:", error);
}
};
return (
);
}
```
## Next Steps
# Sponsor Gas Fees on Cosmos
Source: https://docs.getpara.com/v2/react/guides/web3-operations/cosmos/gas-sponsorship
Learn how to implement gas sponsorship on Cosmos networks using fee grants with Para
This guide demonstrates how to implement gas sponsorship on Cosmos networks using fee grants. Unlike EVM chains that use account abstraction for gasless transactions, Cosmos networks natively support gas sponsorship through fee grants, allowing a grantor address to pay for another account's transaction fees.
## Prerequisites
## Understanding Fee Grants
Fee grants in Cosmos allow one account (grantor) to pay transaction fees for another account (grantee). This mechanism provides native gas sponsorship without requiring smart contracts or account abstraction.
Key concepts:
* **Grantor**: The account that pays for gas fees
* **Grantee**: The account whose transactions are sponsored
* **Allowance**: Defines spending limits and expiration for the grant
Only one fee grant is allowed per granter-grantee pair. Self-grants are not permitted.
## Creating a Basic Fee Grant
Create a basic allowance to grant gas sponsorship to another address:
```typescript theme={null}
import { MsgGrantAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/tx";
import { BasicAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/feegrant";
const granterAddress = paraSigner.address; // From your Para signer setup
const granteeAddress = "cosmos1grantee..."; // Replace with actual grantee address
// Create basic allowance with spending limits
const basicAllowance = BasicAllowance.fromPartial({
spendLimit: [{
denom: "uatom",
amount: "1000000" // 1 ATOM limit
}],
expiration: {
seconds: BigInt(Math.floor(Date.now() / 1000) + 86400), // 24 hours from now
nanos: 0
}
});
// Create the grant message
const grantMsg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgGrantAllowance",
value: MsgGrantAllowance.fromPartial({
granter: granterAddress,
grantee: granteeAddress,
allowance: {
typeUrl: "/cosmos.feegrant.v1beta1.BasicAllowance",
value: BasicAllowance.encode(basicAllowance).finish()
}
})
};
// Sign and broadcast the grant transaction
const result = await client.signAndBroadcast(
granterAddress,
[grantMsg],
"auto", // Let the client estimate gas
"Granting fee allowance"
);
```
## Using Fee Grants as a Grantee
Once a fee grant is established, the grantee can perform transactions with sponsored gas fees:
```typescript theme={null}
// Create a signer for the grantee
const granteeSigner = new ParaProtoSigner(granteeParaInstance, "cosmos");
const granteeClient = await SigningStargateClient.connectWithSigner(rpcUrl, granteeSigner);
// Send tokens with sponsored gas
const result = await granteeClient.sendTokens(
granteeAddress,
"cosmos1recipient...",
[{ denom: "uatom", amount: "100000" }], // 0.1 ATOM
{
amount: [{ denom: "uatom", amount: "5000" }],
gas: "200000",
granter: granterAddress // This tells the network to use the fee grant
}
);
```
## Querying Fee Grants
Check existing grants before creating new ones:
```typescript theme={null}
// Query grants for a specific grantee
const grantsByGrantee = await fetch(
`${restUrl}/cosmos/feegrant/v1beta1/allowances/${granteeAddress}`
).then(res => res.json());
// Query all grants by a specific granter
const grantsByGranter = await fetch(
`${restUrl}/cosmos/feegrant/v1beta1/issued/${granterAddress}`
).then(res => res.json());
// Query a specific grant
const specificGrant = await fetch(
`${restUrl}/cosmos/feegrant/v1beta1/allowance/${granterAddress}/${granteeAddress}`
).then(res => res.json());
```
## Other Allowance Types
### Periodic Allowance
Resets spending limits periodically:
```typescript theme={null}
import { PeriodicAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/feegrant";
const periodicAllowance = PeriodicAllowance.fromPartial({
basic: {
spendLimit: [{
denom: "uatom",
amount: "10000000" // 10 ATOM total limit
}],
expiration: null // No expiration
},
period: { seconds: BigInt(86400), nanos: 0 }, // 24 hours
periodSpendLimit: [{
denom: "uatom",
amount: "1000000" // 1 ATOM per period
}]
});
```
### Allowed Message Allowance
Restricts which message types can be sponsored:
```typescript theme={null}
import { AllowedMsgAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/feegrant";
const allowedMsgAllowance = AllowedMsgAllowance.fromPartial({
allowance: {
typeUrl: "/cosmos.feegrant.v1beta1.BasicAllowance",
value: BasicAllowance.encode(basicAllowance).finish()
},
allowedMessages: [
"/cosmos.bank.v1beta1.MsgSend",
"/cosmos.staking.v1beta1.MsgDelegate"
]
});
```
## Revoking Fee Grants
Remove a fee grant when it's no longer needed:
```typescript theme={null}
import { MsgRevokeAllowance } from "cosmjs-types/cosmos/feegrant/v1beta1/tx";
const revokeMsg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance",
value: MsgRevokeAllowance.fromPartial({
granter: granterAddress,
grantee: granteeAddress
})
};
const result = await client.signAndBroadcast(
granterAddress,
[revokeMsg],
"auto"
);
```
## Advanced: Server-Controlled Gas Sponsorship
For production applications, use server-side controlled wallets as grantors while allowing users to authenticate client-side as grantees. This pattern uses Para's pregenerated wallets to create an app-controlled grantor wallet.
### Server-Side Setup
Create and manage a grantor wallet on your server:
```typescript theme={null}
// server.ts
import { Para } from "@getpara/server-sdk";
import { SigningStargateClient } from "@cosmjs/stargate";
import { ParaProtoSigner } from "@getpara/cosmjs-adapter";
const serverPara = new Para(process.env.PARA_API_KEY);
// Create a pregen wallet for gas sponsorship
const grantorWallet = await serverPara.createPregenWallet({
type: 'COSMOS',
pregenId: { customId: "app-gas-sponsor-wallet" }
});
// Store the user share securely
const userShare = await serverPara.getUserShare();
// Store userShare in your secure database
```
### API Endpoint for Creating Grants
```typescript theme={null}
// POST /api/create-fee-grant
async function createFeeGrantForUser(userAddress: string) {
// Load the grantor wallet
await serverPara.setUserShare(storedUserShare);
// Set up Cosmos client
const granterSigner = new ParaProtoSigner(serverPara, "cosmos");
const client = await SigningStargateClient.connectWithSigner(rpcUrl, granterSigner);
// Check if grant already exists
const existingGrant = await fetch(
`${restUrl}/cosmos/feegrant/v1beta1/allowance/${granterSigner.address}/${userAddress}`
).then(res => res.json());
if (existingGrant.allowance) {
// Revoke existing grant first
const revokeMsg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance",
value: MsgRevokeAllowance.fromPartial({
granter: granterSigner.address,
grantee: userAddress
})
};
await client.signAndBroadcast(
granterSigner.address,
[revokeMsg],
"auto"
);
}
// Create new grant with daily limit
const basicAllowance = BasicAllowance.fromPartial({
spendLimit: [{
denom: "uatom",
amount: "1000000" // 1 ATOM daily limit
}],
expiration: {
seconds: BigInt(Math.floor(Date.now() / 1000) + 86400),
nanos: 0
}
});
const grantMsg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgGrantAllowance",
value: MsgGrantAllowance.fromPartial({
granter: granterSigner.address,
grantee: userAddress,
allowance: {
typeUrl: "/cosmos.feegrant.v1beta1.BasicAllowance",
value: BasicAllowance.encode(basicAllowance).finish()
}
})
};
const result = await client.signAndBroadcast(
granterSigner.address,
[grantMsg],
"auto"
);
return {
transactionHash: result.transactionHash,
granterAddress: granterSigner.address
};
}
```
### Client-Side Integration
```typescript theme={null}
// client.tsx
import { useParaWallet } from "@getpara/react-sdk";
import { SigningStargateClient } from "@cosmjs/stargate";
import { ParaProtoSigner } from "@getpara/cosmjs-adapter";
function MyApp() {
const { para, address } = useParaWallet();
const [granterAddress, setGranterAddress] = useState();
const setupGasSponsorship = async () => {
if (!address) return;
const response = await fetch('/api/create-fee-grant', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userAddress: address })
});
const { granterAddress } = await response.json();
setGranterAddress(granterAddress);
};
const performSponsoredTransaction = async (recipientAddress: string, amount: string) => {
if (!para || !address || !granterAddress) return;
const signer = new ParaProtoSigner(para, "cosmos");
const client = await SigningStargateClient.connectWithSigner(rpcUrl, signer);
const result = await client.sendTokens(
address,
recipientAddress,
[{ denom: "uatom", amount }],
{
amount: [{ denom: "uatom", amount: "5000" }],
gas: "200000",
granter: granterAddress // App pays the gas fees
}
);
return result.transactionHash;
};
return (
<>
>
);
}
```
### Complete Server Implementation
```typescript theme={null}
// Complete server implementation with grant management
class FeeGrantService {
private serverPara: Para;
private granterAddress?: string;
constructor() {
this.serverPara = new Para(process.env.PARA_API_KEY);
}
async initialize() {
// Load or create grantor wallet
const userShare = await loadUserShareFromDatabase();
await this.serverPara.setUserShare(userShare);
const signer = new ParaProtoSigner(this.serverPara, "cosmos");
this.granterAddress = signer.address;
}
async createGrant(userAddress: string, limitAmount: string, periodSeconds: number) {
const signer = new ParaProtoSigner(this.serverPara, "cosmos");
const client = await SigningStargateClient.connectWithSigner(rpcUrl, signer);
const allowance = BasicAllowance.fromPartial({
spendLimit: [{
denom: "uatom",
amount: limitAmount
}],
expiration: {
seconds: BigInt(Math.floor(Date.now() / 1000) + periodSeconds),
nanos: 0
}
});
const msg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgGrantAllowance",
value: MsgGrantAllowance.fromPartial({
granter: this.granterAddress,
grantee: userAddress,
allowance: {
typeUrl: "/cosmos.feegrant.v1beta1.BasicAllowance",
value: BasicAllowance.encode(allowance).finish()
}
})
};
return client.signAndBroadcast(this.granterAddress, [msg], "auto");
}
async revokeGrant(userAddress: string) {
const signer = new ParaProtoSigner(this.serverPara, "cosmos");
const client = await SigningStargateClient.connectWithSigner(rpcUrl, signer);
const msg = {
typeUrl: "/cosmos.feegrant.v1beta1.MsgRevokeAllowance",
value: MsgRevokeAllowance.fromPartial({
granter: this.granterAddress,
grantee: userAddress
})
};
return client.signAndBroadcast(this.granterAddress, [msg], "auto");
}
}
```
## Best Practices
* Set appropriate spending limits based on expected transaction volume
* Use expiration times to automatically clean up unused grants
* Monitor grant usage to control costs and detect abuse
* Consider using periodic allowances for regular users
* Use allowed message allowances to restrict transaction types
* Remember that creating and revoking grants also incur gas costs
Fee grants provide native gas sponsorship on Cosmos networks without requiring smart contracts, making them more efficient than EVM account abstraction solutions.
# IBC Cross-Chain Transfers
Source: https://docs.getpara.com/v2/react/guides/web3-operations/cosmos/ibc-transfers
Transfer tokens between Cosmos chains using Inter-Blockchain Communication
Transfer tokens between different Cosmos chains using IBC (Inter-Blockchain Communication) with CosmJS.
## Prerequisites
## IBC Transfer
```typescript theme={null}
import { useCosmosClient } from "./useCosmosClient";
import { coins } from "@cosmjs/stargate";
import { MsgTransfer } from "cosmjs-types/ibc/applications/transfer/v1/tx";
import { Height } from "cosmjs-types/ibc/core/client/v1/client";
function IBCTransfer() {
const { signingClient } = useCosmosClient("https://rpc.cosmos.network");
const sendIBCTransfer = async () => {
if (!signingClient) return;
const accounts = await signingClient.signer.getAccounts();
const sender = accounts[0].address;
const currentHeight = await signingClient.getHeight();
const timeoutHeight = Height.fromPartial({
revisionNumber: 1n,
revisionHeight: BigInt(currentHeight + 1000),
});
const msgTransfer = {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: MsgTransfer.fromPartial({
sourcePort: "transfer",
sourceChannel: "channel-141", // Osmosis channel
token: {
denom: "uatom",
amount: "1000000", // 1 ATOM
},
sender: sender,
receiver: "osmo1...", // Osmosis address
timeoutHeight: timeoutHeight,
timeoutTimestamp: 0n,
}),
};
const fee = {
amount: coins(5000, "uatom"),
gas: "250000",
};
try {
const result = await signingClient.signAndBroadcast(
sender,
[msgTransfer],
fee,
"IBC transfer via Para"
);
console.log("IBC transfer initiated:", result.transactionHash);
} catch (error) {
console.error("IBC transfer failed:", error);
}
};
return (
);
}
```
## Next Steps
# Query Wallet Balances with Cosmos Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/cosmos/query-balances
Check account balances and token holdings using CosmJS with Para wallets
Query token balances for any Cosmos address or your connected Para wallet using CosmJS.
## Prerequisites
## Query Balances
```typescript theme={null}
import { useCosmosClient } from "./useCosmosClient";
import { coins } from "@cosmjs/stargate";
function BalanceDisplay() {
const { publicClient, signingClient } = useCosmosClient("https://rpc.cosmos.network");
const [balances, setBalances] = useState([]);
const queryBalances = async () => {
const client = signingClient || publicClient;
if (!client) return;
const address = signingClient
? await signingClient.signer.getAccounts().then(accs => accs[0]?.address)
: "cosmos1..."; // Replace with any address to query
if (!address) return;
const allBalances = await client.getAllBalances(address);
setBalances(allBalances);
const atomBalance = await client.getBalance(address, "uatom");
console.log("ATOM balance:", atomBalance.amount, atomBalance.denom);
};
return (
);
}
```
## Next Steps
# Sign Typed Data (EIP-712)
Source: https://docs.getpara.com/v2/react/guides/web3-operations/evm/sign-typed-data
Sign structured data using the EIP-712 standard for improved security and UX
Sign structured data according to the EIP-712 standard, which provides a more readable and secure signing experience for users. This guide covers signing typed data with , , and .
## Prerequisites
You need Web3 libraries configured with Para authentication.
## Sign Typed Data
```typescript theme={null}
import { ethers } from "ethers";
async function signTypedData(
signer: ethers.Signer,
domain: any,
types: any,
value: any
) {
const signature = await signer.signTypedData(domain, types, value);
console.log("Signature:", signature);
return signature;
}
```
```typescript theme={null}
async function signTypedData(
walletClient: any,
account: any,
domain: any,
types: any,
primaryType: string,
message: any
) {
const signature = await walletClient.signTypedData({
account,
domain,
types,
primaryType,
message,
});
console.log("Signature:", signature);
return signature;
}
```
```typescript theme={null}
import { useSignTypedData } from "wagmi";
function SignTypedData(
domain: any,
types: any,
primaryType: string,
message: any
) {
const { data: signature, signTypedData } = useSignTypedData();
return (
{signature &&
Signature: {signature}
}
);
}
```
# Setup Smart Account Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/evm/smart-accounts/setup-libraries
Install and configure account abstraction providers like Alchemy, Biconomy, Rhinestone, ZeroDev, and Safe
This guide shows you how to set up various account abstraction libraries to create and manage smart accounts using the standard and Para's Viem integration. These libraries allow you to interact with smart accounts, send user operations, and sponsor gas fees for transactions.
## Setup and Configuration
### Installation
```bash npm theme={null}
npm install --save-exact @aa-sdk/core@latest @account-kit/infra@latest @account-kit/smart-contracts@latest viem
```
```bash yarn theme={null}
yarn add --exact @aa-sdk/core@latest @account-kit/infra@latest @account-kit/smart-contracts@latest viem
```
```bash pnpm theme={null}
pnpm add --save-exact @aa-sdk/core@latest @account-kit/infra@latest @account-kit/smart-contracts@latest viem
```
```bash bun theme={null}
bun add --exact @aa-sdk/core@latest @account-kit/infra@latest @account-kit/smart-contracts@latest viem
```
### Configuration
```typescript useAlchemySmartAccount.ts theme={null}
import { type WalletClient, http, createWalletClient } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { WalletClientSigner } from "@aa-sdk/core";
import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts";
import { alchemy } from "@account-kit/infra";
import { useState, useEffect } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const ALCHEMY_RPC_URL = "https://eth-sepolia.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY"; // Replace with your Alchemy RPC URL
const GAS_POLICY_ID = "YOUR_ALCHEMY_GAS_POLICY_ID"; // Replace with your Alchemy gas policy ID
const salt = "YOUR_SALT"; // Used for account creation.
export const useAlchemySmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [client, setClient] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create a Viem WalletClient with the Para account
const walletClient: WalletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(ALCHEMY_RPC_URL)
});
// Create WalletClientSigner from the Viem WalletClient
const walletClientSigner = new WalletClientSigner(walletClient, "wallet");
// Create modular account Alchemy client with the WalletClientSigner
const alchemyClient = await createModularAccountAlchemyClient({
transport: alchemy({ rpcUrl: ALCHEMY_RPC_URL }),
chain: CHAIN,
signer: walletClientSigner,
policyId: GAS_POLICY_ID,
salt,
});
setClient(alchemyClient);
} catch (error) {
console.error("Failed to initialize Alchemy client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { client, isLoading: isLoading || accountLoading };
};
```
### Installation
```bash npm theme={null}
npm install --save-exact @biconomy/account@latest viem@latest
```
```bash yarn theme={null}
yarn add --exact @biconomy/account@latest viem@latest
```
```bash pnpm theme={null}
pnpm add --save-exact @biconomy/account@latest viem@latest
```
```bash bun theme={null}
bun add --exact @biconomy/account@latest viem@latest
```
### Configuration
```typescript useBiconomySmartAccount.ts theme={null}
import { type WalletClient, http, createWalletClient } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { createSmartAccountClient } from '@biconomy/account';
import { useState, useEffect } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"; // Your RPC endpoint
const BUNDLER_API_KEY = 'YOUR_BICONOMY_BUNDLER_API_KEY'; // From dashboard.biconomy.io
const PAYMASTER_API_KEY = 'YOUR_BICONOMY_PAYMASTER_API_KEY'; // From dashboard.biconomy.io (optional for paymaster)
const BUNDLER_URL = `https://bundler.biconomy.io/api/v2/${CHAIN.id}/${BUNDLER_API_KEY}`; // Biconomy bundler URL
const PAYMASTER_URL = `https://paymaster.biconomy.io/api/v2/${CHAIN.id}/${PAYMASTER_API_KEY}`; // Biconomy paymaster URL (optional)
export const useBiconomySmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [smartAccountClient, setSmartAccountClient] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create a Viem WalletClient with the Para account
const walletClient: WalletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(RPC_URL)
});
// Create Biconomy smart account client with the WalletClient
const client = await createSmartAccountClient({
signer: walletClient,
chainId: CHAIN.id,
bundlerUrl: BUNDLER_URL,
paymasterUrl: PAYMASTER_URL, // Optional; include for gasless tx support
});
setSmartAccountClient(client);
} catch (error) {
console.error("Failed to initialize Biconomy client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { smartAccountClient, isLoading: isLoading || accountLoading };
};
```
### Installation
```bash npm theme={null}
npm install --save-exact @gelatonetwork/smartwallet viem
```
```bash yarn theme={null}
yarn add --exact @gelatonetwork/smartwallet viem
```
```bash pnpm theme={null}
pnpm add --save-exact @gelatonetwork/smartwallet viem
```
```bash bun theme={null}
bun add --exact @gelatonetwork/smartwallet viem
```
### Configuration
```typescript useGelatoSmartAccount.ts theme={null}
import { createPublicClient, http, createWalletClient } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { createGelatoSmartWalletClient, accounts } from "@gelatonetwork/smartwallet";
import { useState, useEffect } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"; // Your RPC endpoint
const GELATO_API_KEY = "YOUR_GELATO_API_KEY"; // Gelato API key
const WALLET_INDEX = 0n; // Account index for deterministic addresses
export const useGelatoSmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [smartWalletClient, setSmartWalletClient] = useState(null);
const [walletClient, setWalletClient] = useState(null);
const [kernelAccount, setKernelAccount] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create a public client for the chain
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(RPC_URL)
});
// Create Gelato Kernel account with Para as the owner
const kernel = await accounts.kernel({
owner: viemAccount,
client: publicClient,
index: WALLET_INDEX,
eip7702: false, // Disable EIP-7702 mode
});
// Create wallet client with the kernel account
const wallet = createWalletClient({
account: kernel,
chain: CHAIN,
transport: http(RPC_URL),
});
// Create Gelato smart wallet client for gasless transactions
const smartWallet = await createGelatoSmartWalletClient(wallet, {
apiKey: GELATO_API_KEY,
});
setKernelAccount(kernel);
setWalletClient(wallet);
setSmartWalletClient(smartWallet);
} catch (error) {
console.error("Failed to initialize Gelato client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { smartWalletClient, walletClient, kernelAccount, isLoading: isLoading || accountLoading };
};
```
### Installation
```bash npm theme={null}
npm install --save-exact permissionless viem
```
```bash yarn theme={null}
yarn add --exact permissionless viem
```
```bash pnpm theme={null}
pnpm add --save-exact permissionless viem
```
```bash bun theme={null}
bun add --exact permissionless viem
```
### Configuration
```typescript usePimlicoSmartAccount.ts theme={null}
import { http, createPublicClient } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { createSmartAccountClient, toSimpleSmartAccount } from "permissionless";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { entryPoint07Address } from "viem/account-abstraction";
import { useState, useEffect } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"; // Your RPC endpoint
const PIMLICO_API_KEY = 'YOUR_PIMLICO_API_KEY'; // From dashboard.pimlico.io
const PIMLICO_URL = `https://api.pimlico.io/v2/${CHAIN.id}/rpc?apikey=${PIMLICO_API_KEY}`;
export const usePimlicoSmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [smartAccountClient, setSmartAccountClient] = useState(null);
const [pimlicoClient, setPimlicoClient] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create Viem PublicClient (needed for smart account)
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(RPC_URL)
});
// Create a SimpleAccount using the Para account as owner
const simpleSmartAccount = await toSimpleSmartAccount({
owner: viemAccount, // Para account acts as the EOA owner
client: publicClient,
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
// Optional: you can specify index for deterministic address
// index: 0n
});
// Create Pimlico client for bundler operations
const pimlico = createPimlicoClient({
transport: http(PIMLICO_URL),
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
});
// Create the Smart Account Client
const client = createSmartAccountClient({
account: simpleSmartAccount,
chain: CHAIN,
bundlerTransport: http(PIMLICO_URL),
paymaster: pimlico, // Optional: for gasless transactions
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlico.getUserOperationGasPrice()).fast
}
}
});
setPimlicoClient(pimlico);
setSmartAccountClient(client);
} catch (error) {
console.error("Failed to initialize Pimlico client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { smartAccountClient, pimlicoClient, isLoading: isLoading || accountLoading };
};
```
### Installation
```bash npm theme={null}
npm install --save-exact @rhinestone/sdk viem @tanstack/react-query
```
```bash yarn theme={null}
yarn add --exact @rhinestone/sdk viem @tanstack/react-query
```
```bash pnpm theme={null}
pnpm add --save-exact @rhinestone/sdk viem @tanstack/react-query
```
```bash bun theme={null}
bun add --exact @rhinestone/sdk viem @tanstack/react-query
```
### Configuration
```typescript useRhinestoneSmartAccount.ts theme={null}
import { useState, useEffect, useCallback } from "react";
import { useWallet, useViemAccount } from "@getpara/react-sdk";
import { RhinestoneSDK, wrapParaAccount } from "@rhinestone/sdk";
import type { Account } from "viem";
// You can get a Rhinestone API key from the Rhinestone dashboard. You don't need a key to use the SDK on testnets.
// We recommend not storing the sponsoredAPI key in the client-side code.
// See https://docs.rhinestone.dev/sdk/security for more information.
const RHINESTONE_API_KEY = process.env.NEXT_PUBLIC_RHINESTONE_API_KEY;
export const useRhinestoneSmartAccount = () => {
const { data: wallet } = useWallet();
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [rhinestoneAccount, setRhinestoneAccount] = useState(null);
const [accountAddress, setAccountAddress] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeAccount = async () => {
if (!viemAccount || !wallet?.id || accountLoading) return;
try {
// Initialize Rhinestone SDK
const rhinestone = new RhinestoneSDK({
apiKey: RHINESTONE_API_KEY,
endpointUrl: RHINESTONE_ENDPOINT,
});
// Wrap Para account for signature compatibility
const wrappedAccount = wrapParaAccount(viemAccount, wallet.id);
// Create Rhinestone cross-chain smart account
const account = await rhinestone.createAccount({
owners: {
type: "ecdsa",
accounts: [wrappedAccount as Account],
},
});
setRhinestoneAccount(account);
setAccountAddress(account.getAddress());
} catch (error) {
console.error("Failed to initialize Rhinestone account:", error);
} finally {
setIsLoading(false);
}
};
initializeAccount();
}, [viemAccount, wallet?.id, accountLoading]);
const sendCrossChainTransaction = useCallback(
async (sourceChains: any[], targetChain: any, calls: any[], tokenRequests: any[]) => {
if (!rhinestoneAccount) throw new Error("Account not initialized");
const transaction = await rhinestoneAccount.sendTransaction({
sourceChains,
targetChain,
calls,
tokenRequests,
sponsored: true, // Enable gas sponsorship
});
return await rhinestoneAccount.waitForExecution(transaction);
},
[rhinestoneAccount]
);
return {
rhinestoneAccount,
accountAddress,
sendCrossChainTransaction,
isLoading: isLoading || accountLoading
};
};
```
### Installation
```bash npm theme={null}
npm install --save-exact permissionless viem
```
```bash yarn theme={null}
yarn add --exact permissionless viem
```
```bash pnpm theme={null}
pnpm add --save-exact permissionless viem
```
```bash bun theme={null}
bun add --exact permissionless viem
```
### Configuration
```typescript useSafeSmartAccount.ts theme={null}
import { createWalletClient, http, createPublicClient } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { createSmartAccountClient } from "permissionless";
import { toSafeSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { entryPoint07Address } from "viem/account-abstraction";
import { useState, useEffect } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"; // Your RPC endpoint
const PIMLICO_API_KEY = 'YOUR_PIMLICO_API_KEY'; // From dashboard.pimlico.io
const PIMLICO_URL = `https://api.pimlico.io/v2/${CHAIN.id}/rpc?apikey=${PIMLICO_API_KEY}`;
export const useSafeSmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [smartAccountClient, setSmartAccountClient] = useState(null);
const [safeAccount, setSafeAccount] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create Viem WalletClient (used as the EOA owner)
const walletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(RPC_URL)
});
// Create Viem PublicClient (needed for smart account)
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(RPC_URL)
});
// Create a Safe account using the WalletClient as owner
const safe = await toSafeSmartAccount({
client: publicClient,
owners: [walletClient],
entryPoint: {
address: entryPoint07Address,
version: "0.7"
},
safeVersion: "1.4.1"
// Optional: index for deterministic address
// index: 0n
});
// Create Pimlico client for bundler operations
const pimlicoClient = createPimlicoClient({
transport: http(PIMLICO_URL),
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
});
// Create the Smart Account Client
const client = createSmartAccountClient({
account: safe,
chain: CHAIN,
bundlerTransport: http(PIMLICO_URL),
paymaster: pimlicoClient, // Optional: for gasless transactions
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
}
}
});
setSafeAccount(safe);
setSmartAccountClient(client);
} catch (error) {
console.error("Failed to initialize Safe client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { smartAccountClient, safeAccount, isLoading: isLoading || accountLoading };
};
```
### Installation
```bash npm theme={null}
npm install --save-exact thirdweb viem
```
```bash yarn theme={null}
yarn add --exact thirdweb viem
```
```bash pnpm theme={null}
pnpm add --save-exact thirdweb viem
```
```bash bun theme={null}
bun add --exact thirdweb viem
```
### Configuration
```typescript useThirdwebSmartAccount.ts theme={null}
import { createWalletClient, http } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { smartWallet } from "thirdweb/wallets";
import { viemAdapter } from "thirdweb/adapters/viem";
import { DEFAULT_ACCOUNT_FACTORY_V0_7 } from "thirdweb/wallets/smart";
import { createThirdwebClient } from "thirdweb";
import { useState, useEffect, useRef } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"; // Your RPC endpoint
const THIRDWEB_CLIENT_ID = "YOUR_THIRDWEB_CLIENT_ID"; // thirdweb client ID
const THIRDWEB_SECRET_KEY = "YOUR_THIRDWEB_SECRET_KEY"; // thirdweb secret
const WALLET_INDEX = 0n; // Account index for deterministic addresses
// Create singleton thirdweb client outside hook
const thirdwebClient = createThirdwebClient({
clientId: THIRDWEB_CLIENT_ID,
secretKey: THIRDWEB_SECRET_KEY,
});
export const useThirdwebSmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [smartAccount, setSmartAccount] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const clientRef = useRef(thirdwebClient);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create Viem wallet client with Para account
const viemWalletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(RPC_URL),
});
// Convert Viem wallet to thirdweb wallet
const personalAccount = viemAdapter.walletClient.fromViem({
walletClient: viemWalletClient,
});
// Generate deterministic salt for the smart wallet
const salt = `0x${WALLET_INDEX.toString(16).padStart(64, "0")}`;
// Configure smart wallet with gas sponsorship
const smartWalletConfig = smartWallet({
chain: CHAIN,
sponsorGas: true, // Enable gasless transactions
factoryAddress: DEFAULT_ACCOUNT_FACTORY_V0_7,
overrides: {
accountSalt: salt, // Deterministic address generation
},
});
// Connect the smart wallet with Para as the signer
const account = await smartWalletConfig.connect({
client: clientRef.current,
personalAccount,
});
setSmartAccount(account);
} catch (error) {
console.error("Failed to initialize Thirdweb client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { smartAccount, thirdwebClient: clientRef.current, isLoading: isLoading || accountLoading };
};
```
### Installation
```bash npm theme={null}
npm install --save-exact @zerodevapp/sdk viem
```
```bash yarn theme={null}
yarn add --exact @zerodevapp/sdk viem
```
```bash pnpm theme={null}
pnpm add --save-exact @zerodevapp/sdk viem
```
```bash bun theme={null}
bun add --exact @zerodevapp/sdk viem
```
### Configuration
```typescript useZeroDevSmartAccount.ts theme={null}
import { createPublicClient, http, createWalletClient } from "viem";
import { sepolia } from "viem/chains";
import { useViemAccount } from "@getpara/react-sdk";
import { createKernelAccount, createKernelAccountClient } from "@zerodevapp/sdk";
import { useState, useEffect } from "react";
// Configuration constants - Replace with your values
const CHAIN = sepolia; // Target chain
const RPC_URL = "https://ethereum-sepolia-rpc.publicnode.com"; // Your RPC endpoint
const ZERODEV_PROJECT_ID = "YOUR_ZERODEV_PROJECT_ID"; // ZeroDev project ID
export const useZeroDevSmartAccount = () => {
const { viemAccount, isLoading: accountLoading } = useViemAccount();
const [kernelClient, setKernelClient] = useState(null);
const [kernelAccount, setKernelAccount] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initializeClient = async () => {
if (!viemAccount || accountLoading) return;
try {
// Create a public client for the chain
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(RPC_URL)
});
// Create wallet client with Para account
const walletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(RPC_URL)
});
// Create Kernel account with Para as the signer
const kernel = await createKernelAccount(publicClient, {
plugins: {
sudo: walletClient
}
});
// Create Kernel account client with ZeroDev bundler
const client = createKernelAccountClient({
account: kernel,
chain: CHAIN,
transport: http(`https://rpc.zerodev.app/api/v2/bundler/${ZERODEV_PROJECT_ID}`),
});
setKernelAccount(kernel);
setKernelClient(client);
} catch (error) {
console.error("Failed to initialize ZeroDev client:", error);
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, accountLoading]);
return { kernelClient, kernelAccount, isLoading: isLoading || accountLoading };
};
```
Smart Wallets contracts are deployed automatically on the first transaction by the provider. Meaning you don't need to deploy them manually. However, you can deploy them manually if you want by signing and sending an empty transaction.
## Next Steps
# Sponsor Transactions with Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/evm/smart-accounts/sponsor-transactions
Enable gasless transactions using paymasters with various AA providers
Enable gasless transactions for users with sponsored gas fees using popular AA providers + Para. This guide covers how to configure gas sponsorship with various smart account libraries.
## Prerequisites
You need a Para-enabled smart account client configured with your AA provider. Gas sponsorship is typically configured during the initial client setup.
## Sponsor Transactions
```typescript theme={null}
import { createModularAccountAlchemyClient } from "@account-kit/smart-contracts";
import { WalletClientSigner } from "@aa-sdk/core";
import { alchemy } from "@account-kit/infra";
// Gas sponsorship is enabled via the policyId parameter
const client = await createModularAccountAlchemyClient({
transport: alchemy({ rpcUrl: ALCHEMY_RPC_URL }),
chain: CHAIN,
signer: walletClientSigner, // Para-enabled WalletClientSigner
policyId: GAS_POLICY_ID, // ← Enables gas sponsorship
salt,
});
// The gas is automatically sponsored based on your policy
const userOpHash = await client.sendUserOperation({
uo: {
target: "0x...", // Recipient address
data: "0x", // Transaction data
value: 0n // ETH value to send
}
});
// Wait for the transaction to be mined
const receipt = await client.waitForUserOperationReceipt({ hash: userOpHash });
```
```typescript theme={null}
import { createSmartAccountClient } from '@biconomy/account';
// Gas sponsorship is enabled via the paymasterUrl parameter
const smartAccountClient = await createSmartAccountClient({
signer: walletClient, // Para-enabled WalletClient
chainId: chain.id,
bundlerUrl,
paymasterUrl, // ← Enables gas sponsorship
});
// Specify SPONSORED mode in paymasterServiceData
const transaction = {
to: "0x...", // Recipient address
data: "0x", // Transaction data
value: "0" // ETH value as string
};
const userOpResponse = await smartAccountClient.sendTransaction(
transaction,
{
paymasterServiceData: {
mode: "SPONSORED" // ← Request gas sponsorship
}
}
);
const receipt = await userOpResponse.wait();
```
```typescript theme={null}
import { createGelatoSmartWalletClient } from "@gelatonetwork/smartwallet";
// Gas sponsorship is enabled via the Gelato API key
const smartWalletClient = await createGelatoSmartWalletClient(
walletClient, // Para-enabled WalletClient with kernel account
{
apiKey: GELATO_API_KEY, // ← Enables gas sponsorship
}
);
// Gas is automatically sponsored when using the smart wallet client
const txHash = await smartWalletClient.sendTransaction({
to: "0x...", // Recipient address
value: 0n, // ETH value to send
data: "0x" // Transaction data
});
// Wait for the transaction to be mined
const receipt = await smartWalletClient.waitForTransactionReceipt({
hash: txHash
});
```
```typescript theme={null}
import { createSmartAccountClient } from "permissionless";
import { createPimlicoClient } from "permissionless/clients/pimlico";
// Create Pimlico client for paymaster operations
const pimlicoClient = createPimlicoClient({
transport: http(PIMLICO_URL),
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
});
// Include paymaster in smart account client
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount, // Para-enabled smart account
chain: CHAIN,
bundlerTransport: http(PIMLICO_URL),
paymaster: pimlicoClient, // ← Enables gas sponsorship
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
}
}
});
// Gas is automatically sponsored via the paymaster
const userOpHash = await smartAccountClient.sendUserOperation({
calls: [{
to: "0x...", // Recipient address
value: 0n, // ETH value to send
data: "0x" // Transaction data
}]
});
// Wait for the transaction to be mined
const receipt = await smartAccountClient.waitForUserOperationReceipt({
hash: userOpHash
});
```
```typescript theme={null}
import { createSmartAccountClient } from "permissionless";
import { toSafeSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
// Safe uses Pimlico as the paymaster provider
const pimlicoClient = createPimlicoClient({
transport: http(PIMLICO_URL),
entryPoint: {
address: entryPoint07Address,
version: "0.7"
}
});
const smartAccountClient = createSmartAccountClient({
account: safeAccount, // Para-enabled Safe account
chain: CHAIN,
bundlerTransport: http(PIMLICO_URL),
paymaster: pimlicoClient, // ← Enables gas sponsorship
userOperation: {
estimateFeesPerGas: async () => {
return (await pimlicoClient.getUserOperationGasPrice()).fast
}
}
});
// Gas is automatically sponsored via the paymaster
const userOpHash = await smartAccountClient.sendUserOperation({
calls: [{
to: "0x...", // Recipient address
value: 0n, // ETH value to send
data: "0x" // Transaction data
}]
});
// Wait for the transaction to be mined
const receipt = await smartAccountClient.waitForUserOperationReceipt({
hash: userOpHash
});
```
```typescript theme={null}
import { smartWallet } from "thirdweb/wallets";
import { sendTransaction } from "thirdweb";
// Gas sponsorship is enabled via the sponsorGas flag
const smartWalletConfig = smartWallet({
chain: CHAIN,
sponsorGas: true, // ← Enables gas sponsorship
factoryAddress: DEFAULT_ACCOUNT_FACTORY_V0_7,
overrides: {
accountSalt: salt,
},
});
// Connect the smart wallet with Para as the signer
const smartAccount = await smartWalletConfig.connect({
client: thirdwebClient,
personalAccount, // Para-enabled personal account
});
// Gas is automatically sponsored when sponsorGas is enabled
const transaction = {
chain: CHAIN,
client: thirdwebClient,
to: "0x...", // Recipient address
value: 0n, // ETH value to send
data: "0x" // Transaction data
};
const result = await sendTransaction({
transaction,
account: smartAccount // Uses the sponsored smart wallet
});
console.log("Sponsored transaction mined:", result.transactionHash);
```
```typescript theme={null}
import { createKernelAccountClient, createZeroDevPaymasterClient } from "@zerodevapp/sdk";
import { http } from "viem";
// ZeroDev automatically sponsors gas when using their bundler URL
const kernelClient = createKernelAccountClient({
account: kernelAccount, // Para-enabled kernel account
chain: CHAIN,
transport: http(
`https://rpc.zerodev.app/api/v2/bundler/${ZERODEV_PROJECT_ID}`
), // ← Bundler URL includes sponsorship
});
// Optional: For explicit paymaster configuration
const paymasterClient = createZeroDevPaymasterClient({
chain: CHAIN,
transport: http(
`https://rpc.zerodev.app/api/v2/paymaster/${ZERODEV_PROJECT_ID}`
)
});
// Gas is automatically sponsored via the ZeroDev infrastructure
const userOpHash = await kernelClient.sendUserOperation({
callData: await kernelAccount.encodeCalls([{
to: "0x...", // Recipient address
value: 0n, // ETH value to send
data: "0x" // Transaction data
}])
});
// Wait for the transaction to be mined
const receipt = await kernelClient.waitForUserOperationReceipt({
hash: userOpHash
});
```
## Key Points
* **Gas sponsorship is configured during client setup** - Each provider has a specific parameter or configuration that enables sponsorship (policyId, paymasterUrl, apiKey, etc.)
* **Transactions are automatically sponsored** - Once configured, transactions sent through the smart account client will have their gas fees covered
* **No ETH required in user wallets** - Users can interact with your dApp without holding ETH for gas fees
* **Provider-specific limits may apply** - Check your provider's dashboard for sponsorship limits and policies
* **Smart Wallet Deployment** - Providers automatically handle smart wallet deployment if the account does not exist on first transaction
# Verify Signatures with EVM Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/evm/verify-signatures
Verify message and transaction signatures using Ethers, Viem, or Wagmi
Verify the authenticity of signed messages and typed data to ensure they originated from the expected address.
## Prerequisites
You need Web3 libraries configured with Para authentication.
## Verify Personal Signatures
```typescript theme={null}
import { ethers } from "ethers";
async function verifyPersonalSignature(
message: string,
signature: string,
signerAddress: string
) {
const recoveredAddress = ethers.verifyMessage(message, signature);
const isValid = recoveredAddress.toLowerCase() === signerAddress.toLowerCase();
console.log("Signature valid:", isValid);
return isValid;
}
```
```typescript theme={null}
import { verifyMessage } from "viem";
async function verifyPersonalSignature(
address: `0x${string}`,
message: string,
signature: `0x${string}`
) {
const isValid = await verifyMessage({
address,
message,
signature,
});
console.log("Signature valid:", isValid);
return isValid;
}
```
```typescript theme={null}
import { useVerifyMessage } from "wagmi";
function VerifyPersonalSignature({
address,
message,
signature,
}: {
address: `0x${string}`;
message: string;
signature: `0x${string}`;
}) {
const { data: isValid, isLoading } = useVerifyMessage({
address,
message,
signature,
});
if (isLoading) return
Typed Data Signature valid: {isValid ? "Yes" : "No"}
;
}
```
## Next Steps
# Watch Contract Events
Source: https://docs.getpara.com/v2/react/guides/web3-operations/evm/watch-events
Listen to smart contract events and parse event logs in real-time
Subscribe to and filter smart contract events to react to on-chain activity in real-time.
## Prerequisites
You need Web3 libraries configured with Para authentication.
## Watch Contract Events
```typescript theme={null}
import { ethers } from "ethers";
const ERC20_ABI = [
"event Transfer(address indexed from, address indexed to, uint256 value)"
];
async function watchTransferEvents(
provider: ethers.Provider,
tokenAddress: string
) {
const contract = new ethers.Contract(
tokenAddress,
ERC20_ABI,
provider
);
contract.on("Transfer", (from, to, value, event) => {
console.log(`Transfer Event: ${from} -> ${to}, Value: ${ethers.formatUnits(value, 18)}`);
console.log("Event details:", event);
});
console.log(`Listening for Transfer events on ${tokenAddress}...`);
}
async function stopWatchingTransferEvents(
provider: ethers.Provider,
tokenAddress: string
) {
const contract = new ethers.Contract(
tokenAddress,
ERC20_ABI,
provider
);
contract.off("Transfer");
console.log(`Stopped listening for Transfer events on ${tokenAddress}.`);
}
```
```typescript theme={null}
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";
const ERC20_ABI = [
{
anonymous: false,
inputs: [
{ indexed: true, name: "from", type: "address" },
{ indexed: true, name: "to", type: "address" },
{ indexed: false, name: "value", type: "uint256" },
],
name: "Transfer",
type: "event",
},
] as const;
async function watchTransferEvents(
publicClient: any,
tokenAddress: `0x${string}`
) {
const unwatch = publicClient.watchContractEvent({
address: tokenAddress,
abi: ERC20_ABI,
eventName: "Transfer",
onLogs: (logs) => {
for (const log of logs) {
console.log(`Transfer Event: ${log.args.from} -> ${log.args.to}, Value: ${log.args.value}`);
console.log("Log details:", log);
}
},
});
console.log(`Listening for Transfer events on ${tokenAddress}...`);
return unwatch; // Return the unwatch function to stop listening later
}
```
```typescript theme={null}
import { useWatchContractEvent } from "wagmi";
const ERC20_ABI = [
{
anonymous: false,
inputs: [
{ indexed: true, name: "from", type: "address" },
{ indexed: true, name: "to", type: "address" },
{ indexed: false, name: "value", type: "uint256" },
],
name: "Transfer",
type: "event",
},
] as const;
function WatchTransferEvents({ tokenAddress }: { tokenAddress: `0x${string}` }) {
useWatchContractEvent({
address: tokenAddress,
abi: ERC20_ABI,
eventName: "Transfer",
onLogs: (logs) => {
for (const log of logs) {
console.log(`Transfer Event: ${log.args.from} -> ${log.args.to}, Value: ${log.args.value}`);
console.log("Log details:", log);
}
},
});
return
Listening for Transfer events on {tokenAddress}...
;
}
```
## Next Steps
# Get Wallet Data
Source: https://docs.getpara.com/v2/react/guides/web3-operations/get-wallet-address
Access wallet addresses and information using Para React hooks
## Get Current Wallet
Use the `useWallet` hook to access the currently selected wallet in the `ParaModal`.
```tsx theme={null}
import { useWallet } from '@getpara/react-sdk';
export default function CurrentWallet() {
const { data: wallet } = useWallet();
if (!wallet) return
No wallet connected
;
return (
Address: {wallet.address}
Type: {wallet.scheme}
ID: {wallet.id}
);
}
```
## Get All Wallets
Use the `useAccount` hook to access all user wallets for both embedded and external types.
```tsx theme={null}
import { useAccount } from '@getpara/react-sdk';
export default function AllWallets() {
const { embedded, external } = useAccount();
// Get all embedded wallets
const wallets = embedded.wallets; // Record
const walletList = Object.values(wallets);
return (
{walletList.map((wallet) => (
{wallet.scheme}: {wallet.address}
))}
);
}
```
## Filter Wallets by Type
Access wallets filtered by blockchain type using the Para client.
```tsx theme={null}
import { useClient } from '@getpara/react-sdk';
export default function WalletsByType() {
// useClient hook to access Para client
const para = useClient();
// Get wallets by type
const evmWallets = para.getWalletsByType('EVM');
const solanaWallets = para.getWalletsByType('SOLANA');
const cosmosWallets = para.getWalletsByType('COSMOS');
}
```
## Wallet Properties
Each wallet object for embedded wallets has the following properties:
## Next Steps
# Query Wallet Balance with Para
Source: https://docs.getpara.com/v2/react/guides/web3-operations/query-wallet-balance
Get wallet balances directly using Para's built-in balance methods
Para provides a simple React hook to query the native balance of a wallet. This is useful for checking available funds before performing transactions.
### useWalletBalance
### Basic Usage
```tsx theme={null}
import { useWalletBalance } from '@getpara/react-sdk';
const { data: balance, isLoading, error } = useWalletBalance();
// Balance is returned as a string in wei (for EVM)
console.log(balance); // "1000000000000000000" (1 ETH in wei)
```
### Query Specific Wallet
```tsx theme={null}
import { useWalletBalance } from '@getpara/react-sdk';
const { data: balance } = useWalletBalance({
walletId: 'wallet_123'
});
console.log(balance); // Balance in wei
```
### External Wallet with RPC
```tsx theme={null}
import { useWalletBalance } from '@getpara/react-sdk';
const { data: balance } = useWalletBalance({
walletId: 'external_wallet_id',
rpcUrl: 'https://mainnet.infura.io/v3/YOUR_KEY'
});
console.log(balance); // Balance from external RPC
```
## Important Notes
* **EVM only**: Currently only supports EVM wallets. Returns `null` for COSMOS and SOLANA wallets
* **Native balance only**: Does not support token balances
* **Wei format**: Returns balance as a string in wei (smallest unit)
* **No formatting**: Returns raw balance value without formatting or symbol information
## Next Steps
Work directly with popular blockchain libraries for a more comprehensive approach to wallet interactions.
# Sign Messages with Para
Source: https://docs.getpara.com/v2/react/guides/web3-operations/sign-with-para
Learn how to sign a "Hello, Para!" message using the Para SDK
The `signMessage` method is a **low-level API** that signs raw bytes directly without any modifications. This is useful for verifying your Para integration with a simple "Hello, Para!" test after initial setup and authentication.
**Important:** `signMessage` signs raw bytes without standard modifications like EIP-191 message prefixes that libraries like Ethers and Viem automatically add. For production use, always use proper Web3 libraries that handle message formatting, encoding standards, and chain-specific requirements.
### Message Signing
This method signs the exact bytes you provide - perfect for initial "hello world" testing:
```tsx SignMessageExample.tsx theme={null}
import { useSignMessage, useWallet } from '@getpara/react-sdk';
export default function SignMessageExample() {
const { mutateAsync: signMessageAsync } = useSignMessage();
const { data: wallet } = useWallet();
const handleSign = async () => {
if (!wallet) return;
const message = "Hello, Para!";
// Encode message to base64
const messageBase64 = btoa(message);
const result = await signMessageAsync({
walletId: wallet.id,
messageBase64
});
console.log('Signature result:', result);
};
return (
);
}
```
**When to use signMessage:** This method is best suited for signing simple text messages and basic authentication flows. For complex operations like transactions, typed data (EIP-712), or chain-specific functionality, **you should use the appropriate Web3 library** (Viem, Ethers, Solana Web3.js, CosmJS) as shown in the Next Steps below. These libraries provide proper encoding, type safety, and chain-specific features that signMessage alone cannot offer.
## Next Steps
Now that you've successfully verified your Para setup with a simple message signature, it's time to move on to more advanced operations. Depending on your target blockchain, you can explore the following libraries that integrate seamlessly with Para for signing transactions, typed data, and more:
# Set Compute Units and Priority Fees
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/compute-units
Configure compute budget and priority fees for Solana transactions
Optimize your Solana transactions by setting compute unit limits and priority fees. This helps ensure transactions succeed during network congestion and controls execution costs.
```typescript theme={null}
import { useParaSolana } from './hooks/useParaSolana';
import {
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
PublicKey,
ComputeBudgetProgram
} from '@solana/web3.js';
function ComputeUnitsExample() {
const { connection, signer } = useParaSolana();
const sendWithPriorityFee = async () => {
const recipient = new PublicKey("RECIPIENT_ADDRESS");
// Get recent prioritization fees
const recentFees = await connection.getRecentPrioritizationFees();
const avgFee = recentFees.reduce((sum, fee) => sum + fee.prioritizationFee, 0) / recentFees.length;
const priorityFee = Math.ceil(avgFee * 1.2); // 20% above average
const transaction = new Transaction()
.add(
// Set compute unit limit
ComputeBudgetProgram.setComputeUnitLimit({
units: 200000, // Adjust based on your transaction needs
})
)
.add(
// Set priority fee
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee,
})
)
.add(
// Your actual transaction instruction
SystemProgram.transfer({
fromPubkey: signer.sender,
toPubkey: recipient,
lamports: LAMPORTS_PER_SOL * 0.1,
})
);
try {
const signature = await signer.sendTransaction(transaction, {
skipPreflight: false,
maxRetries: 3,
});
console.log("Transaction sent with priority fee:", signature);
console.log("Priority fee used:", priorityFee, "microLamports");
const confirmation = await connection.confirmTransaction(signature, "confirmed");
console.log("Transaction confirmed:", confirmation);
} catch (error) {
console.error("Transaction failed:", error);
}
};
return ;
}
```
```typescript theme={null}
import { useParaSolanaV2 } from './hooks/useParaSolanaV2';
import {
pipe,
createTransactionMessage,
setTransactionMessageFeePayer,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstruction,
getSystemInstruction,
getSetComputeUnitLimitInstruction,
getSetComputeUnitPriceInstruction,
address,
lamports
} from '@solana/web3.js';
function ComputeUnitsExample() {
const { rpc, signer } = useParaSolanaV2();
const sendWithPriorityFee = async () => {
const recipient = address("RECIPIENT_ADDRESS");
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
// Get recent prioritization fees
const { value: recentFees } = await rpc.getRecentPrioritizationFees().send();
const avgFee = recentFees.reduce((sum, fee) => sum + fee.prioritizationFee, 0n) / BigInt(recentFees.length);
const priorityFee = (avgFee * 120n) / 100n; // 20% above average
const transaction = pipe(
createTransactionMessage({ version: 0 }),
tx => setTransactionMessageFeePayer(signer.address, tx),
tx => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
tx => appendTransactionMessageInstruction(
getSetComputeUnitLimitInstruction({
units: 200000,
}),
tx
),
tx => appendTransactionMessageInstruction(
getSetComputeUnitPriceInstruction({
microLamports: priorityFee,
}),
tx
),
tx => appendTransactionMessageInstruction(
getSystemInstruction({
amount: lamports(100_000_000n),
destination: recipient,
source: signer.address,
}),
tx
)
);
try {
const [signature] = await signer.signAndSendTransactions([transaction]);
console.log("Transaction sent with priority fee:", signature);
console.log("Priority fee used:", priorityFee.toString(), "microLamports");
const status = await rpc.getSignatureStatuses([signature]).send();
console.log("Transaction status:", status);
} catch (error) {
console.error("Transaction failed:", error);
}
};
return ;
}
```
```typescript theme={null}
import { useParaAnchor } from './hooks/useParaAnchor';
import {
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
PublicKey,
ComputeBudgetProgram
} from '@solana/web3.js';
import * as anchor from '@project-serum/anchor';
function ComputeUnitsExample() {
const provider = useParaAnchor();
const sendWithPriorityFee = async () => {
const program = new anchor.Program(idl, provider);
// Get recent prioritization fees
const recentFees = await provider.connection.getRecentPrioritizationFees();
const avgFee = recentFees.reduce((sum, fee) => sum + fee.prioritizationFee, 0) / recentFees.length;
const priorityFee = Math.ceil(avgFee * 1.2);
// Create the main instruction
const ix = await program.methods
.someMethod()
.accounts({
user: provider.publicKey,
// ... other accounts
})
.instruction();
// Build transaction with compute budget instructions
const transaction = new Transaction()
.add(
ComputeBudgetProgram.setComputeUnitLimit({
units: 300000, // Higher for complex program calls
})
)
.add(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: priorityFee,
})
)
.add(ix);
try {
const signature = await provider.sendAndConfirm(transaction);
console.log("Transaction sent with priority fee:", signature);
console.log("Priority fee used:", priorityFee, "microLamports");
} catch (error) {
console.error("Transaction failed:", error);
}
};
return ;
}
```
## Next Steps
# Configure RPC Endpoints with Solana Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/configure-rpc
Set up and configure Solana RPC endpoints and connections using Web3.js or Anchor
Configure custom RPC endpoints for Solana to optimize performance, use private nodes, or connect to different networks. This guide covers RPC setup for all supported libraries.
```typescript theme={null}
import { ParaSolanaWeb3Signer } from '@getpara/solana-web3.js-v1-integration';
import { Connection, clusterApiUrl, Commitment } from '@solana/web3.js';
import { para } from './para';
function useCustomRPC() {
// Using Solana public RPC endpoints
const mainnetConnection = new Connection(
clusterApiUrl('mainnet-beta'),
'confirmed'
);
const devnetConnection = new Connection(
clusterApiUrl('devnet'),
'confirmed'
);
// Using custom RPC endpoint (e.g., QuickNode, Alchemy, Helius)
const customConnection = new Connection(
'https://your-custom-rpc-endpoint.com',
{
commitment: 'confirmed',
wsEndpoint: 'wss://your-custom-websocket-endpoint.com',
httpHeaders: {
'Authorization': 'Bearer YOUR_API_KEY',
},
disableRetryOnRateLimit: false,
confirmTransactionInitialTimeout: 30000,
}
);
// Create signers with different connections
const mainnetSigner = new ParaSolanaWeb3Signer(para, mainnetConnection);
const devnetSigner = new ParaSolanaWeb3Signer(para, devnetConnection);
const customSigner = new ParaSolanaWeb3Signer(para, customConnection);
// Example: Get network performance metrics
const checkPerformance = async () => {
const start = Date.now();
const slot = await customConnection.getSlot();
const latency = Date.now() - start;
console.log('Current slot:', slot);
console.log('RPC latency:', latency, 'ms');
const perfSamples = await customConnection.getRecentPerformanceSamples(5);
console.log('Recent performance:', perfSamples);
};
return { mainnetSigner, devnetSigner, customSigner, checkPerformance };
}
```
```typescript theme={null}
import { createParaSolanaSigner } from '@getpara/solana-signers-v2-integration';
import { createSolanaRpc, createSolanaRpcSubscriptions } from '@solana/web3.js';
import { para } from './para';
function useCustomRPC() {
// Mainnet RPC
const mainnetRpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
const mainnetRpcSubscriptions = createSolanaRpcSubscriptions('wss://api.mainnet-beta.solana.com');
// Devnet RPC
const devnetRpc = createSolanaRpc('https://api.devnet.solana.com');
// Custom RPC with authentication
const customRpc = createSolanaRpc('https://your-custom-rpc-endpoint.com', {
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
},
});
// Create signers with different RPC connections
const mainnetSigner = createParaSolanaSigner({ para, rpc: mainnetRpc });
const devnetSigner = createParaSolanaSigner({ para, rpc: devnetRpc });
const customSigner = createParaSolanaSigner({ para, rpc: customRpc });
// Example: Monitor slot updates via WebSocket
const monitorSlots = async () => {
const slotNotifications = await mainnetRpcSubscriptions
.slotNotifications()
.subscribe();
for await (const notification of slotNotifications) {
console.log('New slot:', notification.slot);
console.log('Parent:', notification.parent);
console.log('Root:', notification.root);
}
};
// Example: Get RPC health
const checkHealth = async () => {
const health = await customRpc.getHealth().send();
console.log('RPC health:', health);
const version = await customRpc.getVersion().send();
console.log('RPC version:', version);
};
return { mainnetSigner, devnetSigner, customSigner, monitorSlots, checkHealth };
}
```
```typescript theme={null}
import { ParaSolanaWeb3Signer } from '@getpara/solana-web3.js-v1-integration';
import { Connection, Transaction, VersionedTransaction } from '@solana/web3.js';
import * as anchor from '@project-serum/anchor';
import { para } from './para';
function useCustomAnchorProvider() {
// Configure connection with custom options
const connection = new Connection(
'https://your-custom-rpc-endpoint.com',
{
commitment: 'confirmed',
wsEndpoint: 'wss://your-custom-websocket-endpoint.com',
httpHeaders: {
'Authorization': 'Bearer YOUR_API_KEY',
},
}
);
const signer = new ParaSolanaWeb3Signer(para, connection);
// Create Anchor wallet adapter
const wallet = {
publicKey: signer.sender,
signTransaction: async (tx: T): Promise => {
return await signer.signTransaction(tx);
},
signAllTransactions: async (txs: T[]): Promise => {
return await Promise.all(txs.map((tx) => signer.signTransaction(tx)));
},
};
// Create provider with custom options
const provider = new anchor.AnchorProvider(
connection,
wallet,
{
commitment: 'confirmed',
preflightCommitment: 'confirmed',
skipPreflight: false,
}
);
// Set as default provider
anchor.setProvider(provider);
// Example: Test connection
const testConnection = async () => {
const blockHeight = await connection.getBlockHeight();
console.log('Current block height:', blockHeight);
const epoch = await connection.getEpochInfo();
console.log('Epoch info:', epoch);
const supply = await connection.getSupply();
console.log('Total supply:', supply);
};
return { provider, testConnection };
}
```
## Next Steps
# Execute Transactions with Solana Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/execute-transactions
Interact with Solana programs and execute complex transactions using Web3.js or Anchor
Execute transactions on the Solana blockchain using Para's integrated signers. This includes signing and broadcasting transactions to the network.
```typescript theme={null}
import { useParaSolana } from './hooks/useParaSolana';
import { Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
function SendTransaction() {
const { connection, signer } = useParaSolana();
const sendSOL = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const recipient = new PublicKey("RECIPIENT_ADDRESS_HERE");
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: signer.sender,
toPubkey: recipient,
lamports: LAMPORTS_PER_SOL * 0.1,
})
);
try {
const signature = await signer.sendTransaction(transaction, {
skipPreflight: false,
preflightCommitment: "confirmed",
});
console.log("Transaction signature:", signature);
const confirmation = await connection.confirmTransaction(signature, "confirmed");
console.log("Transaction confirmed:", confirmation);
} catch (error) {
console.error("Transaction failed:", error);
}
};
return ;
}
```
```typescript theme={null}
import { useParaSolanaKit } from './hooks/useParaSolanaKit';
import { Transaction, SystemProgram, LAMPORTS_PER_SOL } from '@solana/kit';
function SendTransaction() {
const { rpc, signer } = useParaSolanaKit();
const sendSOL = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const recipient = "RECIPIENT_ADDRESS_HERE";
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: signer.sender,
toPubkey: recipient,
lamports: LAMPORTS_PER_SOL * 0.1,
})
);
const signatures = await signer.signAndSendTransactions([transaction]);
const signature = signatures[0];
console.log("Transaction signature:", signature);
const latestBlockhash = await rpc.getLatestBlockhash();
await rpc.confirmTransaction({
signature,
blockhash: latestBlockhash.value.blockhash,
lastValidBlockHeight: latestBlockhash.value.lastValidBlockHeight
});
console.log("Transaction confirmed");
};
return ;
}
```
```typescript theme={null}
import { useParaAnchor } from './hooks/useParaAnchor';
import * as anchor from '@project-serum/anchor';
import { SystemProgram, LAMPORTS_PER_SOL } from '@solana/web3.js';
function AnchorTransaction() {
const provider = useParaAnchor();
const executeProgram = async () => {
if (provider.wallet.publicKey.equals(SystemProgram.programId)) {
console.error("No wallet connected. Please authenticate first.");
return;
}
const program = new anchor.Program(idl, provider);
try {
const tx = await program.methods
.initialize()
.accounts({
user: provider.publicKey,
systemProgram: SystemProgram.programId,
})
.rpc();
console.log("Transaction signature:", tx);
const confirmation = await provider.connection.confirmTransaction(tx, "confirmed");
console.log("Transaction confirmed:", confirmation);
} catch (error) {
console.error("Transaction failed:", error);
}
};
return ;
}
```
## Next Steps
# Get Solana Transaction Status
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/get-transaction-status
Check transaction confirmation status and finality on Solana
Monitor transaction confirmation status and wait for finality on Solana. This guide covers checking transaction results and understanding commitment levels.
```typescript theme={null}
import { useParaSolana } from './hooks/useParaSolana';
import { TransactionSignature, Commitment } from '@solana/web3.js';
function TransactionStatus() {
const { connection } = useParaSolana();
const checkTransactionStatus = async (signature: string) => {
try {
// Get signature status
const status = await connection.getSignatureStatus(signature);
if (status.value === null) {
console.log("Transaction not found");
return null;
}
console.log("Confirmations:", status.value.confirmations || "Max (32+)");
console.log("Confirmation status:", status.value.confirmationStatus);
console.log("Slot:", status.value.slot);
if (status.value.err) {
console.error("Transaction failed:", status.value.err);
return false;
}
return status.value.confirmationStatus;
} catch (error) {
console.error("Error checking status:", error);
throw error;
}
};
const waitForConfirmation = async (
signature: string,
commitment: Commitment = 'confirmed'
) => {
try {
const startTime = Date.now();
// Wait for confirmation with timeout
const confirmation = await connection.confirmTransaction(
signature,
commitment
);
const elapsed = Date.now() - startTime;
console.log(`Transaction confirmed in ${elapsed}ms`);
if (confirmation.value.err) {
throw new Error(`Transaction failed: ${confirmation.value.err}`);
}
// Get detailed transaction info
const txInfo = await connection.getTransaction(signature, {
commitment,
maxSupportedTransactionVersion: 0,
});
if (txInfo) {
console.log("Block time:", new Date(txInfo.blockTime! * 1000));
console.log("Fee:", txInfo.meta?.fee, "lamports");
console.log("Compute units consumed:", txInfo.meta?.computeUnitsConsumed);
}
return txInfo;
} catch (error) {
console.error("Confirmation error:", error);
throw error;
}
};
return (
);
}
```
```typescript theme={null}
import { useParaSolanaV2 } from './hooks/useParaSolanaV2';
import { Signature, Commitment } from '@solana/web3.js';
function TransactionStatus() {
const { rpc } = useParaSolanaV2();
const checkTransactionStatus = async (signature: Signature) => {
try {
// Get multiple signature statuses
const { value: statuses } = await rpc
.getSignatureStatuses([signature])
.send();
const status = statuses[0];
if (!status) {
console.log("Transaction not found");
return null;
}
console.log("Confirmations:", status.confirmations || "Max (32+)");
console.log("Confirmation status:", status.confirmationStatus);
console.log("Slot:", status.slot);
if (status.err) {
console.error("Transaction failed:", status.err);
return false;
}
return status;
} catch (error) {
console.error("Error checking status:", error);
throw error;
}
};
const waitForConfirmation = async (
signature: Signature,
commitment: Commitment = 'confirmed'
) => {
try {
const startTime = Date.now();
// Poll for confirmation
let confirmed = false;
while (!confirmed) {
const { value: statuses } = await rpc
.getSignatureStatuses([signature])
.send();
const status = statuses[0];
if (status?.confirmationStatus === commitment ||
status?.confirmationStatus === 'finalized') {
confirmed = true;
if (status.err) {
throw new Error(`Transaction failed: ${JSON.stringify(status.err)}`);
}
}
// Wait before next poll
if (!confirmed) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Timeout after 30 seconds
if (Date.now() - startTime > 30000) {
throw new Error('Transaction confirmation timeout');
}
}
const elapsed = Date.now() - startTime;
console.log(`Transaction confirmed in ${elapsed}ms`);
// Get transaction details
const { value: txInfo } = await rpc
.getTransaction(signature, {
commitment,
maxSupportedTransactionVersion: 0,
})
.send();
if (txInfo) {
console.log("Block time:", new Date(txInfo.blockTime * 1000));
console.log("Fee:", txInfo.meta.fee, "lamports");
console.log("Compute units:", txInfo.meta.computeUnitsConsumed);
}
return txInfo;
} catch (error) {
console.error("Confirmation error:", error);
throw error;
}
};
return (
);
}
```
## Next Steps
# Send Tokens with Solana Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/send-tokens
Transfer SOL tokens using Solana Web3.js or Anchor with Para wallets
Transfer SOL tokens between wallets using Para's integrated signers with different Solana libraries.
```typescript theme={null}
import { useParaSolana } from './hooks/useParaSolana';
import { Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
function SendTokens() {
const { connection, signer } = useParaSolana();
const sendSOL = async (recipient: string, amount: number) => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: signer.sender,
toPubkey: new PublicKey(recipient),
lamports: LAMPORTS_PER_SOL * amount,
})
);
const signature = await signer.sendTransaction(transaction);
console.log("Transaction signature:", signature);
await connection.confirmTransaction(signature, "confirmed");
console.log("Transaction confirmed");
return signature;
};
return (
);
}
```
```typescript theme={null}
import { useParaSolanaKit } from './hooks/useParaSolanaKit';
import { LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
import {
createTransactionMessage,
getTransferSolInstruction,
setTransactionMessageFeePayerSigner,
setTransactionMessageLifetimeUsingBlockhash,
appendTransactionMessageInstructions,
signTransactionMessageWithSigners,
lamports,
pipe,
} from '@solana/kit';
function SendTokens() {
const { rpc, signer } = useParaSolanaKit();
const sendSOL = async (recipient: string, amount: number) => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const transferAmount = lamports(LAMPORTS_PER_SOL * amount);
const recipientPublicKey = new PublicKey(recipient);
const transferInstruction = getTransferSolInstruction({
source: signer.publicKey,
destination: recipientPublicKey,
amount: transferAmount,
});
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = pipe(
createTransactionMessage({ version: 0 }),
(tx) => setTransactionMessageFeePayerSigner(signer.publicKey, tx),
(tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
(tx) => appendTransactionMessageInstructions([transferInstruction], tx)
);
const signedTransaction = await signTransactionMessageWithSigners(transactionMessage);
const signature = await rpc.sendTransaction(signedTransaction).send();
console.log("Transaction signature:", signature);
await rpc.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight
});
console.log("Transaction confirmed");
return signature;
};
return (
);
}
```
```typescript theme={null}
import { useParaAnchor } from './hooks/useParaAnchor';
import { Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
function SendTokens() {
const provider = useParaAnchor();
const sendSOL = async (recipient: string, amount: number) => {
if (provider.wallet.publicKey.equals(SystemProgram.programId)) {
console.error("No wallet connected. Please authenticate first.");
return;
}
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: provider.wallet.publicKey,
toPubkey: new PublicKey(recipient),
lamports: LAMPORTS_PER_SOL * amount,
})
);
const signature = await provider.sendAndConfirm(transaction);
console.log("Transaction signature:", signature);
console.log("Transaction confirmed");
return signature;
};
return (
);
}
```
## Next Steps
# Setup Solana Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/setup-libraries
Install and configure Solana Web3.js or Anchor for use with Para SDK
Para supports multiple Solana libraries for blockchain interactions. Choose for standard operations, (@solana/web3.js-v2) for modern signing interfaces, or for program development.
## Installation
If using `@solana/signers` and the `@getpara/react-sdk` you can use our hook to access the Solana signer without any additional setup.
```bash npm theme={null}
npm install @getpara/solana-web3.js-v1-integration @solana/web3.js --save-exact
```
```bash yarn theme={null}
yarn add @getpara/solana-web3.js-v1-integration @solana/web3.js --exact
```
```bash pnpm theme={null}
pnpm add @getpara/solana-web3.js-v1-integration @solana/web3.js --save-exact
```
```bash bun theme={null}
bun add @getpara/solana-web3.js-v1-integration @solana/web3.js --exact
```
```bash npm theme={null}
npm install @getpara/solana-signers-v2-integration @solana/kit --save-exact
```
```bash yarn theme={null}
yarn add @getpara/solana-signers-v2-integration @solana/kit --exact
```
```bash pnpm theme={null}
pnpm add @getpara/solana-signers-v2-integration @solana/kit --save-exact
```
```bash bun theme={null}
bun add @getpara/solana-signers-v2-integration @solana/kit --exact
```
```bash npm theme={null}
npm install @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor --save-exact
```
```bash yarn theme={null}
yarn add @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor --exact
```
```bash pnpm theme={null}
pnpm add @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor --save-exact
```
```bash bun theme={null}
bun add @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor --exact
```
## Library Setup
```typescript hooks/useParaSolana.ts theme={null}
import { useMemo } from 'react';
import { useClient, useAccount } from '@getpara/react-sdk';
import { ParaSolanaWeb3Signer } from '@getpara/solana-web3.js-v1-integration';
import { Connection, clusterApiUrl } from '@solana/web3.js';
export function useParaSolana() {
const para = useClient();
const { isConnected } = useAccount();
const { connection, signer } = useMemo(() => {
const connection = new Connection(clusterApiUrl('mainnet-beta'));
if (!para || !isConnected) {
return { connection, signer: null };
}
const wallets = para.getWalletsByType({ type: 'Solana' });
if (!wallets || wallets.length === 0) {
return { connection, signer: null };
}
const signer = new ParaSolanaWeb3Signer(para, connection);
return { connection, signer };
}, [para, isConnected]);
return { connection, signer };
}
```
```typescript hooks/useParaSolanaKit.ts theme={null}
import { useMemo } from 'react';
import { useClient, useAccount } from '@getpara/react-sdk';
import { createParaSolanaSigner } from '@getpara/solana-signers-v2-integration';
import { createSolanaRpc } from '@solana/kit';
export function useParaSolanaKit() {
const para = useClient();
const { isConnected } = useAccount();
const { rpc, signer } = useMemo(() => {
const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
if (!para || !isConnected) {
return { rpc, signer: null };
}
const wallets = para.getWalletsByType({ type: 'Solana' });
if (!wallets || wallets.length === 0) {
return { rpc, signer: null };
}
const signer = createParaSolanaSigner({ para, rpc });
return { rpc, signer };
}, [para, isConnected]);
return { rpc, signer };
}
```
```typescript hooks/useParaAnchor.ts theme={null}
import { useMemo } from 'react';
import { useClient, useAccount } from '@getpara/react-sdk';
import { ParaSolanaWeb3Signer } from '@getpara/solana-web3.js-v1-integration';
import { Connection, clusterApiUrl, Transaction, VersionedTransaction, SystemProgram } from '@solana/web3.js';
import * as anchor from '@project-serum/anchor';
export function useParaAnchor() {
const para = useClient();
const { isConnected } = useAccount();
const provider = useMemo(() => {
const connection = new Connection(clusterApiUrl('mainnet-beta'));
if (!para || !isConnected) {
const readOnlyWallet = {
publicKey: SystemProgram.programId,
signTransaction: async (tx: T): Promise => {
throw new Error('Read-only provider: Authenticate to sign transactions.');
},
signAllTransactions: async (txs: T[]): Promise => {
throw new Error('Read-only provider: Authenticate to sign transactions.');
},
};
return new anchor.AnchorProvider(connection, readOnlyWallet, { commitment: 'confirmed' });
}
const wallets = para.getWalletsByType({ type: 'Solana' });
if (!wallets || wallets.length === 0) {
const readOnlyWallet = {
publicKey: SystemProgram.programId,
signTransaction: async (tx: T): Promise => {
throw new Error('No Solana wallet available');
},
signAllTransactions: async (txs: T[]): Promise => {
throw new Error('No Solana wallet available');
},
};
return new anchor.AnchorProvider(connection, readOnlyWallet, { commitment: 'confirmed' });
}
const signer = new ParaSolanaWeb3Signer(para, connection);
const wallet = {
publicKey: signer.sender,
signTransaction: async (tx: T): Promise => {
return await signer.signTransaction(tx);
},
signAllTransactions: async (txs: T[]): Promise => {
return await Promise.all(txs.map((tx) => signer.signTransaction(tx)));
},
};
return new anchor.AnchorProvider(connection, wallet, { commitment: 'confirmed' });
}, [para, isConnected]);
return provider;
}
```
```typescript theme={null}
import { ParaSolanaWeb3Signer } from '@getpara/solana-web3.js-v1-integration';
import { Connection, clusterApiUrl } from '@solana/web3.js';
import { para } from './para';
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const signer = new ParaSolanaWeb3Signer(para, connection);
```
```typescript theme={null}
import { createParaSolanaSigner } from '@getpara/solana-signers-v2-integration';
import { createSolanaRpc } from '@solana/kit';
import { para } from './para';
const rpc = createSolanaRpc('https://api.mainnet-beta.solana.com');
const signer = createParaSolanaSigner({ para, rpc });
```
```typescript theme={null}
import { ParaSolanaWeb3Signer } from '@getpara/solana-web3.js-v1-integration';
import { Connection, clusterApiUrl, Transaction, VersionedTransaction } from '@solana/web3.js';
import * as anchor from '@project-serum/anchor';
import { para } from './para';
const connection = new Connection(clusterApiUrl('mainnet-beta'));
const signer = new ParaSolanaWeb3Signer(para, connection);
const wallet = {
publicKey: signer.sender,
signTransaction: async (tx: T): Promise => {
return await signer.signTransaction(tx);
},
signAllTransactions: async (txs: T[]): Promise => {
return await Promise.all(txs.map((tx) => signer.signTransaction(tx)));
},
};
const provider = new anchor.AnchorProvider(connection, wallet, { commitment: 'confirmed' });
anchor.setProvider(provider);
```
## Next Steps
# SWIG Account Abstraction
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/setup-swig
Build embedded wallets using SWIG AA provider with Para's infrastructure on Solana
SWIG provides account abstraction on Solana, enabling seamless embedded wallet experiences. This guide walks you through integrating SWIG with Para for embedded wallet creation.
## Prerequisites
Before starting this integration:
* Set up a Para account on their developer portal
* Generate an API Key
* Enable Solana as a supported network
* Basic understanding of React, TypeScript, and Solana
## Installation
Install the required dependencies for SWIG integration:
```bash theme={null}
yarn add @getpara/react-sdk @tanstack/react-query @getpara/solana-web3.js-v1-integration @solana/web3.js @swig-wallet/classic @swig-wallet/coder
```
Install Vite polyfills for development:
```bash theme={null}
yarn add vite-plugin-node-polyfills -D
```
## Setup
### Vite Configuration
Update your `vite.config.ts` for Solana compatibility:
```typescript theme={null}
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import path from "path";
export default defineConfig({
plugins: [react(), nodePolyfills()],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
```
### Environment Variables
Create a `.env.local` file in your project root:
```env theme={null}
VITE_PARA_API_KEY=your_para_api_key
```
### Para Provider Setup
Create `providers.tsx` in your `src` directory:
```typescript theme={null}
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
interface ProvidersProps {
children: React.ReactNode;
}
export function Providers({ children }: ProvidersProps) {
return (
{children}
);
}
```
## Authentication
Create a login button component with Para modal integration:
```tsx theme={null}
import React from "react";
import { ParaModal, useModal, OAuthMethod, useAccount, useLogout } from "@getpara/react-sdk";
export const LoginButton: React.FC = () => {
const { openModal } = useModal();
const { data: account } = useAccount();
const { logoutAsync } = useLogout();
const handleClick = () => {
if (account?.isConnected) {
logoutAsync();
} else {
openModal();
}
};
return (
<>
>
);
};
```
## SWIG Account Creation
Create a SWIG account using Para's Solana Web3.js integration:
```tsx theme={null}
import React, { useState, useEffect } from "react";
import { Connection, PublicKey, Transaction, LAMPORTS_PER_SOL } from "@solana/web3.js";
import { useAccount, useWallet, useClient } from "@getpara/react-sdk";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration";
import { Actions, Swig, findSwigPda, createEd25519AuthorityInfo } from "@swig-wallet/classic";
export const SwigAccountCreation: React.FC = () => {
const { data: account } = useAccount();
const { data: wallet } = useWallet();
const [swigAddress, setSwigAddress] = useState(null);
const [solBalance, setSolBalance] = useState(null);
const [isCreatingSwig, setIsCreatingSwig] = useState(false);
const para = useClient();
const connection = new Connection("https://api.devnet.solana.com");
const createSwigAccount = async () => {
if (!wallet?.address || !para) return;
setIsCreatingSwig(true);
try {
const id = new Uint8Array(32);
crypto.getRandomValues(id);
const paraPubkey = new PublicKey(wallet.address);
const [swigPdaAddress] = findSwigPda(id);
const signer = new ParaSolanaWeb3Signer(para, connection, wallet.id);
const rootAuthorityInfo = createEd25519AuthorityInfo(paraPubkey);
const rootActions = Actions.set().all().get();
const createSwigInstruction = Swig.create({
authorityInfo: rootAuthorityInfo,
id,
payer: paraPubkey,
actions: rootActions,
});
const transaction = new Transaction();
transaction.add(createSwigInstruction);
transaction.feePayer = paraPubkey;
const { blockhash } = await connection.getLatestBlockhash();
transaction.recentBlockhash = blockhash;
const signedTransaction = await signer.signTransaction(transaction);
const signature = await connection.sendRawTransaction(signedTransaction.serialize());
await connection.confirmTransaction({
signature,
blockhash,
lastValidBlockHeight: (await connection.getLatestBlockhash()).lastValidBlockHeight,
});
setSwigAddress(swigPdaAddress.toBase58());
setIsCreatingSwig(false);
console.log("Swig account created successfully!");
console.log("Swig address:", swigPdaAddress.toBase58());
console.log("Transaction signature:", signature);
} catch (error) {
console.error("Error in transaction signing:", error);
setIsCreatingSwig(false);
}
};
useEffect(() => {
const fetchBalance = async () => {
if (wallet?.address) {
try {
const balance = await connection.getBalance(new PublicKey(wallet.address));
setSolBalance(balance / LAMPORTS_PER_SOL);
} catch (e) {
setSolBalance(null);
}
}
};
fetchBalance();
}, [wallet?.address]);
if (!account?.isConnected || !wallet) {
return
Please connect your wallet first
;
}
return (
You are signed in!
Wallet address: {wallet.address}
{solBalance !== null && (
Balance: {solBalance} SOL
)}
{swigAddress && (
Swig Account Created!
Address: {swigAddress}
)}
{!swigAddress && (
)}
);
};
```
## Complete Application
Here's the complete App component that brings everything together:
```tsx theme={null}
import React from "react";
import { Providers } from "./providers";
import { LoginButton } from "./LoginButton";
import { SwigAccountCreation } from "./SwigAccountCreation";
export default function App() {
return (
SWIG + Para Integration
Using Para's Solana Web3.js integration for SWIG account creation
);
}
```
## Key Implementation Details
### SWIG Account Creation Process
The `createSwigAccount` function performs these critical steps:
1. **Generate Random ID**: Creates a unique 32-byte identifier for the SWIG account
2. **Create Para Signer**: Initializes `ParaSolanaWeb3Signer` with Para client and wallet
3. **Set Authority**: Establishes the Para-generated wallet as root authority
4. **Configure Actions**: Grants full permissions using `Actions.set().all().get()`
5. **Build Transaction**: Creates and configures the Solana transaction
6. **Sign & Send**: Uses Para's signer to sign and broadcast the transaction
### Important Notes
* **Devnet SOL Required**: You need devnet SOL in your Para-generated wallet before creating a SWIG account
* **Root Authority**: The Para-generated wallet becomes the root authority for the SWIG account
* **Error Handling**: The implementation includes proper error handling and loading states
* **Balance Display**: Shows wallet SOL balance and SWIG address after creation
## Testing Your Integration
1. Start your development server:
```bash theme={null}
yarn dev
```
2. Open your browser to the displayed URL
3. Click "Sign in with Para" to authenticate
4. Send devnet SOL to your wallet address
5. Click "Create Swig Account" to create your SWIG account
## Best Practices
* Never commit API keys to version control
* Use environment variables for sensitive configuration
* Test thoroughly on Solana devnet before mainnet
* Implement proper error handling throughout the flow
* Store SWIG account information securely
## Resources
## Next Steps
Now that you have SWIG working with Para, you can:
* Implement additional SWIG functionality (transfers, token management)
* Customize the Para modal appearance and OAuth providers
* Add more complex transaction flows
* Implement additional security features like multi-signature support
* Integrate with other Solana protocols and applications
# Sign Messages with Solana Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/sign-messages
Sign text and binary messages using Solana Web3.js or Anchor with Para
Sign messages to prove ownership of a Solana address without submitting a transaction. This is commonly used for authentication and verification purposes.
```typescript theme={null}
import { useParaSolana } from './hooks/useParaSolana';
import bs58 from 'bs58';
function SignMessage() {
const { signer } = useParaSolana();
const signMessage = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const message = "Hello, Solana!";
const messageBytes = new TextEncoder().encode(message);
const signature = await signer.signBytes(Buffer.from(messageBytes));
const signatureBase58 = bs58.encode(signature);
console.log("Message:", message);
console.log("Signature:", signatureBase58);
console.log("Signer:", signer.address);
};
return ;
}
```
```typescript theme={null}
import { useParaSolanaKit } from './hooks/useParaSolanaKit';
import { getUtf8Encoder } from '@solana/kit';
import bs58 from 'bs58';
function SignMessage() {
const { signer } = useParaSolanaKit();
const signMessage = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const message = "Hello, Solana!";
const messageBytes = getUtf8Encoder().encode(message);
const signatureResult = await signer.signMessages([
{ content: messageBytes, signatures: {} }
]);
const signatureBytes = signatureResult[0][signer.address];
const signatureBase58 = bs58.encode(signatureBytes);
console.log("Message:", message);
console.log("Signature:", signatureBase58);
console.log("Signer:", signer.address);
};
return ;
}
```
```typescript theme={null}
import { useParaAnchor } from './hooks/useParaAnchor';
import { useParaSolana } from './hooks/useParaSolana';
import bs58 from 'bs58';
function SignMessage() {
const provider = useParaAnchor();
const { signer } = useParaSolana();
const signMessage = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const message = "Hello, Anchor!";
const messageBytes = new TextEncoder().encode(message);
const signature = await signer.signBytes(Buffer.from(messageBytes));
const signatureBase58 = bs58.encode(signature);
console.log("Message:", message);
console.log("Signature:", signatureBase58);
console.log("Signer:", provider.wallet.publicKey.toString());
};
return ;
}
```
## Next Steps
# Verify Signatures with Solana Libraries
Source: https://docs.getpara.com/v2/react/guides/web3-operations/solana/verify-signatures
Verify message and transaction signatures using Solana Web3.js or Anchor
Verify Ed25519 signatures to confirm that a message was signed by a specific Solana address. Essential for authentication and ensuring data integrity.
## Verify Signatures
```typescript theme={null}
import { useParaSolana } from './hooks/useParaSolana';
import { PublicKey } from '@solana/web3.js';
import nacl from 'tweetnacl';
import bs58 from 'bs58';
function VerifySignature() {
const { signer } = useParaSolana();
const verifyMessage = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const message = "Hello, Solana!";
const messageBytes = new TextEncoder().encode(message);
// Sign the message first
const signature = await signer.signBytes(Buffer.from(messageBytes));
const signatureBase58 = bs58.encode(signature);
// Verify the signature
try {
const publicKeyBytes = new PublicKey(signer.address).toBytes();
const isValid = nacl.sign.detached.verify(
messageBytes,
signature,
publicKeyBytes
);
console.log("Message:", message);
console.log("Signature:", signatureBase58);
console.log("Signature valid:", isValid);
console.log("Signer:", signer.address);
return isValid;
} catch (error) {
console.error("Verification failed:", error);
return false;
}
};
return ;
}
```
```typescript theme={null}
import { useParaSolanaKit } from './hooks/useParaSolanaKit';
import { getUtf8Encoder } from '@solana/kit';
import nacl from 'tweetnacl';
import bs58 from 'bs58';
function VerifySignature() {
const { signer } = useParaSolanaKit();
const verifyMessage = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const message = "Hello, Solana!";
const messageBytes = getUtf8Encoder().encode(message);
// Sign the message first
const signatureResult = await signer.signMessages([
{ content: messageBytes, signatures: {} }
]);
const signatureBytes = signatureResult[0][signer.address];
const signatureBase58 = bs58.encode(signatureBytes);
// Verify the signature
try {
const publicKeyBytes = bs58.decode(signer.address);
const isValid = nacl.sign.detached.verify(
messageBytes,
signatureBytes,
publicKeyBytes
);
console.log("Message:", message);
console.log("Signature:", signatureBase58);
console.log("Signature valid:", isValid);
console.log("Signer:", signer.address);
return isValid;
} catch (error) {
console.error("Verification failed:", error);
return false;
}
};
return ;
}
```
```typescript theme={null}
import { useParaAnchor } from './hooks/useParaAnchor';
import { useParaSolana } from './hooks/useParaSolana';
import nacl from 'tweetnacl';
import bs58 from 'bs58';
function VerifySignature() {
const provider = useParaAnchor();
const { signer } = useParaSolana();
const verifyMessage = async () => {
if (!signer) {
console.error("No signer available. Connect wallet first.");
return;
}
const message = "Hello, Anchor!";
const messageBytes = new TextEncoder().encode(message);
// Sign the message first
const signature = await signer.signBytes(Buffer.from(messageBytes));
const signatureBase58 = bs58.encode(signature);
// Verify the signature
try {
const publicKeyBytes = provider.wallet.publicKey.toBytes();
const isValid = nacl.sign.detached.verify(
messageBytes,
signature,
publicKeyBytes
);
console.log("Message:", message);
console.log("Signature:", signatureBase58);
console.log("Signature valid:", isValid);
console.log("Signer:", provider.wallet.publicKey.toString());
return isValid;
} catch (error) {
console.error("Verification failed:", error);
return false;
}
};
return ;
}
```
## Next Steps
# Switch Active Wallet
Source: https://docs.getpara.com/v2/react/guides/web3-operations/switch-wallet
Change the currently selected wallet for blockchain operations
To switch wallets, use the `useWalletState` hook. This hook provides methods to manage the currently selected wallet and allows you to switch between different wallets.
### useWalletState
```tsx theme={null}
import { useWalletState } from '@getpara/react-sdk';
const { selectedWallet, setSelectedWallet, updateSelectedWallet } = useWalletState();
// Switch to a specific wallet by ID
setSelectedWallet({ id: 'wallet_123' });
// Refresh selection from Para client state
updateSelectedWallet();
```
## Next Steps
# React SDK Overview
Source: https://docs.getpara.com/v2/react/overview
Complete guide to integrating Para's React SDK for Web3 authentication and wallet management
Para's React SDK provides a comprehensive solution for Web3 authentication and wallet management in React applications. Navigate through our documentation to find the right integration path for your project.
## React Frameworks
Get started with Para in your preferred React framework. Each guide provides framework-specific setup instructions and best practices.
## Getting Started
Begin your Para integration journey with these essential resources designed to get you up and running quickly.
## Sign Transactions & Messages
Learn how to sign transactions and messages using Para's wallet infrastructure and integrate with popular Web3 libraries.
### Core Signing Operations
### Blockchain Libraries
### Smart Accounts
## Manage Users & Authentication
Implement secure authentication flows and manage user sessions with Para's comprehensive auth system.
### Wallet Connection Options
### Advanced Authentication
## Customize Your Integration
Tailor Para's appearance and behavior to match your brand and user experience requirements.
### Developer Portal Configuration
### UI Customization
## Advanced Features
Explore advanced capabilities and integration patterns for complex use cases.
### Resources & Support
# React SDK Quickstart
Source: https://docs.getpara.com/v2/react/quickstart
Get started with Para's React SDK in minutes with our interactive setup guide
## Interactive Setup
**Need an API Key?** Head to the to create your account and get your API key. You'll need this to complete the integration above.
## Next Steps
Once you've integrated the SDK, explore these guides to enhance your application:
Learn how to sign transactions and messages with Para wallets
Configure your app settings, branding, and security in the Developer Portal
Manage user sessions, JWTs, and authentication flows
Explore all available React hooks for Para integration
# Para with Next.js
Source: https://docs.getpara.com/v2/react/setup/nextjs
A guide to integrate Para into a Next.js application
This guide will walk you through integrating Para SDK into your **Next.js** application, providing seamless user
authentication and wallet management.
This guide uses the **Next.js App Router**. For Pages Router, refer to the for setup instructions.
## Prerequisites
Before starting, you'll need a Para API key which you can obtain from the
Para Developer Portal. You can learn to create your account and get
your API key from the Developer Portal.
## Installation
Install the Para React SDK and React Query:
```bash npm theme={null}
npm install @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```
```bash yarn theme={null}
yarn add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
```
````bash pnpm theme={null}
pnpm add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```bash bun
bun add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
````
## Configure Providers
Create a providers component and wrap your application with it. Note the `"use client"` directive at the top - this is
required for Next.js App Router:
The `import "@getpara/react-sdk/styles.css"` is **required** for the Para modal to display correctly. Without this import, the modal will not be visible.
```jsx providers.tsx theme={null}
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
export function Providers({
children,
}: Readonly<{
children: React.ReactNode,
}>) {
return (
{children}
);
}
```
If you're using a legacy API key (one without an environment prefix) you must provide a value to the
`paraClientConfig.environment`. You can retrieve your updated API key from the Para Developer Portal at
[https://developer.getpara.com/](https://developer.getpara.com/)
## Wrap Your App with Providers
Update your root layout to wrap your application with the Providers component:
```jsx app/layout.tsx theme={null}
import { Providers } from "./providers";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
You can learn more about the `ParaProvider` and its definition in the .
The `ParaProvider` utilizes libraries like Wagmi, Graz, and Solana Wallet Adapter, to power wallet connections. Meaning you can use any of the hooks provided by these libraries when within the `ParaProvider` context. You can learn more about using external wallets in the .
## Create a Connect Button
Now you can create a component that uses Para hooks to manage wallet connection:
```jsx connect-button.tsx theme={null}
"use client";
import { useModal, useAccount, useWallet } from "@getpara/react-sdk";
export function ConnectButton() {
const { openModal } = useModal();
const { data: wallet } = useWallet();
const { isConnected } = useAccount();
return (
);
}
```
Learn more about the hooks used in this example:
* - Control the Para modal programmatically
* - Access the current wallet data
* - Get account connection status and details
**Testing?** Use `BETA` testing credentials for fast development. Check out the to learn about test emails and phone numbers.
## Example
## Next Steps
Success you've set up Para with Next.js! Now you can expand your application with wallet connections, account
management, and more.
# Para with TanStack Start
Source: https://docs.getpara.com/v2/react/setup/tanstack-start
A guide to integrate Para SDK with TanStack Start while preserving server-side rendering capabilities.
This guide will walk you through integrating Para SDK with **TanStack Start** while preserving server-side rendering (SSR) capabilities.
TanStack Start is a full-stack React framework powered by TanStack Router. Para SDK uses styled-components internally which requires client-side only loading to work correctly with SSR.
## Prerequisites
Before starting, you'll need a Para API key which you can obtain from the Para Developer Portal. You can learn to create your account and get your API key from the Developer Portal.
## Installation
Install the Para React SDK and React Query:
```bash npm theme={null}
npm install @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js--save-exact
```
```bash yarn theme={null}
yarn add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
```
````bash pnpm theme={null}
pnpm add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```bash bun
bun add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
````
## Setting Up Polyfills with TanStack Start
Since TanStack Start uses Vite under the hood, we need to set up polyfills for modules like crypto, buffer, etc. that Para SDK relies on. We only want to run these polyfills on the client, not on the server.
1. Install the Vite Node Polyfills plugin:
```bash npm theme={null}
npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn theme={null}
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm theme={null}
pnpm add vite-plugin-node-polyfills -D
```
```bash bun theme={null}
bun add -d vite-plugin-node-polyfills
```
2. Configure the polyfills in your `app.config.ts`:
```ts app.config.ts theme={null}
import { defineConfig } from "@tanstack/react-start/config";
import tsConfigPaths from "vite-tsconfig-paths";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
tsr: { appDirectory: "src" },
// Base configuration (applied to both client and server)
vite: {
plugins: [tsConfigPaths({ projects: ["./tsconfig.json"] })],
define: {
// This helps modules determine the execution environment
"process.browser": true,
},
},
// Client-specific configuration
routers: {
client: {
vite: {
// Apply node polyfills only on the client side
plugins: [nodePolyfills()],
},
},
},
});
```
## Setup Postinstall Script
Add the Para setup script to your `package.json`:
```json package.json theme={null}
{
"scripts": {
"postinstall": "npx setup-para"
}
}
```
## Create a Client-Only Providers Component
Create a providers component using React.lazy and ClientOnly to ensure Para SDK only loads on the client:
```tsx src/components/Providers.tsx theme={null}
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ClientOnly } from "@tanstack/react-router";
const queryClient = new QueryClient();
// Lazy load ParaProvider to avoid SSR issues
const LazyParaProvider = React.lazy(() =>
import("@getpara/react-sdk").then((mod) => ({
default: mod.ParaProvider
}))
);
export default function Providers({ children }: React.PropsWithChildren) {
return (
{children}
);
}
```
If you're using a legacy API key (one without an environment prefix) you must provide a value to the `paraClientConfig.environment`. You can retrieve your updated API key from the Para Developer Portal at [https://developer.getpara.com/](https://developer.getpara.com/)
## Wrap Your App with Providers
Update your root component to wrap your application with the Providers component:
```tsx src/routes/__root.tsx theme={null}
import Providers from "~/components/Providers";
import { Outlet } from "@tanstack/react-router";
export function RootComponent() {
return (
);
}
```
You can learn more about the `ParaProvider` and its definition in the .
The `ParaProvider` utilizes libraries like Wagmi, Graz, and Solana Wallet Adapter, to power wallet connections. Meaning you can use any of the hooks provided by these libraries when within the `ParaProvider` context. You can learn more about using external wallets in the .
## Create a Client-Only Connect Component
Create a component that uses Para SDK hooks, ensuring it's only rendered on the client:
```tsx src/components/ParaContainer.tsx theme={null}
import { useModal, useAccount } from "@getpara/react-sdk";
export function ParaContainer() {
const { openConnectModal, openWalletModal } = useModal();
const account = useAccount();
return (
);
}
```
Learn more about the hooks used in this example:
* - Control the Para modal programmatically (openConnectModal, openWalletModal)
* - Get account connection status and wallet details
Use it in your pages with ClientOnly wrapper:
The `import "@getpara/react-sdk/styles.css"` is **required** for the Para modal to display correctly. Without this import, the modal will not be visible. You can import it in your root route or any page that uses Para components.
```tsx src/routes/index.tsx theme={null}
import React from "react";
import { createFileRoute, ClientOnly } from "@tanstack/react-router";
import "@getpara/react-sdk/styles.css";
// Lazy load Para container component
const LazyParaContainer = React.lazy(() =>
import("~/components/ParaContainer").then((mod) => ({
default: mod.ParaContainer,
}))
);
function Home() {
return (
Para Modal Example
Loading Para components...}>
);
}
export const Route = createFileRoute("/")({
component: Home,
});
```
The Para SDK uses styled-components internally which can cause issues during server-side rendering. By using `React.lazy` and `ClientOnly`, we ensure Para components are only evaluated in the browser environment where styled-components works correctly.
## Example
## Next Steps
Success you've set up Para with TanStack Start! Now you can expand your application with wallet connections, account management, and more.
# Para with React + Vite
Source: https://docs.getpara.com/v2/react/setup/vite
A guide to quickly integrate the Para Modal into your Vite-powered React application.
This guide will walk you through integrating Para SDK into your **Vite**-powered React application, providing seamless user authentication and wallet management.
## Prerequisites
Before starting, you'll need a Para API key which you can obtain from the Para Developer Portal. You can learn to create your account and get your API key from the Developer Portal.
## Installation
Install the Para React SDK, React Query, and required polyfills for Vite:
```bash npm theme={null}
npm install @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact && npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn theme={null}
yarn add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact && yarn add vite-plugin-node-polyfills -D
```
```bash pnpm theme={null}
pnpm add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact && pnpm add vite-plugin-node-polyfills -D
```
```bash bun theme={null}
bun add @getpara/react-sdk @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @farcaster/mini-app-solana @farcaster/miniapp-sdk @farcaster/miniapp-wagmi-connector @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact && bun add -d vite-plugin-node-polyfills
```
Then add the polyfill plugin to your `vite.config.js`:
```js vite.config.js theme={null}
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [react(), nodePolyfills()],
});
```
## Configure Providers
Create a providers component and wrap your application with it:
The `import "@getpara/react-sdk/styles.css"` is **required** for the Para modal to display correctly. Without this import, the modal will not be visible.
```jsx providers.jsx theme={null}
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ParaProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
export function Providers({ children }) {
return (
{children}
);
}
```
If you're using a legacy API key (one without an environment prefix) you must provide a value to the `paraClientConfig.environment`. You can retrieve your updated API key from the Para Developer Portal at [https://developer.getpara.com/](https://developer.getpara.com/)
## Wrap Your App with Providers
Update your main app file:
```jsx src/App.jsx theme={null}
import { Providers } from './providers';
function App() {
return (
{/* Your app content */}
);
}
export default App;
```
You can learn more about the `ParaProvider` and its definition in the .
The `ParaProvider` utilizes libraries like Wagmi, Graz, and Solana Wallet Adapter, to power wallet connections. Meaning you can use any of the hooks provided by these libraries when within the `ParaProvider` context. You can learn more about using external wallets in the .
## Create a Connect Button
Now create a component that uses Para hooks to manage wallet connection:
```jsx ConnectButton.jsx theme={null}
import { useModal, useAccount, useWallet } from "@getpara/react-sdk";
export function ConnectButton() {
const { openModal } = useModal();
const { data: wallet } = useWallet();
const { isConnected } = useAccount();
return (
);
}
```
Learn more about the hooks used in this example:
* - Control the Para modal programmatically
* - Access the current wallet data
* - Get account connection status and details
**Testing?** Use `BETA` testing credentials for fast development. Check out the to learn about test emails and phone numbers.
## Example
## Next Steps
Success you've set up Para with Vite! Now you can expand your application with wallet connections, account management, and more.
# Testing Your Integration
Source: https://docs.getpara.com/v2/react/testing-guide
Use test credentials to develop and test your Para integration without creating real users
## Overview
Para provides test credentials for the `BETA` environment to help you develop and test your integration without creating real user accounts. This guide explains how to use test accounts and manage your testing workflow effectively.
## Test Credentials
### Email Testing
For email-based authentication testing in the `BETA` environment:
* Use any email ending in `@test.getpara.com`
* Examples: `dev@test.getpara.com`, `test1@test.getpara.com`, `user123@test.getpara.com`
* **Any OTP code will work** for verification (e.g., `123456`, `000000`, `111111`)
* Perfect for testing email-based authentication flows
### Phone Number Testing
For SMS-based authentication testing in the BETA environment:
* Use US phone numbers (+1) in format: `(area code)-555-xxxx`
* Examples: `(425)-555-1234`, `(206)-555-9876`, `(310)-555-0001`
* **Any OTP code will work** for verification
* Ideal for testing phone-based authentication flows
## Important Testing Notes
These test credentials **only work in the BETA Environment**. They will not work in production. Make sure your Para SDK is configured for the BETA environment when using these credentials.
### User Limits
* Beta accounts are limited to **50 users**
* If you reach the 50 user limit, you will need to delete users to continue testing
* Regular cleanup helps you stay within limits during development
### Managing Test Users
Navigate to your [Para Developer Portal](https://developer.getpara.com) and log in with your developer credentials
Click on "Users" in the left sidebar. You'll be taken to a list of users for your API key where you can see the identifier and login method for each user.
Click on any user to open a drawer with user details. Inside the drawer, click "Delete User" to remove that specific user.
If you need to clear all test users at once, use the "Delete All Users" button available on the users page.
**Important:** Deleting users is only possible while in the BETA environment. In production, wallets are permanent and cannot be deleted.
## Troubleshooting
* Verify you're using the BETA environment
* Check that the email ends exactly with `@test.getpara.com`
* Ensure phone numbers follow the `(xxx)-555-xxxx` format
* Access the Developer Portal to delete unused test users
* Consider using a naming convention to identify old test accounts
* Set up automated cleanup in your test suite
* Confirm you're in BETA environment (not production)
* Any numeric OTP should work (e.g., "123456")
* Check for typos in the test credentials
## Next Steps
Get started with Para integration
Configure your developer portal
Manage user sessions effectively
# Para with Svelte + Vite
Source: https://docs.getpara.com/v2/svelte/setup/vite
A user-friendly guide to integrate the Para Modal (React-based) into your Svelte application powered by Vite.
If you haven't already you can create a new Svelte project with Vite by following the .
Although mixing React and Svelte is unusual, we can do so via . If you prefer to build your own custom UI, you can also use `@getpara/web-sdk` directly. Reach out to use for help with custom UI integration.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for
integration.
Don't have an API key yet? Request access to the to create API keys, manage billing, teams, and more.
## Installing Dependencies & Peer Dependencies
First, install the Para React SDK and needed peer dependencies, plus React dependencies using your preferred package manager:
```bash npm theme={null}
npm install @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```
```bash yarn theme={null}
yarn add @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
```
```bash pnpm theme={null}
pnpm add @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```
```bash bun theme={null}
bun add @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
```
## Setting Up the Svelte Preprocessor and Vite Polyfills
### Svelte Preprocessor for React
You must configure your **Svelte** app to accept React components. For that, install and configure
`svelte-preprocess-react`:
```bash npm theme={null}
npm install svelte-preprocess-react
```
```bash yarn theme={null}
yarn add svelte-preprocess-react
```
```bash pnpm theme={null}
pnpm add svelte-preprocess-react
```
```bash bun theme={null}
bun add svelte-preprocess-react
```
Then add it to your `svelte.config.js`:
```js svelte.config.js theme={null}
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
import preprocessReact from "svelte-preprocess-react/preprocessReact";
export default {
preprocess: [vitePreprocess(), preprocessReact()],
};
```
### Vite Polyfills
Like other React-based setups, you may need Node.js polyfills for features like `crypto`, `buffer`, and `stream`.
Install `vite-plugin-node-polyfills`:
```bash npm theme={null}
npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn theme={null}
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm theme={null}
pnpm add vite-plugin-node-polyfills -D
```
```bash bun theme={null}
bun add vite-plugin-node-polyfills
```
Then configure it in `vite.config.js` or `vite.config.ts`:
```ts vite.config.ts theme={null}
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [svelte(), nodePolyfills()],
});
```
## Creating a QueryClient Instance
```ts client/queryClient.ts theme={null}
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
export const queryClient = new QueryClient();
```
## Setting Up the Para SDK
Now that you've installed the necessary dependencies, let's set up the Para SDK in your Svelte application. This
involves creating a client instance and integrating the Para Modal.
### Creating a Para Client Instance
Much like in React or Vue, you'll need a Para client instance. Keep it in a dedicated file (e.g., `client/para.ts`):
```ts client/para.ts theme={null}
import { ParaWeb } from "@getpara/react-sdk";
const PARA_API_KEY = import.meta.env.VITE_PARA_API_KEY;
export const para = new ParaWeb(PARA_API_KEY);
```
If you're using a legacy API key (one without an environment prefix) you must provide the `Environment` as the first argument to the `ParaWeb` constructor. You can retrieve your updated API key from the Para Developer Portal at [https://developer.getpara.com/](https://developer.getpara.com/)
### Integrating the Para Modal in Svelte
With `svelte-preprocess-react`, you can **directly** use React components in Svelte:
**Beta Testing Credentials** In the `BETA` Environment, you can use any email ending in `@test.getpara.com` (like
[dev@test.getpara.com](mailto:dev@test.getpara.com)) or US phone numbers (+1) in the format `(area code)-555-xxxx` (like (425)-555-1234). Any OTP
code will work for verification with these test credentials. These credentials are for beta testing only. You can
delete test users anytime in the beta developer console to free up user slots.
```svelte theme={null}
```
When you click **Open Para Modal**, the React-based Para Modal will appear inside your Svelte application.
### Customizing the Para Modal
Just like in React, you can provide any additional props to the `` component. For example, customizing modal
theming:
```svelte theme={null}
```
For a full list of available `ParaModalProps`, refer to the customization guide:
## Examples
For an example of using Para SDK in a Svelte application, check out our Examples Hub repository:
## Troubleshooting
If you encounter issues during the integration or usage of the Para Modal in a Svelte-based app, here are some common
problems and their solutions:
# Para with Vue + Vite
Source: https://docs.getpara.com/v2/vue/setup/vite
A guide to integrate the Para Modal (React-based) into your Vue application powered by Vite.
While mixing React with Vue isn't always considered best practice, it is entirely possible by bridging to the React
modal via a connector. If you prefer to build your own custom UI, you can also use `@getpara/web-sdk` directly. Reach out to use for help with custom UI integration.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for
integration.
Don't have an API key yet? Request access to the to create API keys, manage billing, teams, and more.
## Installing Dependencies & Peer Dependencies
First, install the Para React SDK and needed peer dependencies, plus React dependencies using your preferred package manager:
```bash npm theme={null}
npm install @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```
```bash yarn theme={null}
yarn add @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
```
```bash pnpm theme={null}
pnpm add @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --save-exact
```
```bash bun theme={null}
bun add @getpara/react-sdk react react-dom @tanstack/react-query graz @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet wagmi@^2 viem @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js --exact
```
## Setting Up Polyfills
Like any React + Vite project that may rely on Node modules (`crypto`, `buffer`, `stream`), you'll likely need
polyfills:
```bash npm theme={null}
npm install vite-plugin-node-polyfills
```
```bash yarn theme={null}
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm theme={null}
pnpm add vite-plugin-node-polyfills -D
```
```bash bun theme={null}
bun add vite-plugin-node-polyfills
```
Then, update your `vite.config.js` or `vite.config.ts`:
```js vite.config.js theme={null}
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import react from "@vitejs/plugin-react"; // Needed for the React-based modal
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [
vue(),
react(),
nodePolyfills({
protocolImports: true,
}),
],
});
```
## Setting Up the Para SDK
Now that you've installed the necessary dependencies, let's set up the Para SDK in your Vue project. This involves
creating a client instance and optionally configuring Next.js to transpile external modules if needed.
### Creating a Para Client Instance
Just like in React apps, you need a Para client instance. You can keep it in a dedicated file (e.g., `client/para.ts`):
```ts client/para.ts theme={null}
import { ParaWeb } from "@getpara/react-sdk";
const PARA_API_KEY = import.meta.env.VITE_PARA_API_KEY;
export const para = new ParaWeb(PARA_API_KEY);
```
If you're using a legacy API key (one without an environment prefix) you must provide the `Environment` as the first argument to the `ParaWeb` constructor. You can retrieve your updated API key from the Para Developer Portal at [https://developer.getpara.com/](https://developer.getpara.com/)
### Building a Connector for the React Modal
To display the React-based Para Modal from within Vue, we'll create a component that mounts the React modal into a DOM
element. You can store this in a file such as `para-react-component.jsx`:
```jsx para-react-component.jsx theme={null}
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AuthLayout, ParaProvider } from "@getpara/react-sdk";
import { para } from "./client/para";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
export function ParaReactComponent({ onClose, isOpen }) {
return (
{
onClose?.();
},
logo: "/para.svg",
disableEmailLogin: false,
disablePhoneLogin: false,
authLayout: [AuthLayout.AUTH_FULL],
oAuthMethods: [
"APPLE",
"DISCORD",
"FACEBOOK",
"FARCASTER",
"GOOGLE",
"TWITTER",
],
onRampTestMode: true,
recoverySecretStepEnabled: true,
twoFactorAuthEnabled: false,
theme: {
foregroundColor: "#2D3648",
backgroundColor: "#FFFFFF",
accentColor: "#0066CC",
darkForegroundColor: "#E8EBF2",
darkBackgroundColor: "#1A1F2B",
darkAccentColor: "#4D9FFF",
mode: "light",
borderRadius: "none",
font: "Inter",
},
}}
externalWalletConfig={{
wallets: [],
}}
/>
);
}
```
This connector creates a React root within a given DOM element and renders the Para Modal into it. It also provides a
few methods to open, close, and check the modal's state.
### Integrating in a Vue Component
Use Vue's lifecycle hooks to create and destroy the modal connector. Below is a simplified example (`index.vue`):
```vue index.vue theme={null}
Para Modal Starter (Vue + Vite)
```
When you click the **Open Para Modal** button, the React-based Para Modal will appear within your Vue application.
**Beta Testing Credentials** In the `BETA` Environment, you can use any email ending in `@test.getpara.com` (like
[dev@test.getpara.com](mailto:dev@test.getpara.com)) or US phone numbers (+1) in the format `(area code)-555-xxxx` (like (425)-555-1234). Any OTP
code will work for verification with these test credentials. These credentials are for beta testing only. You can
delete test users anytime in the beta developer console to free up user slots.
### Customizing the Para Modal
All the usual customization props apply, just as in a React app:
```tsx theme={null}
```
Add them to your `ParaReactComponent` as needed.
## Examples
For an example of a fully working Vue + Vite integration, check out our starter templates or example repositories:
## Troubleshooting
If you encounter issues during the integration or usage of the Para Modal in a Vue-based app, here are some common
problems and their solutions:
# Flutter SDK API
Source: https://docs.getpara.com/v2/flutter/api/sdk
Complete API reference for Para Flutter SDK v2, including authentication, wallet management, and blockchain operations. Updated for 2.0.0 with comprehensive multi-chain support and passkey authentication.
## Para Class
The `Para` class is the main entry point for the Para Flutter SDK, providing wallet operations, authentication, and blockchain interactions. The latest version includes passkey authentication, comprehensive multi-chain support, and deep linking capabilities.
### Constructor
Creates a new Para SDK instance using the v2 factory constructor.
Configuration object containing environment and API key.
Your app's deep link scheme (e.g., "yourapp").
```dart theme={null}
final para = Para.fromConfig(
config: ParaConfig(
environment: Environment.beta,
apiKey: 'YOUR_API_KEY',
jsBridgeUri: Uri.parse('custom-bridge-url'), // Optional
relyingPartyId: 'custom.domain.com', // Optional
),
appScheme: 'yourapp',
);
```
### Authentication Methods
Initiates authentication flow for email or phone. Returns an `AuthState` indicating next steps.
Authentication object using `Auth.email()` or `Auth.phone()` helper methods.
```dart theme={null}
// Email authentication
final authState = await para.initiateAuthFlow(
auth: Auth.email('user@example.com')
);
// Phone authentication
final authState = await para.initiateAuthFlow(
auth: Auth.phone('+1234567890')
);
```
Verifies the OTP code sent to email/phone during authentication.
The 6-digit OTP verification code.
```dart theme={null}
final verifiedState = await para.verifyOtp(
otp: '123456'
);
```
Handles login for existing users with passkey authentication.
The auth state returned from `initiateAuthFlow()` for existing users.
```dart theme={null}
final wallet = await para.handleLogin(
authState: authState,
);
```
Handles the complete signup flow for new users, including passkey creation and wallet setup.
The auth state returned from `verifyOtp()` for new users.
The signup method to use: `SignupMethod.passkey`.
```dart theme={null}
final wallet = await para.handleSignup(
authState: authState,
method: SignupMethod.passkey,
);
```
Handles complete OAuth authentication flow using an external browser.
OAuth provider: `OAuthMethod.google`, `.twitter`, `.apple`, `.discord`, or `.facebook`.
Your app's deep link scheme for OAuth callback (e.g., 'yourapp').
```dart theme={null}
final authState = await para.verifyOAuth(
provider: OAuthMethod.google,
appScheme: 'yourapp',
);
```
### Wallet Management
Creates a new wallet with enhanced multi-chain support. Returns a `ParaFuture` that can be cancelled.
The type of wallet to create: `WalletType.evm`, `.solana`, or `.cosmos` (default: `WalletType.evm`).
Whether to skip the distributed backup process.
```dart theme={null}
final walletFuture = para.createWallet(
type: WalletType.evm,
skipDistribute: false,
);
final wallet = await walletFuture.future;
// Or cancel: await para.cancelOperationById(walletFuture.requestId);
```
Retrieves all wallets for the current user.
```dart theme={null}
final walletsFuture = para.fetchWallets();
final wallets = await walletsFuture.future;
```
### Signing Operations
Signs a message with the specified wallet. Returns a cancellable `ParaFuture`.
The wallet ID to use for signing.
The message to sign, base64-encoded.
Optional timeout in milliseconds (default: 30000).
For Cosmos signing: The SignDoc as base64-encoded JSON. When provided, this method signs a Cosmos transaction instead of a generic message.
```dart theme={null}
// Generic message signing
final signatureFuture = para.signMessage(
walletId: wallet.id,
messageBase64: base64Encode(utf8.encode('Hello, Para!')),
);
final signature = await signatureFuture.future;
// Cosmos transaction signing
final cosmosSignature = await para.signMessage(
walletId: cosmosWallet.id,
messageBase64: '', // Not used for Cosmos
cosmosSignDocBase64: base64Encode(utf8.encode(jsonEncode(signDoc))),
).future;
```
Signs an EVM transaction. Returns a cancellable `ParaFuture`.
This method is for EVM transactions only. For Solana, use `signMessage()` with the serialized transaction as `messageBase64`. For Cosmos, use `signMessage()` with `cosmosSignDocBase64`.
**EVM Transaction Return Value**: The `SuccessfulSignatureResult` contains the complete RLP-encoded transaction ready for broadcasting via the `signedTransaction` property.
**Solana/Cosmos Return Value**: For pre-serialized transactions, returns just the signature in `signedTransaction`. For constructed transactions, returns the complete signed transaction.
The wallet ID to use for signing.
RLP-encoded EVM transaction (base64).
Chain ID of the EVM network.
Optional timeout in milliseconds (default: 30000).
```dart theme={null}
// EVM transaction signing
final signatureFuture = para.signTransaction(
walletId: evmWallet.id,
rlpEncodedTxBase64: base64Encode(rlpEncodedTx),
chainId: '1', // Ethereum mainnet
);
final signature = await signatureFuture.future;
```
Cancels an ongoing operation by its request ID.
The request ID from a `ParaFuture`.
```dart theme={null}
final signatureFuture = para.signMessage(...);
// Cancel the operation
await para.cancelOperationById(signatureFuture.requestId);
```
### Session Management
Checks if there's an active user session.
```dart theme={null}
final isActive = await para.isSessionActive();
```
Exports the current session as an encrypted string.
```dart theme={null}
final sessionData = await para.exportSession();
// Save sessionData securely
```
Logs out the current user and clears session data.
```dart theme={null}
await para.logout();
```
### Utility Methods
Gets the current user's basic profile and session status.
```dart theme={null}
final user = await para.currentUser();
if (user.isLoggedIn) {
debugPrint('Logged in as ${user.userId}');
}
```
Returns a browser URL for One-Click (BASIC\_LOGIN) authentication.
Login method identifier (e.g., `BASIC_LOGIN`).
Request a shortened URL.
```dart theme={null}
final loginUrl = await para.getLoginUrl(authMethod: 'BASIC_LOGIN');
```
Presents an auth URL in a secure system web view and returns the callback URI (if any).
The authentication URL to open.
Platform-specific web authentication session.
Descriptive label for logging (default: `authentication`).
Whether to preload signing keyshares after completion.
```dart theme={null}
final callback = await para.presentAuthUrl(
url: loginUrl,
webAuthenticationSession: webAuthSession,
context: 'One-Click login',
loadTransmissionKeyshares: true,
);
```
Waits for an ongoing login operation to complete.
Optional function to check if operation is canceled.
Polling interval in milliseconds (default: 2000).
```dart theme={null}
final result = await para.waitForLogin(pollingIntervalMs: 1000);
```
Polls for completion of a pending One-Click signup.
Optional timeout in milliseconds (default: no timeout).
```dart theme={null}
final completed = await para.waitForSignup(timeoutMs: 300000);
```
Waits for wallet creation to complete after signup.
Optional function to check if operation is canceled.
Polling interval in milliseconds (default: 2000).
```dart theme={null}
final result = await para.waitForWalletCreation(pollingIntervalMs: 1000);
```
Refreshes the current session metadata and returns the latest snapshot (or `null` if nothing changed).
```dart theme={null}
final snapshot = await para.touchSession();
if (snapshot != null) {
debugPrint('Session touched at ${snapshot['updatedAt']}');
}
```
Formats a phone number for authentication.
The phone number to format.
The country code (e.g., '1' for US, '44' for UK).
```dart theme={null}
final formatted = para.formatPhoneNumber('1234567890', '1');
// Returns: '+11234567890' (exact format depends on implementation)
```
Checks if the current user is using an external wallet.
```dart theme={null}
final isExternal = await para.isUsingExternalWallet();
```
Clears local storage data.
Whether to keep the Paillier secret key (default: false).
```dart theme={null}
await para.clearStorage(false);
```
Disposes of SDK resources. Call when the SDK is no longer needed.
```dart theme={null}
para.dispose();
```
## Extension Methods
Para provides extension methods for cleaner authentication flows (requires importing extensions):
```dart theme={null}
import 'package:para/src/auth_extensions.dart';
```
### ParaAuthExtensions
Initiates authentication and returns the current state using `Auth` helper class.
Authentication method: `Auth.email()` or `Auth.phone()`.
```dart theme={null}
// Must import extensions
import 'package:para/src/auth_extensions.dart';
final authState = await para.initiateAuthFlow(
auth: Auth.email('user@example.com')
);
```
Presents a password authentication URL in a secure web view.
The password authentication URL.
Web authentication session for handling the flow.
## Types and Enums
### Environment
```dart theme={null}
enum Environment {
dev, // Development environment
beta, // Beta environment
prod // Production environment
}
```
### Auth
```dart theme={null}
class Auth {
static AuthEmail email(String email);
static AuthPhone phone(String phoneNumber);
}
```
### AuthState
```dart theme={null}
class AuthState {
final AuthStage stage; // Current authentication stage
final String? userId; // User's unique identifier
final AuthIdentity auth; // Authentication identity details
final String? displayName; // User's display name
final String? pfpUrl; // Profile picture URL
final String? username; // Username
final Map? externalWallet; // External wallet info
final String? loginUrl; // One-Click URL, when provided
final List? loginAuthMethods; // Advertised login methods (e.g., BASIC_LOGIN)
final List? signupAuthMethods; // Advertised signup methods
final String? passkeyUrl; // URL for passkey authentication
final String? passkeyId; // Passkey identifier
final String? passwordUrl; // URL for password authentication
final AuthStage? nextStage; // Optional next stage hint
// Helper getters exposed by the SDK:
bool get hasSloUrl; // loginUrl is non-empty
List get loginMethods; // loginAuthMethods ?? []
List get signupMethods; // signupAuthMethods ?? []
AuthStage get effectiveNextStage; // nextStage ?? stage
}
class AuthIdentity {
final String? email;
final String? phoneNumber;
final String? fid; // Farcaster ID
final String? telegramUserId;
// Other identity types
}
enum AuthStage {
verify, // Need to verify email/phone
login, // Existing user, proceed to login
signup // New user, proceed to signup
}
```
### SignupMethod
```dart theme={null}
enum SignupMethod {
passkey, // Hardware-backed passkey
password // Password-based authentication
}
```
### OAuthMethod
```dart theme={null}
enum OAuthMethod {
google,
twitter,
apple,
discord,
facebook
}
```
### WalletType
```dart theme={null}
enum WalletType {
evm, // Ethereum and EVM-compatible chains
solana, // Solana blockchain
cosmos // Cosmos-based chains
}
```
### ParaFuture
```dart theme={null}
class ParaFuture {
final Future future; // The actual future
final String requestId; // ID for cancellation
}
```
### Wallet
```dart theme={null}
class Wallet {
final String id;
final String address;
final WalletType type;
final WalletScheme scheme;
final String? userId; // Associated user ID
final DateTime? createdAt; // Creation timestamp
final String? publicKey; // Wallet public key
final bool? isPregen; // Whether this is a pregenerated wallet
// Additional optional fields available
}
```
### SignatureResult
```dart theme={null}
// Abstract base class
abstract class SignatureResult {}
// Successful signature
class SuccessfulSignatureResult extends SignatureResult {
final String signedTransaction; // For transactions: complete signed transaction ready for broadcasting
// For messages: just the signature
SuccessfulSignatureResult(this.signedTransaction);
/// Gets the transaction data ready for broadcasting.
String get transactionData => signedTransaction;
}
// Denied signature
class DeniedSignatureResult extends SignatureResult {
final String? pendingTransactionId;
DeniedSignatureResult(this.pendingTransactionId);
}
// Denied with URL
class DeniedSignatureResultWithUrl extends SignatureResult {
final String? pendingTransactionId;
final String url;
DeniedSignatureResultWithUrl({this.pendingTransactionId, required this.url});
}
```
# Mobile Examples
Source: https://docs.getpara.com/v2/flutter/examples
Explore Para's mobile integration examples for Flutter
Para provides a minimal, focused Flutter example demonstrating clean integration patterns with our v2 SDK. Our example app serves as a reference implementation that you can adapt to your specific needs.
## Para Flutter Example
Explore our complete Flutter application showcasing Para v2 integration:
### Highlights
* One-Click login, passkeys, and password flows in `lib/screens/auth_screen.dart`
* Social/OAuth providers and external wallets in `lib/features/auth`
* Multi-chain wallet management plus signing demos in `lib/features/wallets`
* End-to-end tests that exercise the full auth flow in `test_e2e/`
### Run It
Follow the project README for setup and commands:
* `README.md` → `mobile/with-flutter`: environment variables, build steps, troubleshooting
* `lib/client/para.dart`: replace the placeholder API key before running `flutter run`
Need another integration? File an issue on the repo or reach out to the Para team.
# Cosmos Integration
Source: https://docs.getpara.com/v2/flutter/guides/cosmos
Sign transactions for Cosmos chains using Para's unified wallet architecture
## Quick Start
```dart theme={null}
import 'package:para/para.dart';
// Sign a Cosmos transaction
final para = Para(apiKey: 'your-api-key');
final wallet = (await para.fetchWallets()).firstWhere((w) => w.type == 'COSMOS');
final transaction = CosmosTransaction(
to: 'cosmos1recipient...',
amount: '1000000', // 1 ATOM in micro-units
chainId: 'theta-testnet-001', // Cosmos Hub testnet (use 'cosmoshub-4' for mainnet)
format: 'proto',
);
final result = await para.signTransaction(
walletId: wallet.id!,
transaction: transaction.toJson(),
chainId: 'theta-testnet-001',
);
print('Transaction signed: ${result.signedTransaction}');
```
## Common Operations
### Sign Transactions for Different Chains
```dart theme={null}
// Sign ATOM transaction on Cosmos Hub testnet
final atomTx = CosmosTransaction(
to: 'cosmos1recipient...',
amount: '1000000', // 1 ATOM
denom: 'uatom',
chainId: 'theta-testnet-001', // Testnet
format: 'proto',
);
// Sign OSMO transaction on Osmosis testnet
final osmoTx = CosmosTransaction(
to: 'osmo1recipient...',
amount: '1000000', // 1 OSMO
denom: 'uosmo',
chainId: 'osmo-test-5', // Testnet
format: 'proto',
);
// Sign JUNO transaction on Juno mainnet
final junoTx = CosmosTransaction(
to: 'juno1recipient...',
amount: '1000000', // 1 JUNO
denom: 'ujuno',
chainId: 'juno-1',
format: 'proto',
);
```
### Sign Transaction
```dart theme={null}
final transaction = CosmosTransaction(
to: 'cosmos1recipient...',
amount: '1000000', // 1 ATOM
denom: 'uatom',
memo: 'Transfer via Para',
chainId: 'theta-testnet-001', // Testnet
format: 'proto', // or 'amino' for legacy
);
final result = await para.signTransaction(
walletId: wallet.id!,
transaction: transaction.toJson(),
chainId: 'theta-testnet-001',
rpcUrl: 'https://rpc.sentry-01.theta-testnet.polypore.xyz',
);
// Cosmos returns: { signBytes, signDoc, format }
print('Signed: ${result.signedTransaction}');
```
### Check Balance
```dart theme={null}
final balance = await para.getBalance(
walletId: wallet.id!,
rpcUrl: 'https://cosmos-rpc.publicnode.com',
chainPrefix: 'cosmos',
denom: 'uatom',
);
print('Balance: $balance uatom');
// Different chain
final osmoBalance = await para.getBalance(
walletId: wallet.id!,
rpcUrl: 'https://osmosis-rpc.publicnode.com',
chainPrefix: 'osmo',
denom: 'uosmo',
);
```
### Sign Message
```dart theme={null}
final message = 'Hello, Cosmos!';
final result = await para.signMessage(
walletId: wallet.id!,
message: message,
);
print('Signature: ${result.signedTransaction}');
```
## Supported Networks
### Testnets
| Network | Chain ID | Prefix | Native Token | RPC URL |
| ---------------------- | ------------------- | -------- | ------------ | -------------------------------------------------- |
| **Cosmos Hub Testnet** | `theta-testnet-001` | `cosmos` | `uatom` | `https://rpc.sentry-01.theta-testnet.polypore.xyz` |
| **Osmosis Testnet** | `osmo-test-5` | `osmo` | `uosmo` | `https://rpc.osmotest5.osmosis.zone` |
### Mainnets
| Network | Chain ID | Prefix | Native Token | Decimals | RPC URL |
| -------------- | ---------------- | ---------- | ------------ | -------- | -------------------------------------- |
| **Cosmos Hub** | `cosmoshub-4` | `cosmos` | `uatom` | 6 | `https://cosmos-rpc.publicnode.com` |
| **Osmosis** | `osmosis-1` | `osmo` | `uosmo` | 6 | `https://osmosis-rpc.publicnode.com` |
| **Juno** | `juno-1` | `juno` | `ujuno` | 6 | `https://rpc-juno.itastakers.com` |
| **Stargaze** | `stargaze-1` | `stars` | `ustars` | 6 | `https://rpc.stargaze-apis.com` |
| **Akash** | `akashnet-2` | `akash` | `uakt` | 6 | `https://rpc.akash.forbole.com` |
| **Celestia** | `celestia` | `celestia` | `utia` | 6 | `https://rpc.celestia.pops.one` |
| **dYdX** | `dydx-mainnet-1` | `dydx` | `adydx` | 18 | `https://dydx-dao-api.polkachu.com` |
| **Injective** | `injective-1` | `inj` | `inj` | 18 | `https://injective-rpc.publicnode.com` |
## Complete Example
```dart theme={null}
import 'package:flutter/material.dart';
import 'package:para/para.dart';
class CosmosWalletView extends StatefulWidget {
final Para para;
final Wallet wallet;
const CosmosWalletView({required this.para, required this.wallet});
@override
State createState() => _CosmosWalletViewState();
}
class _CosmosWalletViewState extends State {
String _selectedChain = 'theta-testnet-001';
bool _isLoading = false;
String? _result;
final chains = {
'theta-testnet-001': {'name': 'Cosmos Hub Testnet', 'rpc': 'https://rpc.sentry-01.theta-testnet.polypore.xyz', 'denom': 'uatom', 'prefix': 'cosmos'},
'osmo-test-5': {'name': 'Osmosis Testnet', 'rpc': 'https://rpc.osmotest5.osmosis.zone', 'denom': 'uosmo', 'prefix': 'osmo'},
};
@override
Widget build(BuildContext context) {
final chain = chains[_selectedChain]!;
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
DropdownButton(
value: _selectedChain,
items: chains.entries.map((e) =>
DropdownMenuItem(value: e.key, child: Text(e.value['name']!))
).toList(),
onChanged: (value) => setState(() => _selectedChain = value!),
),
SizedBox(height: 20),
Text(
widget.wallet.address ?? 'No address',
style: TextStyle(fontFamily: 'monospace', fontSize: 12),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _isLoading ? null : _signTransaction,
child: Text(_isLoading ? 'Signing...' : 'Sign Transaction for ${chain['name']}'),
),
if (_result != null)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text(_result!, style: TextStyle(fontSize: 12)),
),
],
),
);
}
Future _signTransaction() async {
setState(() => _isLoading = true);
final chain = chains[_selectedChain]!;
try {
final transaction = CosmosTransaction(
to: '${chain['prefix']}1recipient...', // Uses chain prefix
amount: '1000000', // 1 token in micro-units
denom: chain['denom']!,
memo: 'Test transaction from Flutter',
chainId: _selectedChain,
format: 'proto',
);
final result = await widget.para.signTransaction(
walletId: widget.wallet.id!,
transaction: transaction.toJson(),
chainId: _selectedChain,
rpcUrl: chain['rpc']!,
);
setState(() => _result = 'Signed! Signature: ${result.signature}');
} catch (e) {
setState(() => _result = 'Error: $e');
} finally {
setState(() => _isLoading = false);
}
}
}
```
## Proto vs Amino Formats
```dart theme={null}
// Modern Proto format (recommended)
final protoTx = CosmosTransaction(
to: 'cosmos1recipient...',
amount: '1000000',
format: 'proto',
chainId: 'cosmoshub-4',
);
// Legacy Amino format (compatibility)
final aminoTx = CosmosTransaction(
to: 'cosmos1recipient...',
amount: '1000000',
format: 'amino',
chainId: 'cosmoshub-4',
);
// Use convenience constructor
final simpleTx = CosmosTransaction(
to: 'cosmos1recipient...',
amount: '1000000',
denom: 'uatom',
chainId: 'theta-testnet-001',
);
```
# EVM Integration
Source: https://docs.getpara.com/v2/flutter/guides/evm
Transfer ETH and interact with EVM chains using Para's unified wallet architecture
## Quick Start
### Transfer
Para handles signing and broadcasting in one call:
```dart theme={null}
import 'package:para/para.dart';
final para = Para(apiKey: 'your-api-key');
final wallet = (await para.fetchWallets()).firstWhere((w) => w.type == 'EVM');
// Send ETH - Para signs and broadcasts
final result = await para.transfer(
walletId: wallet.id!,
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
amount: '1000000000000000', // 0.001 ETH in wei
chainId: '11155111', // Optional: Sepolia testnet (defaults to wallet's chain)
rpcUrl: null, // Optional: override default RPC
);
print('Transaction sent: ${result.hash}');
print('From: ${result.from}, To: ${result.to}');
print('Amount: ${result.amount}, Chain: ${result.chainId}');
```
### Advanced Control
Sign with Para, then broadcast yourself for custom gas/RPC settings:
```dart theme={null}
// Step 1: Sign transaction with Para
final transaction = EVMTransaction(
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '1000000000000000',
gasLimit: '21000',
);
final result = await para.signTransaction(
walletId: wallet.id!,
transaction: transaction.toJson(),
chainId: '11155111', // Sepolia testnet
);
// Step 2: Broadcast using your preferred library (e.g., web3dart)
// The transactionData getter provides the complete signed transaction
// final txHash = await broadcastWithWeb3Dart(result.transactionData);
```
## Common Operations
### Send ETH
```dart theme={null}
// Para handles everything - signing and broadcasting
final result = await para.transfer(
walletId: wallet.id!,
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
amount: '1000000000000000', // 0.001 ETH in wei
chainId: '11155111', // Optional: Sepolia testnet
rpcUrl: null, // Optional: custom RPC URL
);
print('Transaction hash: ${result.hash}');
print('From: ${result.from}, To: ${result.to}, Chain: ${result.chainId}');
```
### Sign Transaction
```dart theme={null}
final transaction = EVMTransaction(
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
value: '1000000000000000',
gasLimit: '21000',
maxPriorityFeePerGas: '1000000000', // 1 Gwei
maxFeePerGas: '3000000000', // 3 Gwei
nonce: '0',
chainId: '11155111', // Sepolia
type: 2,
);
final result = await para.signTransaction(
walletId: wallet.id!,
transaction: transaction.toJson(),
chainId: '11155111',
);
// The transactionData getter returns the complete RLP-encoded transaction
// ready for broadcasting via eth_sendRawTransaction
print('Signed transaction: ${result.transactionData}');
// For backward compatibility, signature field still contains the raw signature
print('Raw signature: ${result.signedTransaction}');
```
### Check Balance
```dart theme={null}
// Native ETH balance
final ethBalance = await para.getBalance(walletId: wallet.id!);
// ERC-20 token balance
final tokenBalance = await para.getBalance(
walletId: wallet.id!,
token: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
);
```
### Sign Message
```dart theme={null}
final message = 'Hello, Ethereum!';
final result = await para.signMessage(
walletId: wallet.id!,
message: message,
);
print('Signature: ${result.signedTransaction}');
```
## Networks
### Testnets
| Network | Chain ID | Native Token | Default RPC |
| ------------------ | ---------- | ------------ | --------------------------------------------- |
| **Sepolia** | `11155111` | ETH | `https://ethereum-sepolia-rpc.publicnode.com` |
| **Polygon Mumbai** | `80001` | MATIC | `https://rpc-mumbai.maticvigil.com` |
| **Base Sepolia** | `84532` | ETH | `https://sepolia.base.org` |
### Mainnets
| Network | Chain ID | Native Token | Default RPC |
| ------------ | -------- | ------------ | ------------------------------ |
| **Ethereum** | `1` | ETH | `https://eth.llamarpc.com` |
| **Polygon** | `137` | MATIC | `https://polygon-rpc.com` |
| **Base** | `8453` | ETH | `https://mainnet.base.org` |
| **Arbitrum** | `42161` | ETH | `https://arb1.arbitrum.io/rpc` |
| **Optimism** | `10` | ETH | `https://mainnet.optimism.io` |
## Complete Example
```dart theme={null}
import 'package:flutter/material.dart';
import 'package:para/para.dart';
class EVMWalletView extends StatefulWidget {
final Para para;
final Wallet wallet;
const EVMWalletView({required this.para, required this.wallet});
@override
State createState() => _EVMWalletViewState();
}
class _EVMWalletViewState extends State {
bool _isLoading = false;
String? _txHash;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
widget.wallet.address ?? 'No address',
style: TextStyle(fontFamily: 'monospace', fontSize: 12),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: _isLoading ? null : _sendETH,
child: Text(_isLoading ? 'Sending...' : 'Send 0.001 ETH'),
),
if (_txHash != null)
Padding(
padding: const EdgeInsets.only(top: 20),
child: Text('Sent: $_txHash', style: TextStyle(fontSize: 12)),
),
],
),
);
}
Future _sendETH() async {
setState(() => _isLoading = true);
try {
final result = await widget.para.transfer(
walletId: widget.wallet.id!,
to: '0x742d35Cc6634C0532925a3b844Bc454e4438f44e',
amount: '1000000000000000',
chainId: '11155111', // Optional: Sepolia testnet
rpcUrl: null, // Optional: custom RPC
);
setState(() => _txHash = result.hash);
} catch (e) {
print('Error: $e');
} finally {
setState(() => _isLoading = false);
}
}
}
```
## Smart Contract Interaction
**Transaction Data**: For EVM transactions, `result.transactionData` returns the complete RLP-encoded signed transaction that's ready to broadcast via `eth_sendRawTransaction`. The `signature` field contains just the raw signature for backward compatibility.
```dart theme={null}
// Call a contract function
final contractTransaction = EVMTransaction(
to: '0x123abc...', // Contract address
value: '0',
gasLimit: '150000',
maxPriorityFeePerGas: '1000000000',
maxFeePerGas: '3000000000',
nonce: '0',
chainId: '11155111', // Sepolia testnet
smartContractAbi: '''[{
"inputs": [{"name":"num","type":"uint256"}],
"name": "store",
"type": "function"
}]''',
smartContractFunctionName: 'store',
smartContractFunctionArgs: ['42'],
type: 2,
);
final result = await para.signTransaction(
walletId: wallet.id!,
transaction: contractTransaction.toJson(),
chainId: '11155111', // Sepolia testnet
);
// Use result.transactionData to get the complete signed transaction
print('Signed transaction: ${result.transactionData}');
```
### ERC20 Token Transfer
Transfer ERC20 tokens using the standard transfer function:
```dart theme={null}
// Transfer USDC on Sepolia testnet
final usdcTransaction = EVMTransaction(
to: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', // USDC contract on Sepolia
value: '0', // No ETH being sent, only tokens
gasLimit: '100000', // Higher gas limit for token transfers
maxPriorityFeePerGas: '1000000000', // 1 Gwei
maxFeePerGas: '3000000000', // 3 Gwei
nonce: '0',
chainId: '11155111', // Sepolia testnet
// ERC20 transfer function ABI
smartContractAbi: '''[{
"inputs": [
{"name": "to", "type": "address"},
{"name": "amount", "type": "uint256"}
],
"name": "transfer",
"outputs": [{"name": "", "type": "bool"}],
"type": "function"
}]''',
smartContractFunctionName: 'transfer',
smartContractFunctionArgs: [
'0x742d35Cc6634C0532925a3b844Bc454e4438f44e', // Recipient address
'100000', // 0.1 USDC (USDC has 6 decimals, so 100000 = 0.1 USDC)
],
type: 2, // EIP-1559 transaction
);
// Sign the token transfer transaction
final result = await para.signTransaction(
walletId: wallet.id!,
transaction: usdcTransaction.toJson(),
chainId: '11155111',
);
// The Flutter SDK sends the ABI and function parameters separately
// The bridge handles the encoding to create the proper transaction data
print('Token transfer signed: ${result.transactionData}');
// You can now broadcast this transaction using eth_sendRawTransaction
// or use Para's transfer method for automatic broadcasting
```
# External Wallets
Source: https://docs.getpara.com/v2/flutter/guides/external-wallets
Connect and authenticate users with external wallets like MetaMask and Phantom in Flutter.
## Overview
Para v2 supports authentication with external wallets, allowing users to connect their existing MetaMask, Phantom, or other wallets to authenticate with your Flutter application. The Flutter SDK maintains blockchain packages (web3dart, solana\_web3) to provide direct access to external wallet functionality while integrating with Para's unified wallet architecture.
This guide covers how to implement external wallet authentication and interaction using Para's connector classes.
## Prerequisites
Before implementing external wallet support, ensure you have:
1. Para SDK set up in your Flutter project (see the [Setup Guide](/v2/flutter/setup))
2. Deep linking configured for your app
3. Target external wallet apps installed on the device
## Deep Link Configuration
External wallet authentication requires deep linking to redirect users back to your app after wallet interaction. Configure this in both iOS and Android.
### Android Configuration
Add an intent filter to your `android/app/src/main/AndroidManifest.xml`:
```xml theme={null}
```
### iOS Configuration
Add your URL scheme to `ios/Runner/Info.plist`:
```xml theme={null}
CFBundleURLTypesCFBundleTypeRoleEditorCFBundleURLSchemesyourapp
```
## External Wallet Authentication
Para v2 provides a unified method for external wallet authentication:
```dart theme={null}
import 'package:para/para.dart';
Future authenticateWithExternalWallet(
String externalAddress,
String walletType,
) async {
try {
// Login with external wallet address
await para.loginExternalWallet(
externalAddress: externalAddress,
type: walletType, // "EVM" or "SOLANA"
);
// Check if authentication was successful
final isActive = await para.isSessionActive().future;
if (isActive) {
final wallets = await para.fetchWallets().future;
print('Authenticated with ${wallets.length} wallets');
}
} catch (e) {
print('External wallet authentication failed: $e');
}
}
```
## Working with Specific Wallets
Para provides dedicated connectors for popular external wallets:
### MetaMask Integration
Para includes a MetaMask connector for EVM interactions:
```dart theme={null}
import 'package:para/para.dart';
class MetaMaskService {
late ParaMetaMaskConnector _connector;
void initialize() {
_connector = ParaMetaMaskConnector(
para: para,
appUrl: 'https://yourapp.com',
appScheme: 'yourapp',
);
}
Future connectMetaMask() async {
try {
await _connector.connect();
print('MetaMask connected');
} catch (e) {
print('Failed to connect MetaMask: $e');
}
}
Future signMessage(String message) async {
if (_connector.accounts.isEmpty) {
throw Exception('No accounts connected');
}
final signature = await _connector.signMessage(
message,
_connector.accounts.first,
);
return signature;
}
Future sendTransaction({
required String toAddress,
required BigInt value,
}) async {
if (_connector.accounts.isEmpty) {
throw Exception('No accounts connected');
}
final transaction = Transaction(
from: EthereumAddress.fromHex(_connector.accounts.first),
to: EthereumAddress.fromHex(toAddress),
value: EtherAmount.inWei(value),
maxGas: 100000,
gasPrice: EtherAmount.inWei(BigInt.from(20000000000)), // 20 Gwei
);
final txHash = await _connector.sendTransaction(
transaction,
_connector.accounts.first,
);
return txHash;
}
}
```
### Phantom Integration
Para includes a Phantom connector for Solana interactions:
```dart theme={null}
import 'package:para/para.dart';
import 'package:solana_web3/solana_web3.dart';
class PhantomService {
late ParaPhantomConnector _connector;
void initialize() {
_connector = ParaPhantomConnector(
para: para,
appUrl: 'https://yourapp.com',
appScheme: 'yourapp',
);
}
Future connectPhantom() async {
try {
await _connector.connect();
print('Phantom connected');
} catch (e) {
print('Failed to connect Phantom: $e');
}
}
Future signMessage(String message) async {
final signature = await _connector.signMessage(message);
return signature;
}
/// Sign a transaction using serialized transaction bytes
/// Returns: Base58 encoded signed transaction that must be sent to the network
Future signTransactionBytes(Uint8List transactionBytes) async {
final signedTxBase58 = await _connector.signTransactionBytes(transactionBytes);
return signedTxBase58;
}
}
```
## Example: Complete External Wallet Flow
Here's a complete example showing external wallet authentication and usage:
```dart theme={null}
import 'package:flutter/material.dart';
import 'package:para/para.dart';
class ExternalWalletScreen extends StatefulWidget {
@override
_ExternalWalletScreenState createState() => _ExternalWalletScreenState();
}
class _ExternalWalletScreenState extends State {
ParaMetaMaskConnector? _metamaskConnector;
ParaPhantomConnector? _phantomConnector;
bool _isConnected = false;
@override
void initState() {
super.initState();
_initializeConnectors();
}
void _initializeConnectors() {
_metamaskConnector = ParaMetaMaskConnector(
para: para,
appUrl: 'https://yourapp.com',
appScheme: 'yourapp',
);
_phantomConnector = ParaPhantomConnector(
para: para,
appUrl: 'https://yourapp.com',
appScheme: 'yourapp',
);
}
Future _connectMetaMask() async {
try {
await _metamaskConnector!.connect();
setState(() => _isConnected = true);
// Check if Para session is active after connection
final isActive = await para.isSessionActive().future;
if (isActive) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('MetaMask connected and authenticated with Para!')),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to connect MetaMask: $e')),
);
}
}
Future _connectPhantom() async {
try {
await _phantomConnector!.connect();
setState(() => _isConnected = true);
// Check if Para session is active after connection
final isActive = await para.isSessionActive().future;
if (isActive) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Phantom connected and authenticated with Para!')),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to connect Phantom: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('External Wallets')),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text(
'Connect an external wallet to get started',
style: Theme.of(context).textTheme.headlineSmall,
),
SizedBox(height: 32),
// MetaMask Connection
ElevatedButton.icon(
onPressed: _connectMetaMask,
icon: Icon(Icons.account_balance_wallet),
label: Text('Connect MetaMask'),
style: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 50),
),
),
SizedBox(height: 16),
// Phantom Connection
ElevatedButton.icon(
onPressed: _connectPhantom,
icon: Icon(Icons.account_balance_wallet),
label: Text('Connect Phantom'),
style: ElevatedButton.styleFrom(
minimumSize: Size(double.infinity, 50),
),
),
],
),
),
);
}
}
```
## Security Considerations
When working with external wallets:
1. **Validate Connections**: Always verify the wallet connection before performing operations
2. **Handle Errors Gracefully**: External wallet apps may not be installed or might reject connections
3. **User Experience**: Provide clear instructions for users on wallet installation and connection
4. **Permissions**: Ensure your app requests appropriate permissions for wallet interactions
5. **Deep Link Security**: Validate deep link callbacks to prevent malicious redirects
## Troubleshooting
Common issues and solutions:
### Connection Failures
```dart theme={null}
Future connectWithRetry(VoidCallback connectFunction) async {
int retries = 3;
while (retries > 0) {
try {
await connectFunction();
return;
} catch (e) {
retries--;
if (retries == 0) {
throw Exception('Failed to connect after 3 attempts: $e');
}
await Future.delayed(Duration(seconds: 2));
}
}
}
```
### Phantom Transaction Errors
When working with Phantom, use the transaction helper for proper encoding:
```dart theme={null}
import 'package:para/para.dart';
// Convert signed transaction for network submission
final signedTxBase64 = ParaPhantomTransactionHelper.signedTransactionToBase64(signedTxBase58);
// Send to Solana network
final signature = await solanaClient.rpcClient.sendTransaction(signedTxBase64);
```
### Wallet App Not Installed
```dart theme={null}
Future isWalletInstalled(String walletScheme) async {
try {
return await canLaunchUrl(Uri.parse('$walletScheme://'));
} catch (e) {
return false;
}
}
Future openWalletInstallPage(String storeUrl) async {
if (await canLaunchUrl(Uri.parse(storeUrl))) {
await launchUrl(Uri.parse(storeUrl));
}
}
```
### Network Issues
Ensure you have the correct network selected in your external wallet:
* **Phantom**: Check that you're on the intended Solana network (mainnet-beta, devnet, testnet)
* **MetaMask**: Verify you're connected to the correct Ethereum network
* **Transactions**: Use testnet/devnet for development to avoid spending real funds
## Key Features
Para's Flutter SDK provides:
* **Unified Architecture**: External wallets integrate seamlessly with Para's unified wallet system
* **Direct Blockchain Access**: Uses web3dart for Ethereum and solana\_web3 for Solana interactions
* **Deep Link Support**: Handles wallet app redirections automatically
* **Transaction Helpers**: Utility classes for transaction encoding/decoding
* **Error Handling**: Comprehensive error handling for wallet interactions
⚠️ Important Notes
* External wallets use blockchain packages directly for maximum compatibility
* The Flutter SDK maintains these dependencies to support external wallet features
* Phantom's `signAndSendTransaction` method is deprecated - use `signTransactionBytes` instead
* Always validate wallet connections before performing operations
## Resources
For more information about external wallet integration:
# Wallet Pregeneration
Source: https://docs.getpara.com/v2/flutter/guides/pregen
Create and manage pregenerated wallets for users in Flutter applications
Para's Wallet Pregeneration feature allows you to create wallets for users before they authenticate, giving you control over when and how users claim ownership of their wallets. This is particularly powerful in mobile applications, where you can leverage device-specific storage capabilities for enhanced user experiences.
## Mobile-Specific Benefits
While pregeneration works the same across all Para SDKs, Flutter applications offer unique advantages:
Pregeneration is especially valuable for devices that may not have full WebAuthn support for passkeys. It allows you to create Para wallets for users on any device while managing the security of the wallet yourself.
## Creating Pregenerated Wallets
In Flutter, you can create pregenerated wallets of multiple types with a single method call:
```dart theme={null}
import 'package:para/para.dart';
Future> createPregenWallets() async {
final pregenWalletsFuture = para.createPregenWalletPerType(
pregenId: {'EMAIL': 'user@example.com'}, // Map format for pregen ID
types: [WalletType.evm], // Optionally specify wallet types
);
final pregenWallets = await pregenWalletsFuture.future;
// Get the user share
final userShareFuture = para.getUserShare();
final userShare = await userShareFuture.future;
// Store user share securely (see storage options below)
return pregenWallets;
}
```
## Mobile Storage Options
In Flutter applications, you have several options for securely storing the user share:
```dart theme={null}
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = FlutterSecureStorage();
// Store the user share
Future storeUserShare(String userShare) async {
try {
await storage.write(
key: 'para_user_share',
value: userShare,
);
} catch (e) {
// Handle error
}
}
// Retrieve the user share
Future retrieveUserShare() async {
try {
return await storage.read(key: 'para_user_share');
} catch (e) {
// Handle error
return null;
}
}
```
```dart theme={null}
import 'package:encrypted_shared_preferences/encrypted_shared_preferences.dart';
final encryptedPrefs = EncryptedSharedPreferences();
// Store the user share
Future storeUserShare(String userShare) async {
try {
await encryptedPrefs.setString('para_user_share', userShare);
} catch (e) {
// Handle error
}
}
// Retrieve the user share
Future retrieveUserShare() async {
try {
return await encryptedPrefs.getString('para_user_share');
} catch (e) {
// Handle error
return null;
}
}
```
Whichever storage method you choose, ensure you implement proper security measures. The user share is critical for wallet access, and if lost, the wallet becomes permanently inaccessible.
## Using Pregenerated Wallets in Flutter Apps
Once you have created a pregenerated wallet and stored the user share, you can use it for signing operations:
```dart theme={null}
import 'dart:convert';
import 'package:para/para.dart';
Future usePregenWallet(String walletId) async {
// Retrieve the user share from your secure storage
final userShare = await retrieveUserShare();
if (userShare == null) {
throw Exception("User share not found");
}
// Load the user share into the Para client
await para.setUserShare(userShare);
// Now you can perform signing operations
final messageBase64 = base64Encode(utf8.encode("Hello, World!"));
final signatureFuture = para.signMessage(
walletId: walletId,
messageBase64: messageBase64,
);
final signatureResult = await signatureFuture.future;
// The signature is in the signedTransaction property
return (signatureResult as SuccessfulSignatureResult).signedTransaction;
}
```
### Mobile-Specific Use Cases
Create wallets that are bound to a specific device by using device-specific identifiers combined with secure local storage. This approach is ideal for multi-device users who need different wallets for different devices.
```dart theme={null}
import 'package:device_info_plus/device_info_plus.dart';
import 'package:para/para.dart';
Future> createDeviceWallet() async {
final deviceInfo = DeviceInfoPlugin();
String deviceId;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
deviceId = androidInfo.id;
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
deviceId = iosInfo.identifierForVendor ?? 'unknown';
} else {
deviceId = 'unknown';
}
final pregenWalletsFuture = para.createPregenWalletPerType(
pregenId: {'CUSTOM_ID': 'device-$deviceId'},
);
final pregenWallets = await pregenWalletsFuture.future;
// Store the user share in device-specific secure storage
final userShare = await para.getUserShare().future;
await storeUserShare(userShare);
return pregenWallets;
}
```
Seamlessly introduce blockchain functionality to your existing app users without requiring them to understand wallets or crypto.
```dart theme={null}
import 'package:para/para.dart';
Future> createWalletForExistingUser(String userId) async {
final pregenIdentifier = "user-$userId";
try {
final pregenWalletsFuture = para.createPregenWalletPerType(
pregenId: {'CUSTOM_ID': pregenIdentifier},
);
final pregenWallets = await pregenWalletsFuture.future;
final userShare = await para.getUserShare().future;
await storeUserShare(userShare);
return pregenWallets;
} catch (e) {
// Handle errors
throw e;
}
}
```
## Claiming Pregenerated Wallets
When a user is ready to take ownership of their pregenerated wallet, they can claim it once they've authenticated with Para:
```dart theme={null}
import 'package:para/para.dart';
Future claimWallet(Map pregenId) async {
// Ensure user is authenticated
if (!(await para.isSessionActive().future)) {
throw Exception("User must be authenticated to claim wallets");
}
// Retrieve and load the user share
final userShare = await retrieveUserShare();
if (userShare != null) {
await para.setUserShare(userShare);
}
// Claim the wallet with the pregen ID
final claimFuture = para.claimPregenWallets(
pregenId: pregenId, // e.g., {'EMAIL': 'user@example.com'}
);
final recoverySecret = await claimFuture.future;
// Optionally, clear the locally stored user share after claiming
// since Para now manages it through the user's authentication
await clearUserShare();
return recoverySecret;
}
```
After claiming, Para will manage the user share through the user's authentication methods. You can safely remove the user share from your local storage if you no longer need to access the wallet directly.
## Best Practices for Mobile
1. **Utilize Device Security**: Leverage biometric authentication (TouchID/FaceID) to protect access to locally stored user shares.
2. **Implement Device Sync**: For users with multiple devices, consider implementing your own synchronization mechanism for user shares across devices.
3. **Handle Offline States**: Mobile applications often work offline. Design your pregenerated wallet system to function properly even when connectivity is limited.
4. **Backup Strategies**: Provide users with options to back up their wallet data, especially for device-specific wallets that might not be associated with their Para account.
5. **Clear Security Boundaries**: Clearly communicate to users when they're using an app-managed wallet versus a personally-owned wallet.
## Related Resources
# Flutter Session Management
Source: https://docs.getpara.com/v2/flutter/guides/sessions
Guide to managing authentication sessions in Para for Flutter applications
Para provides a comprehensive set of methods for managing authentication sessions in Flutter applications. These sessions are crucial for secure transaction signing and other authenticated operations.
## Session Duration
The Para session length is `2 hours` by default, but can be configured to up to 30 days. To configure this parameter, please visit the Configuration section of the . A user signing a message or transaction extends the session by the duration of the session length.
## Managing Sessions
### Checking Session Status
Use `isSessionActive()` to verify whether a user's session is currently valid before performing authenticated operations.
```dart theme={null}
Future isSessionActive()
```
In Flutter applications, it's especially important to check the session status before allowing users to access authenticated areas of your app due to the persistence of local storage between app launches.
Example usage:
```dart theme={null}
import 'package:para/para.dart';
Future checkSession() async {
try {
final isActive = await para.isSessionActive().future;
if (!isActive) {
// First clear any existing data
await para.logout().future;
// Navigate to login screen
// Handle navigation according to your app's navigation strategy
} else {
// Session is valid, proceed with app flow
// Navigate to authenticated part of your app
}
} catch (e) {
// Handle error
}
}
```
### Refreshing Expired Sessions
When a session has expired, Para recommends initiating a full authentication flow rather than trying to refresh the session.
For Flutter applications, always call `logout()` before reinitiating authentication when a session has expired to ensure all stored data is properly cleared.
```dart theme={null}
import 'package:para/para.dart';
Future handleSessionExpiration() async {
// When session expires, first clear storage
await para.logout().future;
// Then redirect to authentication screen
// Handle navigation according to your app's navigation strategy
}
```
## Exporting Sessions to Your Server
Use `exportSession()` when you need to transfer session state to your server for performing operations on behalf of the user.
```dart theme={null}
String exportSession()
```
Example implementation:
```dart theme={null}
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:para/para.dart';
Future