# Architecture Overview
Source: https://docs.getpara.com/concepts/architecture
An introduction to Para's architecture and core components
Para provides a robust and secure architecture for creating and managing 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 wallet portability and cross-application usage.
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 embedded wallets in their applications across various platforms and blockchain
ecosystems.
# Key Management System
Source: https://docs.getpara.com/concepts/key-management
An in-depth look at Para's innovative approach to secure key management using MPC and hardware secure enclaves
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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.
# Wallet Portability
Source: https://docs.getpara.com/concepts/portability
Exploring Para's approach to wallet portability and seamless cross-application usage
Para allows 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.
Wallet portability is not about transferring assets between wallets, but rather accessing the same wallet from
different applications and platforms.
## Key Components
Para's approach to 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
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 wallet 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 wallet
2. * Access to richer transaction history from shared wallets
3. * Easily craft multi-app experiences and build an ecosystem
4. * request specific permissions based on the application's needs.
## Wallet Portability 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 | Para Portable 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 Portable 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 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 wallet portability. 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 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.
# Wallet Recovery Process
Source: https://docs.getpara.com/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 Key**: This is shared with the user in the Para Backup Kit. The Cloud Key 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/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) 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 even in the event of service disruptions. Measures
include:
* Option for users to export their Cloud Key
* 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/concepts/technical-faq
Frequently asked technical questions about implementing and using Para for developers
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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 and signing ceremonies. 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 key 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.
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.
Wallet portability 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.
For further technical details or questions not covered here, please refer to our documentation or reach out to our
developer support team at .
# Mobile Examples
Source: https://docs.getpara.com/flutter/examples
Explore Para's mobile integration examples for Flutter
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return
;
};
Para offers comprehensive Flutter examples to help you integrate our technology into your cross-platform mobile applications. Our example repository demonstrates minimal implementation requirements with clean, focused code that you can easily adapt to your specific application needs.
## Para Flutter Example
Explore our Flutter example showcasing Para integration with various features:
The Flutter example demonstrates:
* Email and passkey authentication
* Social login integration
* Wallet creation and management
* Transaction signing across multiple chains
* Session management
* Pregenerated wallets
## 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.
# Cosmos Integration
Source: https://docs.getpara.com/flutter/guides/cosmos
Integrate Para with Cosmos ecosystem in your Flutter applications (Coming Soon)
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
**Coming Soon**: Cosmos integration for Flutter applications is currently in development and will be available soon. Reach out to us at if you'd like to get early access.
Para will soon provide support for Cosmos ecosystem integration in Flutter applications, allowing you to leverage Para's secure wallet infrastructure with Cosmos-based blockchains.
# EVM Integration
Source: https://docs.getpara.com/flutter/guides/evm
Integrate Para with EVM-compatible chains in your Flutter applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
## Introduction
This guide demonstrates how to integrate Para with Ethereum Virtual Machine (EVM) compatible blockchains in your Flutter applications. Para acts as a secure signer for your transactions, while Web3Dart handles the blockchain interactions.
Para's approach to EVM transaction signing is designed for flexibility and security:
* Para signs the raw transaction bytes without modifying the transaction data
* Your application maintains full control over transaction construction
* Para never has access to your application's blockchain RPC endpoints
## Prerequisites
Before you begin, ensure you have:
1. Para SDK set up in your Flutter project (see the [Setup Guide](/flutter/setup))
2. A user authenticated with Para
3. Web3Dart package installed in your project
```bash
flutter pub add web3dart:'^2.7.3'
```
Use version `^2.7.3` of Web3Dart to ensure compatibility with Para's EVM signer.
## Setting Up the Para EVM Signer
The Para SDK provides an EVM signer that implements interfaces compatible with Web3Dart. First, let's create a signer and connect it to Web3Dart:
```dart
import 'package:para/para.dart';
import 'package:web3dart/web3dart.dart';
import 'package:http/http.dart';
Future setupEvmSigner() async {
// Initialize Para SDK (if not already done)
final para = Para(
environment: Environment.beta,
apiKey: 'YOUR_API_KEY',
);
// Get the user's EVM wallet
final wallets = await para.getWallets();
final evmWallet = wallets.values
.firstWhere((wallet) => wallet.type == WalletType.evm);
// Create the Para EVM signer
final signer = await para.getEvmSigner(evmWallet);
// Initialize Web3Dart client with your RPC endpoint
final client = Web3Client(
'https://rpc.ankr.com/eth_sepolia', // Example RPC URL (Sepolia testnet)
Client(),
);
// The signer and client are now ready for transaction creation and signing
}
```
## Signing Transactions
Para acts as the underlying signer, taking the serialized version of the transaction data and signing the raw bytes. Para never modifies the transaction data, which means your application is responsible for properly constructing transactions.
### EIP-1559 Transaction
EIP-1559 transactions are the recommended format for most modern EVM chains, offering more predictable gas fees:
```dart
Future sendEip1559Transaction(
Web3Client client,
ParaEvmSigner signer,
EthereumAddress recipientAddress,
BigInt amount,
) async {
// Prepare transaction parameters
final credentials = signer.credentials;
final fromAddress = await credentials.extractAddress();
// Get the latest gas parameters from the network
final gasPrice = await client.getGasPrice();
final chainId = await client.getChainId();
final nonce = await client.getTransactionCount(fromAddress);
// Create the transaction
final transaction = Transaction(
from: fromAddress,
to: recipientAddress,
value: EtherAmount.fromBigInt(EtherUnit.wei, amount),
nonce: nonce,
maxGas: 21000, // Standard gas limit for ETH transfers
maxFeePerGas: gasPrice,
maxPriorityFeePerGas: EtherAmount.fromBigInt(EtherUnit.gwei, BigInt.from(1)),
);
// Sign and send the transaction
final txHash = await client.sendTransaction(
credentials,
transaction,
chainId: chainId.toInt(),
);
return txHash;
}
```
## Interacting with Smart Contracts
Para can also sign transactions that interact with smart contracts:
```dart
Future callSmartContract(
Web3Client client,
ParaEvmSigner signer,
String contractAddress,
String contractAbi,
String functionName,
List parameters,
) async {
// Prepare transaction parameters
final credentials = signer.credentials;
final fromAddress = await credentials.extractAddress();
// Parse the contract ABI and create a contract instance
final contract = DeployedContract(
ContractAbi.fromJson(contractAbi, 'Contract'),
EthereumAddress.fromHex(contractAddress),
);
// Get the function from the contract
final function = contract.function(functionName);
// Estimate gas needed for this transaction
final gasEstimate = await client.estimateGas(
sender: fromAddress,
to: contract.address,
data: function.encodeCall(parameters),
);
// Get the latest gas parameters and nonce
final gasPrice = await client.getGasPrice();
final chainId = await client.getChainId();
final nonce = await client.getTransactionCount(fromAddress);
// Create and send the transaction
final txHash = await client.sendTransaction(
credentials,
Transaction(
from: fromAddress,
to: contract.address,
data: function.encodeCall(parameters),
maxGas: (gasEstimate * BigInt.from(1.5)).toInt(), // Add buffer to gas estimate
maxFeePerGas: gasPrice,
maxPriorityFeePerGas: EtherAmount.fromBigInt(EtherUnit.gwei, BigInt.from(1)),
nonce: nonce,
),
chainId: chainId.toInt(),
);
return txHash;
}
```
## Error Handling
When working with Para and EVM transactions, you might encounter several types of errors:
1. **Para Signing Errors**: Occur when Para cannot sign the transaction (e.g., user cancels or verification fails)
2. **Transaction Formation Errors**: Occur when the transaction is improperly formed
3. **RPC Errors**: Occur when the blockchain node rejects the transaction
## Best Practices
When using Para with EVM chains:
1. **Always verify transaction data before signing**: Para signs whatever you provide, so ensure transaction data is correct
2. **Keep RPC endpoints secure**: Never expose your RPC endpoints in client-side code
3. **Estimate gas properly**: Always estimate gas and add a buffer to avoid transaction failures
4. **Handle nonce management**: Track nonces carefully, especially for high-frequency transaction applications
5. **Test on testnets first**: Always test your integration on testnets before moving to mainnet
## Multi-Chain Support
Para's EVM signer works with any EVM-compatible chain. To support multiple chains, simply change the RPC endpoint in your Web3Client:
```dart
// Ethereum Mainnet
final ethMainnetClient = Web3Client(
'https://rpc.ankr.com/eth',
Client(),
);
// Polygon Mainnet
final polygonClient = Web3Client(
'https://rpc.ankr.com/polygon',
Client(),
);
// Arbitrum
final arbitrumClient = Web3Client(
'https://rpc.ankr.com/arbitrum',
Client(),
);
```
## Example
For an example on using Para with EVM in Flutter, check out this evm\_sign\_example.dart in our examples repository:
## Additional Resources
For more information on Web3Dart usage, refer to the Web3Dart documentation:
# Flutter Session Management
Source: https://docs.getpara.com/flutter/guides/sessions
Guide to managing authentication sessions in Para for Flutter applications
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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
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
import 'package:para_flutter/client/para.dart';
Future checkSession() async {
try {
final isActive = await para.isSessionActive();
if (!isActive) {
// First clear any existing data
await para.logout();
// 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
import 'package:para_flutter/client/para.dart';
Future handleSessionExpiration() async {
// When session expires, first clear storage
await para.logout();
// 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
String exportSession()
```
Example implementation:
```dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:para_flutter/client/para.dart';
Future> sendSessionToServer() async {
// Export session without signing capabilities
final sessionData = para.exportSession();
// Send to your server
try {
final response = await http.post(
Uri.parse('https://your-api.com/sessions'),
headers: {
'Content-Type': 'application/json',
},
body: jsonEncode({'session': sessionData}),
);
if (response.statusCode != 200) {
throw Exception('Failed to send session to server');
}
return jsonDecode(response.body);
} catch (e) {
// Handle error
throw e;
}
}
```
## Best Practices for Flutter
1. **Check Sessions on App Launch**: Verify session status when your app starts to determine if users need to reauthenticate.
```dart
import 'package:flutter/material.dart';
import 'package:para_flutter/client/para.dart';
// In your app's entry point or state initialization
@override
void initState() {
super.initState();
checkSessionOnLaunch();
}
Future checkSessionOnLaunch() async {
final isActive = await para.isSessionActive();
if (isActive) {
// Navigate to authenticated part of your app
} else {
await para.logout(); // Clear any lingering data
// Navigate to login screen
}
}
```
2. **Handle App Lifecycle Changes**: Flutter apps can be backgrounded and foregrounded, which may affect session status.
```dart
import 'package:flutter/material.dart';
import 'package:para_flutter/client/para.dart';
class YourWidget extends StatefulWidget {
@override
_YourWidgetState createState() => _YourWidgetState();
}
class _YourWidgetState extends State with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
// App came to foreground, check session
checkSession();
}
}
Future checkSession() async {
final isActive = await para.isSessionActive();
if (!isActive) {
await para.logout();
// Navigate to login screen
}
}
@override
Widget build(BuildContext context) {
// Your widget implementation
return Container();
}
}
```
## Next Steps
Explore more advanced features and integrations with Para in Flutter:
# Social Login
Source: https://docs.getpara.com/flutter/guides/social-login
Implementing social login with Para in Flutter applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para supports social login authentication in Flutter applications, allowing users to sign in using popular providers like Google, Apple, Twitter, Discord, and Farcaster. This guide explains how to implement social login in your Flutter app using Para's Flutter SDK.
The social login flow in mobile applications requires handling browser sessions and deeplinks to redirect users back to your app after successful authentication. Once the social login flow completes, Para uses the returned identifier to authenticate users through the standard email-based flow, creating or authenticating with a native passkey.
## Prerequisites
Before implementing social login, ensure you have completed the basic Para setup for your Flutter application.
## Configuration
Add URL schemes to your `Info.plist` file to handle callbacks from authentication:
```xml
CFBundleURLTypes
CFBundleTypeRole
Editor
CFBundleURLSchemes
YOUR_APP_SCHEME
```
In your `AndroidManifest.xml`, add the callback activity and URL scheme:
```xml
```
Replace `YOUR_APP_SCHEME` with your application's unique URL scheme (e.g., `paraflutter`). This scheme must be unique to your application and will be used for redirecting back to your app after authentication.
## Implementation
### OAuth Authentication
```dart OAuth Authentication
import 'package:para/para.dart';
import 'package:para_flutter/client/para.dart';
// OAuth connection with Para
final oAuthResponse = await para.oAuthConnect(provider, "YOUR_APP_SCHEME");
// For existing users
if (oAuthResponse.userExists) {
// Login with the email from OAuth response
final wallet = await para.login(email: oAuthResponse.email);
}
// For new users
else {
// Verify OAuth completion
final biometricsId = await para.verifyOAuth();
// Generate passkey
await para.generatePasskey(oAuthResponse.email!, biometricsId);
// Create wallet
final result = await para.createWallet(skipDistribute: false);
}
```
```dart Farcaster Authentication
import 'package:para/para.dart';
import 'package:para_flutter/client/para.dart';
// Connect with Farcaster
final farcasterResponse = await para.farcasterConnect();
// For existing users
if (farcasterResponse.userExists) {
// Login with Farcaster username
final wallet = await para.login(farcasterUsername: farcasterResponse.username);
}
// For new users
else {
// Verify OAuth completion
final biometricsId = await para.verifyOAuth();
// Generate passkey
await para.generatePasskey(farcasterResponse.username, biometricsId);
// Create wallet
final result = await para.createWallet(skipDistribute: false);
}
```
Social login with Para still requires creating a native passkey to secure the user's wallets. After social authentication completes, Para associates a native passkey with the user's account. For returning users, the native passkey is used for authentication. The passkey is associated on a per-app basis, making authentication streamlined, and users will only see passkey options they created for your specific app.
## Available OAuth Providers
Para supports various OAuth providers through the `OAuthMethod` enum:
```dart
// Available OAuth methods
OAuthMethod.google // Google authentication
OAuthMethod.apple // Apple authentication
OAuthMethod.twitter // Twitter (X.com) authentication
OAuthMethod.discord // Discord authentication
```
## Checking Login Status
You may want to check if a user is already authenticated when your app starts:
```dart
// Check if user is logged in
final isLoggedIn = await para.isFullyLoggedIn();
// If logged in, get user wallets
if (isLoggedIn) {
final wallets = await para.getWallets();
// Process wallets as needed
}
```
## Examples
Explore our complete example implementations for social login with Para:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Solana Integration
Source: https://docs.getpara.com/flutter/guides/solana
Integrate Para with Solana in your Flutter applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
## Introduction
This guide demonstrates how to integrate Para with Solana in your Flutter applications. Para acts as a secure signer for your Solana transactions, while the Solana Dart package handles the blockchain interactions.
Para's approach to Solana transaction signing is designed for flexibility and security:
* Para signs the raw transaction bytes without modifying the transaction data
* Your application maintains full control over transaction construction
* Your private keys remain secure in Para's wallet infrastructure
## Prerequisites
Before you begin, ensure you have:
1. Para SDK set up in your Flutter project (see the [Setup Guide](/flutter/setup))
2. A user authenticated with Para
3. Solana Dart package installed in your project
```bash
flutter pub add solana
```
## Setting Up the Para Solana Signer
The Para SDK provides a Solana signer that works with the Solana Dart package. First, let's create the necessary components:
```dart
import 'package:para/para.dart';
import 'package:solana/solana.dart' as web3;
// Initialize Para and get wallet (assuming user is already authenticated)
final para = Para(
environment: Environment.beta,
apiKey: 'YOUR_API_KEY',
);
// Set up Solana client with the desired network
final devnetRpcUrl = Uri.parse('https://api.devnet.solana.com');
final devnetWsUrl = Uri.parse('wss://api.devnet.solana.com');
final solanaClient = web3.SolanaClient(
rpcUrl: devnetRpcUrl,
websocketUrl: devnetWsUrl,
);
// Initialize Para Solana signer
final solanaSigner = ParaSolanaWeb3Signer(
para: para,
solanaClient: solanaClient,
);
```
## Sending Transactions
After signing a transaction, you can send it to the Solana network:
```dart
Future sendSolanaTransaction(
Wallet wallet,
String recipientAddress,
double amountInSol,
) async {
// Convert wallet address to Solana public key
final publicKey = web3.Ed25519HDPublicKey.fromBase58(wallet.address!);
// Convert recipient address to Solana public key
final recipient = web3.Ed25519HDPublicKey.fromBase58(recipientAddress);
// Get the latest blockhash (required for Solana transactions)
final blockhash = (await solanaClient.rpcClient.getLatestBlockhash()).value;
// Convert SOL amount to lamports (Solana's smallest unit)
final lamports = (web3.lamportsPerSol * amountInSol).toInt();
// Create a transfer instruction
final instruction = web3.SystemInstruction.transfer(
fundingAccount: publicKey,
recipientAccount: recipient,
lamports: lamports,
);
// Create and compile the message
final message = web3.Message(instructions: [instruction]);
final compiledMessage = message.compile(
recentBlockhash: blockhash.blockhash,
feePayer: publicKey,
);
// Sign the transaction using Para
final signedTransaction = await solanaSigner.signTransaction(compiledMessage);
// Send the signed transaction to the network
final signature = await solanaSigner.sendTransaction(signedTransaction);
return signature;
}
```
## Checking Transaction Status
After sending a transaction, you may want to check its status:
```dart
Future confirmTransaction(String signature) async {
try {
final confirmation = await solanaClient.rpcClient
.confirmTransaction(signature, commitment: web3.Commitment.confirmed);
return confirmation.value;
} catch (e) {
return false;
}
}
```
## Working with SPL Tokens
Para can also sign transactions involving SPL tokens (Solana's token standard). Here's an example of transferring an SPL token:
```dart
import 'package:solana/solana.dart' as web3;
import 'package:solana/token_program.dart' as token_program;
Future transferSplToken(
Wallet wallet,
String recipientAddress,
String tokenMintAddress,
double amount,
int decimals,
) async {
// Convert wallet address to Solana public key
final publicKey = web3.Ed25519HDPublicKey.fromBase58(wallet.address!);
// Convert recipient address to Solana public key
final recipient = web3.Ed25519HDPublicKey.fromBase58(recipientAddress);
// Convert token mint address to Solana public key
final tokenMint = web3.Ed25519HDPublicKey.fromBase58(tokenMintAddress);
// Get the latest blockhash
final blockhash = (await solanaClient.rpcClient.getLatestBlockhash()).value;
// Find the token account addresses for sender and recipient
final senderTokenAccount = await token_program.findAssociatedTokenAddress(
owner: publicKey,
mint: tokenMint,
);
final recipientTokenAccount = await token_program.findAssociatedTokenAddress(
owner: recipient,
mint: tokenMint,
);
// Check if recipient token account exists, if not create it
final instructions = [];
final accounts = await solanaClient.rpcClient
.getTokenAccountsByOwner(
recipient.toBase58(),
const web3.TokenAccountsFilter.byMint(tokenMint),
);
final recipientTokenAccountExists = accounts.value.any(
(account) => account.pubkey == recipientTokenAccount,
);
if (!recipientTokenAccountExists) {
instructions.add(
token_program.createAssociatedTokenAccountInstruction(
associatedToken: recipientTokenAccount,
payer: publicKey,
owner: recipient,
mint: tokenMint,
),
);
}
// Convert token amount accounting for decimals
final tokenAmount = (amount * (10 * decimals)).toInt();
// Add token transfer instruction
instructions.add(
token_program.transferInstruction(
source: senderTokenAccount,
destination: recipientTokenAccount,
owner: publicKey,
amount: tokenAmount,
),
);
// Create and compile the message
final message = web3.Message(instructions: instructions);
final compiledMessage = message.compile(
recentBlockhash: blockhash.blockhash,
feePayer: publicKey,
);
// Sign the transaction using Para
final signedTransaction = await solanaSigner.signTransaction(compiledMessage);
// Send the signed transaction
final signature = await solanaSigner.sendTransaction(signedTransaction);
return signature;
}
```
## Best Practices
When using Para with Solana:
1. **Always verify transaction data before signing**: Para signs whatever you provide, so ensure transaction data is correct
2. **Use appropriate commitment levels**: For important transactions, wait for "confirmed" or "finalized" commitment
3. **Handle network congestion**: Solana transactions can fail during network congestion, implement appropriate retry mechanisms
4. **Check token accounts**: Always verify token accounts exist before sending SPL tokens
5. **Test on devnet first**: Always test your integration on Solana devnet before moving to mainnet
## Example
For an example of signing with Solana transactions using para and the Solana Dart package, check out the following code example:
## Resources
For more information about using the Solana Dart package, refer to the official documentation:
# Overview
Source: https://docs.getpara.com/flutter/overview
An introduction to Flutter integrations with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para provides comprehensive support for Flutter, allowing you to easily implement secure wallet and authentication functionality in your cross-platform mobile applications. This overview will guide you through the available integrations and features.
## Getting Started with Flutter
## Blockchain Ecosystem Integrations
Para works seamlessly with major blockchain ecosystems in your Flutter apps, allowing you to leverage Para's authentication alongside chain-specific libraries and development tools.
## Advanced Features
Customize Para's behavior and extend its functionality with these advanced configuration guides.
## Authentication Options
Enhance your application's security and user experience with additional authentication methods.
## Implementation Examples
Explore complete implementation examples to accelerate your Flutter development with Para.
# Flutter (Mobile)
Source: https://docs.getpara.com/flutter/setup
Step-by-step instructions for integrating the Para Flutter SDK into your mobile app.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide walks you through the setup of Para in a Flutter application. You'll learn how to install the SDK, configure iOS/Android for passkey-based logins, implement user authentication flows, and generate wallets.
If you haven't already created your Flutter app, follow
to set up a new project.
## 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.
## Installation
Start by installing the Para SDK:
```bash
flutter pub add para
```
## Project Setup
To set up associated domains for passkey functionality in your Flutter project, you need to configure both iOS and
Android platforms:
To enable passkeys on iOS, you need to set up associated domains in your Xcode project:
1. Open your Flutter project's iOS folder in Xcode
2. Select your target and go to "Signing & Capabilities"
3. Click "+ Capability" and add "Associated Domains"
4. Add the following domains:
* `webcredentials:app.beta.usecapsule.com`
* `webcredentials:app.usecapsule.com`
For more details, see the .
**Important**: You must register your TeamId and BundleId with Para via the to use associated domains. This is required by Apple for passkey security. Allow up to 24 hours for domain propagation.
For Android setup, you need to provide your app's SHA-256 certificate fingerprint to Para.
**Quick Testing Option**: You can use `com.getpara.example.flutter` as your package name for immediate testing. This package name is pre-registered and works with the SHA-256 certificate from the default debug.keystore, making it testable in debug mode for newly scaffolded Flutter apps.
### Fix Namespace Issue
For newer versions of Flutter, you need to add the following configuration block to your `android/build.gradle` file to resolve a namespace issue with the passkey dependency:
```gradle
subprojects {
afterEvaluate { project ->
if (project.hasProperty('android')) {
project.android {
if (namespace == null) {
namespace project.group
}
}
}
}
}
```
### Get SHA-256 Fingerprint
To get your SHA-256 fingerprint:
* For debug builds: `keytool -list -v -keystore ~/.android/debug.keystore`
* For release builds: `keytool -list -v -keystore `
For production apps, you'll need to: 1. Upgrade your plan in the Developer Portal 2. Register your actual package name 3. Provide your app's SHA-256 fingerprint 4. Wait up to 24 hours for the Digital Asset Links to propagate
To ensure passkey functionality works correctly:
* Enable biometric or device unlock settings (fingerprint, face unlock, or PIN)
* Sign in to a Google account on the device (required for Google Play Services passkey management)
## Initializing Para
To use Para's features, you'll need to initialize a Para client instance that can be accessed throughout your app. This
client handles all interactions with Para's services, including authentication, wallet management, and transaction
signing.
Create a file (e.g., `lib/services/para_client.dart`) to initialize your Para client:
```dart
import 'package:para/para.dart';
// Initialize a global Para client instance
final para = Para(
environment: Environment.beta, // Use Environment.prod for production
apiKey: 'YOUR_PARA_API_KEY',
);
```
You can access `para` from anywhere in your app by importing the file where you initialized it. This singleton pattern
ensures consistent state management across your application.
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
## Using the Para SDK
The Para SDK provides multiple authentication methods including email and phone. Here's how to implement these methods in your Flutter 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.
### Create a User with Email
Follow these steps to register a new user with email verification:
```dart
Future registerUser(String email) async {
// Check if user exists
if (await para.checkIfUserExists(email)) {
// User exists, handle login flow
return;
}
// Create a new user account - this sends a verification code
await para.createUser(email);
// Next step: Verify the email with the code
}
Future verifyAndCreateWallet(String email, String verificationCode) async {
// Verify the code to get biometricsId
final biometricsId = await para.verifyEmail(verificationCode);
// Generate a passkey for this device
await para.generatePasskey(email, biometricsId);
// Create a wallet for the user
final result = await para.createWallet(skipDistribute: false);
// Store the recovery share securely
final recoveryShare = result.recoveryShare;
}
```
### Login with Email
To authenticate an existing user with their email:
```dart
Future loginUser() async {
// This triggers the platform's native passkey selection UI
final wallet = await para.login();
// User is now logged in
// You can access their wallet address
final address = wallet.address;
}
```
You can also check if a user is already logged in:
```dart
Future checkLoginStatus() async {
final isLoggedIn = await para.isFullyLoggedIn();
if (isLoggedIn) {
// User is logged in, get their wallets
final wallets = await para.getWallets();
// Access the first wallet
if (wallets.isNotEmpty) {
final wallet = wallets.values.first;
final address = wallet.address;
}
}
}
```
### Create a User with Phone
Follow these steps to register a new user with phone verification:
```dart
Future registerUserByPhone(String phone, String countryCode) async {
// Check if user exists
if (await para.checkIfUserExistsByPhone(phone, countryCode)) {
// User exists, handle login flow
return;
}
// Create a new user account - this sends an SMS verification code
await para.createUserByPhone(phone, countryCode);
// Next step: Verify the phone with the code
}
Future verifyPhoneAndCreateWallet(String phone, String countryCode, String verificationCode) async {
// Verify the code to get biometricsId
final biometricsId = await para.verifyPhone(verificationCode);
// Generate a passkey for this device
// The full phone number format should be: +{countryCode}{phone}
final fullPhoneNumber = '+$countryCode$phone';
await para.generatePasskey(fullPhoneNumber, biometricsId);
// Create a wallet for the user
final result = await para.createWallet(skipDistribute: false);
// Store the recovery share securely
final recoveryShare = result.recoveryShare;
}
```
Phone numbers need to be in the format `+1234567890` with the country code included. For country code ensure a + is included.
### Login with Phone
To authenticate an existing user with their phone number:
```dart
Future loginUserByPhone() async {
// This triggers the platform's native passkey selection UI
final wallet = await para.login();
// User is now logged in
// You can access their wallet address
final address = wallet.address;
}
```
### Resend Verification Code
If the user doesn't receive the verification code:
```dart
Future resendVerificationCode() async {
await para.resendVerificationCodeByPhone();
}
```
When `createWallet` is called with `skipDistribute: false`, Para automatically handles the distribution of backup
shares. Make sure to securely store and display the recovery secret to the user in your app as it's essential for account recovery.
## Working with Wallets
Once a user is authenticated, you can access and manage their wallets:
```dart
// Get all wallets for the current user
Future getUserWallets() async {
try {
final wallets = await para.getWallets();
// Access wallet information
for (final wallet in wallets) {
print('Wallet type: ${wallet.type}');
print('Wallet address: ${wallet.address}');
}
} catch (e) {
// Handle errors
}
}
```
## Examples
For practical implementations of the Para SDK in Flutter environments, check out our GitHub repository:
## Troubleshooting
If you encounter issues during the integration or usage of the Para SDK in your Flutter application, here are some common
problems and their solutions:
If you're having trouble initializing the Para SDK:
* Ensure that you're using the correct API key and environment.
* Check that all necessary dependencies are installed properly.
* Look for any Dart errors in your Flutter debug console.
* Verify that your Flutter version is compatible with the Para SDK.
If passkey creation, retrieval, or usage isn't working:
* Verify that you've set up associated domains correctly in your iOS project.
* For Android, check that you've configured your `build.gradle` file with the namespace fix.
* Make sure you've provided the correct SHA-256 fingerprint to the Para team for Android.
* Ensure that biometric authentication is enabled on the test device.
* For Android, confirm the test device has a Google account signed in.
If you're experiencing authentication issues:
* Double-check that your API key is correct and properly set in your Para client initialization.
* Verify you're using the correct environment (`beta` or `prod`) that matches your API key.
* Ensure your account has the necessary permissions for the operations you're attempting.
* Check for any network-related issues that might be blocking API requests.
If you're encountering platform-specific problems:
* For iOS, ensure your team ID and bundle ID are correctly registered with Para.
* For Android, confirm your package name and SHA-256 fingerprint are properly registered.
* Check that you're running on a physical device or emulator that supports biometric authentication.
* Verify that your Flutter plugins are compatible with your Flutter SDK version.
## Next Steps
After integrating Para into your Flutter app, you can explore other features and integrations to enhance your Para experience.
# Account Abstraction
Source: https://docs.getpara.com/general/account-abstraction
Explore account abstraction integration options with Para across different platforms
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
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/general/checklist
A checklist to help you go live with the Para SDK.
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Before going live, ensure you've completed the following steps:
* [x] 🔑 Request Access to the 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)
* [ ] 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
* `PRODUCTION` **requires a paid plan**
* This is the correct environment to use for release or production builds
* `PRODUCTION` users can NOT be deleted, and have no per-project limit
# Migrating from Capsule to Para
Source: https://docs.getpara.com/general/migration-from-capsule
Guide for migrating from @usecapsule/* packages to @getpara/* packages
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
## 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
{
"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
npm install @getpara/[package-name]
```
```bash yarn
yarn add @getpara/[package-name]
```
```bash pnpm
pnpm add @getpara/[package-name]
```
## Mobile SDK Updates
### Flutter
The Flutter package has moved from `capsule` to `para` on pub.dev:
```diff
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
- 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
// Old
createUser(email: string)
// New
createUser({ email: string })
```
```typescript
// Old
createUserByPhone(phone: string, countryCode: string)
// New
createUserByPhone({ phone: string, countryCode: string })
```
```typescript
// Old
externalWalletLogin(address: string, type: string, provider?: string, addressBech32?: string)
// New
externalWalletLogin({
address: string,
type: string,
provider?: string,
addressBech32?: string
})
```
```typescript
// Old
createWallet(type: WalletType, skipDistribute?: boolean)
// New
createWallet({
type: WalletType,
skipDistribute?: boolean = false
})
```
```typescript
// 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
// Old
distributeNewWalletShare(
walletId: string,
userShare?: string,
skipBiometricShareCreation?: boolean,
forceRefreshRecovery?: boolean
)
// New
distributeNewWalletShare({
walletId: string,
userShare?: string,
skipBiometricShareCreation?: boolean = false,
forceRefresh?: boolean = false
})
```
```typescript
// Old
createWalletPreGen(
type: WalletType,
pregenIdentifier: string,
pregenIdentifierType?: PregenIdentifierType
)
// New
createPregenWallet({
type: WalletType,
pregenIdentifier: string,
pregenIdentifierType?: PregenIdentifierType
})
```
```typescript
// Old - Note: Function name changed
updateWalletIdentifierPreGen(
newIdentifier: string,
walletId: string,
newType?: PregenIdentifierType
)
// New
updatePregenWalletIdentifier({
walletId: string,
newPregenIdentifier: string,
newPregenIdentifierType?: PregenIdentifierType
})
```
```typescript
// Old
signMessage(
walletId: string,
messageBase64: string,
timeoutMs?: number,
cosmosSignDocBase64?: string
)
// New
signMessage({
walletId: string,
messageBase64: string,
timeoutMs?: number,
cosmosSignDocBase64?: string
})
```
```typescript
// 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
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
# Wallet Pregeneration
Source: https://docs.getpara.com/general/pregen
Overview of generating pregenerated wallets for Para
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
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.
# Telegram Bots & Mini-Apps
Source: https://docs.getpara.com/general/telegram
A user-friendly guide to quickly integrate Telegram Apps
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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/general/troubleshooting
Something not working quite right? This is the section for you!
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
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 to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the 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 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/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
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
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
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
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 Media Resources
Source: https://docs.getpara.com/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
VIDEO
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.
VIDEO
Nitya Subramanian delves into various approaches key management and trust in blockchain transactions, both on-chain and off-chain at ETHDenver 2024.
VIDEO
Learn about different methods for achieving chain abstraction in blockchain technology, presented by Nitya Subramanian at ETHCC 2024.
VIDEO
# Examples Hub
Source: https://docs.getpara.com/introduction/examples
Explore Para's examples hub for integration patterns across web, mobile, and specific use cases
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
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/introduction/framework-support
Supported Features Across Para's Frameworks
export const FeatureGrid = ({instanceId = `feature-grid-web`}) => {
if (typeof document === "undefined") return null;
const initializeFeatureGrid = () => {
const platforms = [{
id: "react-web",
name: "React/Web"
}, {
id: "node-server",
name: "Node/Server"
}, {
id: "react-native",
name: "React Native"
}, {
id: "swift",
name: "Swift"
}, {
id: "flutter",
name: "Flutter"
}, {
id: "android",
name: "Android (Coming Soon!)"
}];
const footnotes = [{
id: "1",
text: "Flutter SDK currently supports MetaMask and Phantom Wallet Auth. Swift SDK currently supports MetaMask Wallet Auth."
}, {
id: "2",
text: "While it's possible to launch the para-wrapped provider UIs with Native Mobile experiences, we have observed that onramp experiences are much smoother via direct integrations."
}];
const createStatus = (status, footnoteId = null) => {
const base = {
status
};
if (footnoteId) {
base.footnoteId = footnoteId;
}
return base;
};
const featureSections = [{
id: "signing",
title: "Signing",
features: [{
id: "signing-evm",
name: "EVM",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "signing-solana",
name: "Solana",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "signing-cosmos",
name: "Cosmos",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("coming-soon"),
"android": createStatus("not-supported")
}
}, {
id: "signing-server-side",
name: "Server-Side",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}]
}, {
id: "wallet-types",
title: "Wallet Types",
features: [{
id: "wallet-embedded",
name: "Embedded",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "wallet-external",
name: "External Wallets",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("not-supported"),
"react-native": createStatus("supported"),
"swift": createStatus("footnote", "1"),
"flutter": createStatus("footnote", "1"),
"android": createStatus("not-supported")
}
}, {
id: "wallet-pregen",
name: "Pregen Wallets",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "wallet-smart-accounts",
name: "Smart Accounts",
subitems: [{
id: "wallet-smart-accounts-evm",
name: "EVM",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "wallet-smart-accounts-solana",
name: "Solana",
platforms: {
"react-web": createStatus("coming-soon"),
"node-server": createStatus("coming-soon"),
"react-native": createStatus("coming-soon"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("coming-soon"),
"android": createStatus("not-supported")
}
}],
platforms: {
"react-web": null,
"node-server": null,
"react-native": null,
"swift": null,
"flutter": null,
"android": null
}
}, {
id: "wallet-gas-sponsorship",
name: "Gas Sponsorship",
subitems: [{
id: "wallet-gas-sponsorship-evm",
name: "EVM",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "wallet-gas-sponsorship-solana",
name: "Solana",
platforms: {
"react-web": createStatus("coming-soon"),
"node-server": createStatus("coming-soon"),
"react-native": createStatus("coming-soon"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("coming-soon"),
"android": createStatus("not-supported")
}
}],
platforms: {
"react-web": null,
"node-server": null,
"react-native": null,
"swift": null,
"flutter": null,
"android": null
}
}]
}, {
id: "authentication",
title: "Authentication",
features: [{
id: "auth-email-phone",
name: "Email + Phone",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("not-supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "auth-social",
name: "Social Logins",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("not-supported"),
"react-native": createStatus("supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "auth-passkeys",
name: "Passkeys",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("not-supported"),
"swift": createStatus("supported"),
"flutter": createStatus("supported"),
"android": createStatus("not-supported")
}
}, {
id: "auth-password",
name: "Password",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("coming-soon"),
"react-native": createStatus("not-supported"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("coming-soon"),
"android": createStatus("not-supported")
}
}]
}, {
id: "on-off-ramps",
title: "On/Off Ramps",
features: [{
id: "ramps-stripe",
name: "Stripe",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("not-supported"),
"react-native": createStatus("footnote", "2"),
"swift": createStatus("footnote", "2"),
"flutter": createStatus("footnote", "2"),
"android": createStatus("not-supported")
}
}, {
id: "ramps-ramp",
name: "Ramp",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("not-supported"),
"react-native": createStatus("footnote", "2"),
"swift": createStatus("footnote", "2"),
"flutter": createStatus("footnote", "2"),
"android": createStatus("not-supported")
}
}, {
id: "ramps-moonpay",
name: "Moonpay",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("not-supported"),
"react-native": createStatus("footnote", "2"),
"swift": createStatus("footnote", "2"),
"flutter": createStatus("footnote", "2"),
"android": createStatus("not-supported")
}
}]
}, {
id: "permissions",
title: "Permissions",
features: [{
id: "permissions-main",
name: "Permissions",
subitems: [{
id: "permissions-evm",
name: "EVM",
platforms: {
"react-web": createStatus("supported"),
"node-server": createStatus("supported"),
"react-native": createStatus("supported"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("coming-soon"),
"android": createStatus("not-supported")
}
}, {
id: "permissions-solana",
name: "Solana",
platforms: {
"react-web": createStatus("coming-soon"),
"node-server": createStatus("coming-soon"),
"react-native": createStatus("coming-soon"),
"swift": createStatus("coming-soon"),
"flutter": createStatus("coming-soon"),
"android": createStatus("not-supported")
}
}],
platforms: {
"react-web": null,
"node-server": null,
"react-native": null,
"swift": null,
"flutter": null,
"android": null
}
}]
}];
const icons = {
check: ` `,
minus: ` `,
clock: ` `,
chevronRight: ` `,
chevronDown: ` `
};
const styles = `
:root {
--background: #ffffff;
--foreground: #09090b;
--card: #ffffff;
--card-foreground: #09090b;
--popover: #ffffff;
--popover-foreground: #09090b;
--primary: #09090b;
--primary-foreground: #fafafa;
--secondary: #f4f4f5;
--secondary-foreground: #09090b;
--muted: #f4f4f5;
--muted-foreground: #71717a;
--accent: #f4f4f5;
--accent-foreground: #09090b;
--destructive: #ef4444;
--destructive-foreground: #fafafa;
--border: #e4e4e7;
--input: #e4e4e7;
--ring: #09090b;
--radius: 0.5rem;
--green-100: #dcfce7;
--green-600: #16a34a;
--amber-100: #fef3c7;
--amber-600: #d97706;
--blue-100: #dbeafe;
--blue-600: #2563eb;
--muted-hsl: 240, 4.8%, 95.9%;
}
.${instanceId}-container body {
font-family: "PPMORI";
background-color: var(--background);
color: var(--foreground);
margin: 0;
padding: 0;
box-sizing: border-box;
}
.${instanceId}-container *, .${instanceId}-container *::before, .${instanceId}-container *::after {
box-sizing: inherit;
}
.${instanceId}-container .feature-grid-container {
max-width: 1100px;
margin-left: auto;
margin-right: auto;
}
.${instanceId}-container .feature-card {
}
.${instanceId}-container .feature-card-header {
padding: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border);
}
.${instanceId}-container .feature-card-title {
font-size: 1.5rem;
font-weight: 600;
line-height: 2rem;
letter-spacing: -0.025em;
margin-bottom: 0.25rem;
}
.${instanceId}-container .feature-card-description {
color: var(--muted-foreground);
font-size: 0.875rem;
}
.${instanceId}-container .feature-card-content {
}
.${instanceId}-container .legend {
margin-bottom: 1rem;
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
}
.${instanceId}-container .legend-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.${instanceId}-container .legend-icon {
height: 1.5rem;
width: 1.5rem;
border-radius: 9999px;
display: flex;
align-items: center;
justify-content: center;
}
.${instanceId}-container .legend-text {
font-size: 0.875rem;
color: var(--foreground);
}
.${instanceId}-container .status-icon-base {
height: 1.75rem;
width: 1.75rem;
border-radius: 9999px;
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
}
.${instanceId}-container .status-supported { background-color: var(--green-100); color: var(--green-600); }
.${instanceId}-container .status-not-supported { background-color: var(--muted); color: var(--muted-foreground); }
.${instanceId}-container .status-coming-soon { background-color: var(--amber-100); color: var(--amber-600); }
.${instanceId}-container .status-footnote {
background-color: var(--blue-100);
color: var(--blue-600);
cursor: help;
}
.${instanceId}-container .status-footnote span { font-size: 0.75rem; font-weight: 700; }
.${instanceId}-container .section {
margin-bottom: 1rem;
}
.${instanceId}-container .section:last-child {
margin-bottom: 0;
}
.${instanceId}-container .section-title {
font-size: 1.125rem;feat
font-weight: 600;
margin-bottom: 0.75rem;
}
.${instanceId}-container .separator {
border: 0;
border-top: 1px solid var(--border);
margin-top: 0.5rem;
margin-bottom: 0.5rem;
}
.${instanceId}-container .table-container {
overflow-x: auto;
}
.${instanceId}-container .feature-table {
width: 100%;
border-collapse: collapse;
caption-side: bottom;
font-size: 0.875rem;
margin-bottom: 0.5rem;
margin-top: 0.5rem;
}
.${instanceId}-container .feature-table th,
.${instanceId}-container .feature-table td {
padding: 0.75rem 0.5rem;
text-align: left;
vertical-align: middle;
border-bottom: 1px solid var(--border);
}
.${instanceId}-container .feature-table tr:last-child td {
border-bottom: none;
}
.${instanceId}-container .feature-table th {
font-weight: 500;
color: var(--muted-foreground);
text-transform: none;
white-space: nowrap;
}
.${instanceId}-container .feature-table th.platform-head {
width: 100px;
text-align: center;
white-space: normal;
}
.${instanceId}-container .feature-table th.feature-head { width: 250px; }
.${instanceId}-container .feature-table tbody tr.expandable-row {
cursor: pointer;
}
.${instanceId}-container .feature-table tbody tr.expandable-row:hover {
background-color: hsla(var(--muted-hsl, 240, 4.8%, 95.9%), 0.5);
}
.${instanceId}-container .feature-table tbody tr.subitem-row {
background-color: hsla(var(--muted-hsl, 240, 4.8%, 95.9%), 0.2);
}
.${instanceId}-container .feature-table td {
color: var(--foreground);
}
.${instanceId}-container .feature-table td.feature-cell {
font-weight: 500;
}
.${instanceId}-container .feature-cell-content {
display: flex;
align-items: center;
gap: 0.5rem;
}
.${instanceId}-container .subitem-cell-content {
display: flex;
align-items: center;
gap: 0.5rem;
padding-left: 1.5rem;
}
.${instanceId}-container .expand-icon {
margin-right: 0.5rem;
display: inline-flex;
align-items: center;
color: var(--muted-foreground);
flex-shrink: 0;
}
.${instanceId}-container .platform-cell {
text-align: center;
}
.${instanceId}-container .feature-card-footer {
padding: 1rem 1.5rem;
border-top: 1px solid var(--border);
display: flex;
flex-direction: column;
align-items: flex-start;
}
.${instanceId}-container .request-feature-button {
display: inline-block;
background-color: var(--background);
color: var(--foreground);
border: 1px solid var(--input);
padding: 0.375rem 0.75rem;
font-size: 0.875rem;
border-radius: calc(var(--radius) - 2px);
font-weight: 500;
cursor: pointer;
margin-bottom: 1rem;
transition: background-color 0.2s, color 0.2s;
text-decoration: none;
}
.${instanceId}-container .request-feature-button:hover {
background-color: var(--accent);
color: var(--accent-foreground);
}
.${instanceId}-container .footnotes-container {
width: 100%;
}
.${instanceId}-container .footnotes-title {
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 0.5rem;
}
.${instanceId}-container .footnotes-list {
font-size: 0.75rem;
color: var(--muted-foreground);
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.${instanceId}-container .footnotes-list li span {
font-weight: 700;
margin-right: 0.25rem;
}
`;
function createElement(tag, classNames = [], attributes = {}, children = []) {
const element = document.createElement(tag);
if (classNames.length > 0) {
element.classList.add(...classNames);
}
for (const key in attributes) {
element.setAttribute(key, attributes[key]);
}
children.forEach(child => {
if (typeof child === 'string') {
element.appendChild(document.createTextNode(child));
} else if (child instanceof Node) {
element.appendChild(child);
} else if (typeof child === 'object' && child !== null && typeof child.outerHTML === 'string') {
element.innerHTML += child.outerHTML;
}
});
return element;
}
function createIconElement(iconSvg) {
const div = document.createElement('div');
if (typeof iconSvg === 'string') {
div.innerHTML = iconSvg.trim();
const svgElement = div.firstElementChild;
if (svgElement) {
svgElement.style.display = 'inline-block';
svgElement.style.verticalAlign = 'middle';
return svgElement;
}
}
return null;
}
function renderPlatformStatus(platformStatus) {
const wrapper = createElement('div', ['platform-cell']);
let statusElement = null;
if (!platformStatus) {
statusElement = createElement('div');
wrapper.appendChild(statusElement);
return wrapper;
}
switch (platformStatus.status) {
case "supported":
statusElement = createElement('div', ['status-icon-base', 'status-supported']);
statusElement.appendChild(createIconElement(icons.check));
break;
case "not-supported":
statusElement = createElement('div', ['status-icon-base', 'status-not-supported']);
statusElement.appendChild(createIconElement(icons.minus));
break;
case "coming-soon":
statusElement = createElement('div', ['status-icon-base', 'status-coming-soon']);
statusElement.appendChild(createIconElement(icons.clock));
break;
case "footnote":
statusElement = createElement('div', ['status-icon-base', 'status-footnote']);
const span = createElement('span', [], {}, [platformStatus.footnoteId || '?']);
statusElement.appendChild(span);
break;
default:
statusElement = createElement('div');
}
if (statusElement) {
wrapper.appendChild(statusElement);
}
return wrapper;
}
function createFeatureRow(feature, isSubitem = false) {
const uniqueFeatureId = `${instanceId}-${feature.id}`;
const row = createElement('tr', isSubitem ? ['subitem-row'] : []);
row.dataset.featureId = uniqueFeatureId;
const hasSubitems = feature.subitems && feature.subitems.length > 0;
if (hasSubitems) {
row.classList.add('expandable-row');
}
const featureCell = createElement('td', ['feature-cell']);
const featureCellContent = createElement('div', isSubitem ? ['subitem-cell-content'] : ['feature-cell-content']);
if (hasSubitems) {
const expandIconWrapper = createElement('span', ['expand-icon']);
expandIconWrapper.innerHTML = icons.chevronDown;
featureCellContent.appendChild(expandIconWrapper);
row.dataset.expanded = 'true';
row.dataset.subitemsTarget = `subitems-${uniqueFeatureId}`;
}
featureCellContent.appendChild(document.createTextNode(feature.name));
featureCell.appendChild(featureCellContent);
row.appendChild(featureCell);
platforms.forEach(platform => {
const platformCell = createElement('td', ['platform-cell']);
const status = feature.platforms ? feature.platforms[platform.id] : null;
platformCell.appendChild(renderPlatformStatus(status));
row.appendChild(platformCell);
});
return row;
}
function buildFeatureGrid() {
const container = createElement('div', ['feature-grid-container']);
const card = createElement('div', ['feature-card']);
const cardContent = createElement('div', ['feature-card-content']);
const legend = createElement('div', ['legend']);
const legendItems = [{
status: 'supported',
text: 'Supported',
icon: icons.check,
bg: '--green-100',
fg: '--green-600'
}, {
status: 'not-supported',
text: 'N/A',
icon: icons.minus,
bg: '--muted',
fg: '--muted-foreground'
}, {
status: 'coming-soon',
text: 'Coming Soon',
icon: icons.clock,
bg: '--amber-100',
fg: '--amber-600'
}, {
status: 'footnote',
text: 'See Footnote',
icon: '1',
bg: '--blue-100',
fg: '--blue-600',
isFootnote: true
}];
legendItems.forEach(item => {
const legendItem = createElement('div', ['legend-item']);
const iconDiv = createElement('div', ['legend-icon']);
iconDiv.style.backgroundColor = `var(${item.bg})`;
iconDiv.style.color = `var(${item.fg})`;
if (item.isFootnote) {
const span = createElement('span', [], {}, [item.icon]);
span.style.fontSize = '0.75rem';
span.style.fontWeight = '700';
iconDiv.appendChild(span);
} else {
const iconElement = createIconElement(item.icon);
if (iconElement) iconDiv.appendChild(iconElement);
}
legendItem.appendChild(iconDiv);
legendItem.appendChild(createElement('span', ['legend-text'], {}, [item.text]));
legend.appendChild(legendItem);
});
cardContent.appendChild(legend);
featureSections.forEach(section => {
const sectionDiv = createElement('div', ['section']);
sectionDiv.appendChild(createElement('h2', ['section-title'], {}, [section.title]));
sectionDiv.appendChild(createElement('hr', ['separator']));
const tableContainer = createElement('div', ['table-container']);
const table = createElement('table', ['feature-table']);
const thead = createElement('thead');
const headerRow = createElement('tr');
headerRow.appendChild(createElement('th', ['feature-head'], {}, ["Feature"]));
platforms.forEach(platform => {
const th = createElement('th', ['platform-head']);
th.innerHTML = platform.name;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
const tbody = createElement('tbody');
section.features.forEach(feature => {
const featureRow = createFeatureRow(feature, false);
tbody.appendChild(featureRow);
if (feature.subitems && feature.subitems.length > 0) {
const uniqueFeatureId = `${instanceId}-${feature.id}`;
const subRows = feature.subitems.map(subitem => createFeatureRow(subitem, true));
subRows.forEach(subRow => {
subRow.classList.add(`subitems-${uniqueFeatureId}`);
tbody.appendChild(subRow);
});
}
});
table.appendChild(tbody);
tableContainer.appendChild(table);
sectionDiv.appendChild(tableContainer);
cardContent.appendChild(sectionDiv);
});
card.appendChild(cardContent);
const cardFooter = createElement('div', ['feature-card-footer']);
const mailtoLink = createElement('a', ['request-feature-button'], {
href: "mailto:support@getpara.com?subject=[DEV SUPPORT]",
target: "_blank",
rel: "noopener noreferrer"
});
mailtoLink.textContent = "Ask Us / Request a Feature";
cardFooter.appendChild(mailtoLink);
const footnotesContainer = createElement('div', ['footnotes-container']);
footnotesContainer.appendChild(createElement('h3', ['footnotes-title'], {}, ["Footnotes:"]));
const footnotesList = createElement('ul', ['footnotes-list']);
footnotes.forEach(note => {
const listItem = createElement('li');
const boldPart = createElement('span', [], {}, [`${note.id}.`]);
listItem.appendChild(boldPart);
listItem.appendChild(document.createTextNode(` ${note.text}`));
footnotesList.appendChild(listItem);
});
footnotesContainer.appendChild(footnotesList);
cardFooter.appendChild(footnotesContainer);
card.appendChild(cardFooter);
container.appendChild(card);
container.addEventListener('click', event => {
const expandableRow = event.target.closest('.expandable-row');
if (expandableRow) {
const targetClass = expandableRow.dataset.subitemsTarget;
if (!targetClass) return;
const isExpanded = expandableRow.dataset.expanded === 'true';
const subitemRows = container.querySelectorAll(`tr.${targetClass}`);
const icon = expandableRow.querySelector('.expand-icon');
subitemRows.forEach(row => {
row.style.display = isExpanded ? 'none' : '';
});
expandableRow.dataset.expanded = !isExpanded;
if (icon) {
icon.innerHTML = isExpanded ? icons.chevronRight : icons.chevronDown;
}
}
});
return container;
}
if (!document.getElementById('feature-grid-styles')) {
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.id = 'feature-grid-styles';
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
const wrapperElement = document.getElementById(instanceId);
if (wrapperElement) {
const featureGridElement = buildFeatureGrid();
while (wrapperElement.firstChild) {
wrapperElement.removeChild(wrapperElement.firstChild);
}
wrapperElement.appendChild(featureGridElement);
wrapperElement.classList.add(`${instanceId}-container`);
} else {
console.error(`FeatureGrid: Could not find wrapper element with ID ${instanceId}`);
}
};
setTimeout(initializeFeatureGrid, 1);
return
;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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!
# Migrate to Para
Source: https://docs.getpara.com/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/main/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/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!
export const HelpSection = () => {
return
🤝 Need Help?
If you have any questions, we're here to help! You can get in touch in
the following ways:
;
};
export const IconGridItem = ({src, label, href, disabled = false, disabledLabel = "Talk to us!"}) => {
return ;
};
export const ConnectorsGrid = () => {
return ;
};
export const FrameworksGrid = () => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Capsule is now Para!
Same mission, new name. We're excited to continue supporting amazing onchain experiences.
## What is Para?
is the easiest and most secure way to onboard all your users and support them throughout their crypto journey. We
support projects throughout their growth, ranging from personal projects to many of the most trusted teams in crypto and
beyond.
Para's cross-app embedded wallets work universally across apps, chains, and ecosystem, so whether users start
transacting on EVM, Solana, or Cosmos, they can **onboard once and transact forever, all with the same wallet**.
## Getting Started
Kickstart Your Integration
Manage your integration seamlessly with the Modal Designer and Developer Portal. Get access with just a few
clicks.
If you'd like to see Para in action with your brand, try our .
Ready to get started? Head over to our to create your account. The portal is self-serve, allowing you to begin your integration journey immediately.
Once your account is set up, explore the resources below to begin integrating Para with your platform.
## Adding Para to Your App
New: Alpha Release
Check out the New Alpha Release for the latest features, streamlined integrations, and more! You can still use 1.x in the meantime if you prefer the most stable build
Para's SDKs support a wide variety of frameworks across Web, Native Mobile, and more. Pick your framework(s) and follow the Quickstart to create your first Para User.
Don't see the framework you're building on? Get in touch! We're always broadening our framework support and often have SDKs not listed here in alpha.
Just looking to add MPC wallets to your existing app's auth and user models? You can reserve wallets for users and progressively onboard them to web3 with
Para supports any EVM, Solana, or Cosmos chain and is compatible with popular signing libraries. We also have adapters for many popular wallet connector libraries if you prefer to keep the one you're using and just add Para cross-app embedded wallets as an add-on
Not sure whether we support your ecosystem? Ask us!
Once you're set up, there are many add-ons to make the integration look and work just right for your product.
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
External and Embedded Wallets
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Social Logins
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Phone Number Auth
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Passkey & Password Protection
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Set up 2FA
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Manage Emails
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Configure Popups
{
e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)";
}}
onMouseOut={(e) => {
e.currentTarget.style.background = "#e5e7eb";
}}
>
Modal Customization and Theming
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Integrations for Fiat On & Off Ramps
{e.currentTarget.style.background = "linear-gradient(45deg, #FFB4A1, #E5B1FF)"}} onMouseOut={(e) => {e.currentTarget.style.background = "#e5e7eb"}}>
Account Abstraction
# React Native & Expo SDK
Source: https://docs.getpara.com/react-native/api/sdk
Documentation for the Para SDK in React Native and Expo applications
## ParaMobile Class
The `ParaMobile` class represents a mobile implementation of the Para SDK, extending the `CorePara` class.
### Methods
Creates an instance of ParaMobile.
The environment to use (BETA or PROD).
The API key for authentication.
The relying party ID for WebAuthn.
Additional constructor options.
Verifies an email and returns the biometrics ID.
The verification code sent to the email.
The biometrics ID.
Verifies a phone number and returns the biometrics ID.
The verification code sent to the phone.
The biometrics ID.
Registers a passkey for the user.
The user's email or phone number.
The biometrics ID obtained from verification.
The Web Crypto API instance.
The type of identifier used.
The country calling code for phone numbers.
A promise that resolves when the passkey is registered.
Logs in the user using either email or phone number.
The user's email address.
The user's phone number.
The country calling code for phone numbers.
An array of user wallets.
If neither email nor both phone and countryCode are provided.
# Mobile Examples
Source: https://docs.getpara.com/react-native/examples
Explore Para's mobile integration examples for React Native and Expo
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para offers a collection of mobile examples to help you integrate our technology into your React Native and Expo applications. These examples demonstrate minimal implementation requirements with clean, focused code that you can easily adapt to your specific application needs.
## Para Mobile Examples
Browse our mobile examples showcasing Para integration with React Native and Expo:
## 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.
# Cosmos Support
Source: https://docs.getpara.com/react-native/guides/cosmos
Using Cosmos libraries like CosmJS in React Native with Para's SDK
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para's React Native SDK supports Cosmos blockchain interactions through compatible libraries like CosmJS. After authenticating a user with native passkeys in your React Native or Expo application, all Cosmos-related operations function identically to our web SDKs.
## Implementation and References
Once a user has successfully authenticated with the Para React Native SDK using native passkeys, you can immediately sign Cosmos transactions and messages, interact with different Cosmos chains (including Cosmos Hub, Osmosis, and Juno), connect with IBC-enabled networks, and perform staking, delegation, and other operations. The CosmJS library and related Cosmos SDK tools work identically to their web implementations when used with Para's React Native SDK.
## Key Considerations for Mobile
When implementing Cosmos functionality in React Native applications:
* Initialize the Para SDK with your project ID before attempting any Cosmos operations
* Complete the authentication flow with native passkeys
* Consider mobile-specific UI patterns for transaction approvals
* Test on physical devices to ensure proper integration with native security features
While the integration steps are nearly identical to web implementations, mobile devices offer enhanced security through hardware-backed passkeys.
# Custom Storage with MMKV
Source: https://docs.getpara.com/react-native/guides/custom-storage
Configure Para client to use MMKV storage in React Native applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para's React Native SDK uses AsyncStorage and Keychain Storage by default. However, you can configure Para to use MMKV for improved performance. This guide shows you how to implement a custom storage solution using the MMKV library.
## Installation
First, install the MMKV package:
```bash
npm install react-native-mmkv
# or
yarn add react-native-mmkv
```
## Implementation
Create your MMKV storage instance and configure Para to use it:
```typescript
import { MMKV } from 'react-native-mmkv';
import { ParaMobile, Environment } from '@getpara/react-native-wallet';
// Initialize MMKV storage instances
const storage = new MMKV({
id: 'para-storage'
});
// Initialize Para client with MMKV storage
const para = new ParaMobile(
Environment.BETA,
"YOUR_API_KEY",
undefined,
{
// Custom storage overrides
localStorageGetItemOverride: async (key) => {
const value = storage.getString(key);
return value ?? null;
},
localStorageSetItemOverride: async (key, value) => {
storage.set(key, value);
},
sessionStorageGetItemOverride: async (key) => {
const value = storage.getString(key);
return value ?? null;
},
sessionStorageSetItemOverride: async (key, value) => {
storage.set(key, value);
},
sessionStorageRemoveItemOverride: async (key) => {
storage.delete(key);
},
clearStorageOverride: async () => {
storage.clearAll();
}
}
);
export { para };
```
The custom storage implementation must handle serialization and deserialization of JSON data. All values are stored as strings, so your implementation should handle converting values correctly.
# EVM Support
Source: https://docs.getpara.com/react-native/guides/evm
Using EVM libraries like Ethers.js and Viem in React Native with Para's SDK
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para's React Native SDK provides full support for EVM chains through popular libraries like Ethers.js and Viem. Once a user is authenticated using native passkeys in your React Native or Expo application, all EVM-related operations work the same way as in our web SDKs.
## Implementation and References
After authenticating with the Para React Native SDK using native passkeys, you can seamlessly sign transactions and messages, interact with smart contracts, connect to different EVM networks, and perform other EVM operations. Both Ethers.js and Viem libraries are fully compatible with Para's React Native SDK, working identically to their web counterparts.
## Key Considerations for Mobile
When implementing EVM functionality in React Native applications:
* Ensure you've properly initialized the Para SDK with your project ID
* Complete authentication with native passkeys before attempting any signing operations
* Consider mobile UI/UX design for transaction approval flows
* Test on actual devices to verify the signing experience
The underlying code for signing transactions and messages remains identical between web and mobile implementations.
# Password Authentication
Source: https://docs.getpara.com/react-native/guides/passwords
Authenticate users with passwords in React Native and Expo applications (Coming Soon)
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
**Coming Soon**: Password authentication for React Native and Expo applications is currently in development and will be available soon. Reach out to us at if you'd like to get early access.
Para will soon support password-based authentication for React Native and Expo applications, providing a familiar authentication experience for users while maintaining security for their wallets. This alternative to passkeys offers support for devices where passkey functionality may be limited, though passkeys are still recommended for a more streamlined experience as passwords can be forgotten.
## Get Early Access
Interested in implementing password authentication in your mobile application before the public release? Contact us to join our early access program:
# Wallet Pregeneration
Source: https://docs.getpara.com/react-native/guides/pregen
Create and manage pregenerated wallets for users in React Native and Expo applications
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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, React Native and Expo 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 a Pregenerated Wallet
### Check if a wallet exists
```typescript
import { para } from '../your-para-client';
async function checkPregenWallet() {
const hasWallet = await para.hasPregenWallet({
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
return hasWallet;
}
```
### Create a pregenerated wallet
```typescript
import { para } from '../your-para-client';
import { WalletType } from '@getpara/react-native-wallet';
async function createPregenWallet() {
const pregenWallet = await para.createPregenWallet({
type: WalletType.EVM,
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
console.log("Pregenerated Wallet ID:", pregenWallet.id);
return pregenWallet;
}
```
### Retrieve the user share
```typescript
import { para } from '../your-para-client';
async function getUserShare() {
const userShare = await para.getUserShare();
// Store this share securely
return userShare;
}
```
## Mobile Storage Options
In mobile applications, you have several options for securely storing the user share:
```typescript
import * as Keychain from 'react-native-keychain';
// Store the user share
async function storeUserShare(userShare) {
try {
await Keychain.setGenericPassword(
'para_user_share',
userShare,
{
service: 'com.yourapp.wallet',
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY
}
);
} catch (error) {
console.error("Error storing user share:", error);
}
}
// Retrieve the user share
async function retrieveUserShare() {
try {
const credentials = await Keychain.getGenericPassword({
service: 'com.yourapp.wallet'
});
if (credentials) {
return credentials.password;
}
return null;
} catch (error) {
console.error("Error retrieving user share:", error);
return null;
}
}
```
```typescript
import { MMKV } from 'react-native-mmkv';
// Initialize with encryption for better security
const storage = new MMKV({
id: 'wallet-storage',
encryptionKey: 'your-secure-encryption-key'
});
// Store the user share
function storeUserShare(userShare) {
storage.set('user_share', userShare);
}
// Retrieve the user share
function retrieveUserShare() {
return storage.getString('user_share');
}
```
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 Mobile Apps
Once you have created a pregenerated wallet and stored the user share, you can use it for signing operations:
```typescript
import { para } from '../your-para-client';
async function usePregenWallet() {
// Retrieve the user share from your secure storage
const userShare = await retrieveUserShare();
if (!userShare) {
console.error("User share not found");
return;
}
// Load the user share into the Para client
await para.setUserShare(userShare);
// Now you can perform signing operations
const messageBase64 = btoa("Hello, World!");
const signature = await para.signMessage({
walletId: "your-wallet-id",
messageBase64,
});
return signature;
}
```
### 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.
```typescript
import DeviceInfo from 'react-native-device-info';
async function createDeviceWallet() {
const deviceId = await DeviceInfo.getUniqueId();
const pregenWallet = await para.createPregenWallet({
type: WalletType.EVM,
pregenIdentifier: `device-${deviceId}`,
pregenIdentifierType: "CUSTOM_ID",
});
// Store the user share in device-specific secure storage
const userShare = await para.getUserShare();
await storeUserShare(userShare);
return pregenWallet;
}
```
For iOS App Clips or Android Instant Apps, create temporary wallets that enable limited blockchain functionality without requiring full app installation or user authentication.
```typescript
async function createTemporaryWallet() {
// Generate a random identifier for this session
const sessionId = Math.random().toString(36).substring(2, 15);
const pregenWallet = await para.createPregenWallet({
type: WalletType.EVM,
pregenIdentifier: `temp-${sessionId}`,
pregenIdentifierType: "CUSTOM_ID",
});
const userShare = await para.getUserShare();
// Store in memory for this session only
// (could also use temporary secure storage)
sessionStorage.userShare = userShare;
return pregenWallet;
}
```
Seamlessly introduce blockchain functionality to your existing app users without requiring them to understand wallets or crypto.
```typescript
async function createWalletForExistingUser(userId) {
// Check if we already created a wallet for this user
const hasWallet = await para.hasPregenWallet({
pregenIdentifier: `user-${userId}`,
pregenIdentifierType: "CUSTOM_ID",
});
if (!hasWallet) {
const pregenWallet = await para.createPregenWallet({
type: WalletType.EVM,
pregenIdentifier: `user-${userId}`,
pregenIdentifierType: "CUSTOM_ID",
});
const userShare = await para.getUserShare();
await storeUserShare(userShare);
return pregenWallet;
} else {
// Retrieve existing wallet info
const wallets = await para.getPregenWallets({
pregenIdentifier: `user-${userId}`,
pregenIdentifierType: "CUSTOM_ID",
});
return wallets[0];
}
}
```
## 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:
```typescript
import { para } from '../your-para-client';
async function claimWallet() {
// Ensure user is authenticated
if (!(await para.isFullyLoggedIn())) {
console.error("User must be authenticated to claim wallets");
return;
}
// Retrieve and load the user share
const userShare = await retrieveUserShare();
await para.setUserShare(userShare);
// Claim the wallet
const recoverySecret = await para.claimPregenWallets();
// 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
# React Native Session Management
Source: https://docs.getpara.com/react-native/guides/sessions
Guide to managing authentication sessions in Para for React Native applications
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para provides a comprehensive set of methods for managing authentication sessions in React Native 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.
```typescript
async isSessionActive(): Promise
```
In React Native 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:
```typescript
import { para } from '../your-para-client';
async function checkSession() {
try {
const isActive = await para.isSessionActive();
if (!isActive) {
// First clear any existing data
await para.logout();
// Navigate to login screen
navigation.navigate('Login');
} else {
// Session is valid, proceed with app flow
navigation.navigate('Dashboard');
}
} catch (error) {
console.error("Session check failed:", error);
// Handle error
}
}
```
### Maintaining Active Sessions
Para provides the `keepSessionAlive()` method to extend an active session without requiring full reauthentication.
```typescript
async keepSessionAlive(): Promise
```
Example usage:
```typescript
import { para } from '../your-para-client';
async function extendSession() {
try {
const success = await para.keepSessionAlive();
if (!success) {
// Session could not be extended
// Clear storage and navigate to login
await para.logout();
navigation.navigate('Login');
}
} catch (error) {
console.error("Session maintenance failed:", error);
}
}
```
### Refreshing Expired Sessions
When a session has expired, Para recommends initiating a full authentication flow rather than trying to refresh the session.
For React Native applications, always call `logout()` before reinitiating authentication when a session has expired to ensure all stored data is properly cleared.
```typescript
import { para } from '../your-para-client';
async function handleSessionExpiration() {
// When session expires, first clear storage
await para.logout();
// Then redirect to authentication screen
navigation.navigate('Login');
}
```
## 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.
```typescript
exportSession({ excludeSigners?: boolean }): string
```
If your server doesn't need to perform signing operations, use `{ excludeSigners: true }` when exporting sessions for enhanced security.
Example implementation:
```typescript
import { para } from '../your-para-client';
async function sendSessionToServer() {
// Export session without signing capabilities
const sessionData = para.exportSession({ excludeSigners: true });
// Send to your server
try {
const response = await fetch('https://your-api.com/sessions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ session: sessionData }),
});
if (!response.ok) {
throw new Error('Failed to send session to server');
}
return await response.json();
} catch (error) {
console.error('Error sending session to server:', error);
throw error;
}
}
```
## Best Practices for React Native
1. **Check Sessions on App Launch**: Verify session status when your app starts to determine if users need to reauthenticate.
```typescript
// In your app's entry point or navigation setup
useEffect(() => {
async function checkSessionOnLaunch() {
const isActive = await para.isSessionActive();
if (isActive) {
navigation.navigate('Dashboard');
} else {
await para.logout(); // Clear any lingering data
navigation.navigate('Login');
}
}
checkSessionOnLaunch();
}, []);
```
2. **Implement Automatic Session Extension**: For long app usage sessions, periodically call `keepSessionAlive()` to prevent unexpected session expirations.
```typescript
useEffect(() => {
const sessionInterval = setInterval(async () => {
try {
const isActive = await para.isSessionActive();
if (isActive) {
await para.keepSessionAlive();
} else {
// Session expired, handle accordingly
await para.logout();
navigation.navigate('Login');
}
} catch (error) {
console.error('Error maintaining session:', error);
}
}, 30 * 60 * 1000); // Check every 30 minutes
return () => clearInterval(sessionInterval);
}, []);
```
3. **Handle Background/Foreground State**: React Native apps can be backgrounded and foregrounded, which may affect session status.
```typescript
import { AppState } from 'react-native';
useEffect(() => {
const subscription = AppState.addEventListener('change', async (nextAppState) => {
if (nextAppState === 'active') {
// App came to foreground, check session
const isActive = await para.isSessionActive();
if (!isActive) {
await para.logout();
navigation.navigate('Login');
}
}
});
return () => {
subscription.remove();
};
}, []);
```
4. **Secure Storage Configuration**: For enhanced security, consider implementing a to manage sensitive session data.
## Next Steps:
Explore more advanced features and integrations with Para in Flutter:
# OAuth Authentication
Source: https://docs.getpara.com/react-native/guides/social-login
Implementing OAuth login with Para in React Native and Expo applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para supports OAuth authentication in React Native and Expo applications, allowing users to sign in using popular providers like Google, Discord, and Farcaster. This guide explains how to implement OAuth login in your mobile app using Para's React Native SDK.
The OAuth flow in mobile applications requires handling browser sessions and deeplinks to redirect users back to your app after successful authentication. Once the OAuth flow completes, Para uses the returned email to authenticate users through the standard email-based flow, creating or authenticating with a native passkey.
## Prerequisites
Before implementing OAuth authentication, ensure you have completed the basic Para setup for your React Native or Expo application.
## Implementation
### Installation
Install the In-App Browser package to handle OAuth redirects securely:
```bash
npm install react-native-inappbrowser-reborn
# or
yarn add react-native-inappbrowser-reborn
```
For iOS, add the following to your `Info.plist` to define your URL scheme:
```xml
CFBundleURLTypes
CFBundleURLSchemes
your-app-scheme
```
For Android, add your URL scheme to `AndroidManifest.xml`:
```xml
```
Both `react-native-inappbrowser-reborn` and `expo-web-browser` use secure browser implementations that leverage the device's native browser engine rather than a WebView. This provides stronger security protections, including shared security context with the device's browser, protection against common web vulnerabilities, and support for modern authentication methods.
### Implementing Authentication
```typescript Standard OAuth
import { para } from "../your-para-client";
import { OAuthMethod } from "@getpara/react-native-wallet";
import { InAppBrowser } from "react-native-inappbrowser-reborn";
const APP_SCHEME = "your-app-scheme";
async function handleOAuthLogin(provider: OAuthMethod) {
if (provider === OAuthMethod.FARCASTER) {
return handleFarcasterLogin();
}
const oauthUrl = await para.getOAuthURL({
method: provider,
deeplinkUrl: APP_SCHEME,
});
await InAppBrowser.openAuth(oauthUrl, APP_SCHEME, {
ephemeralWebSession: false,
showTitle: false,
enableUrlBarHiding: true,
});
const { email, userExists } = await para.waitForOAuth();
if (userExists) {
await para.login({ email: email! });
// Navigate to your authenticated screen
} else {
const biometricId = await para.getSetUpBiometricsURL({
isForNewDevice: false,
authType: "email",
});
if (biometricId) {
await para.registerPasskey({ email: email!, biometricsId: biometricId });
// Navigate to your authenticated screen
}
}
}
```
```typescript Farcaster Login
import { Linking } from "react-native";
import { para } from "../your-para-client";
async function handleFarcasterLogin() {
const farcasterUrl = await para.getFarcasterConnectURL();
await Linking.openURL(farcasterUrl);
const { userExists, username } = await para.waitForFarcasterStatus();
if (userExists) {
await para.login({ email: username! });
// Navigate to your authenticated screen
} else {
const biometricId = await para.getSetUpBiometricsURL({
isForNewDevice: false,
authType: "email",
});
if (biometricId) {
await para.registerPasskey({ email: username!, biometricsId: biometricId });
// Navigate to your authenticated screen
}
}
}
```
### Installation
Install the Expo Web Browser package:
```bash
npx expo install expo-web-browser
```
Configure your app.json with the URL scheme:
```json
{
"expo": {
"scheme": "your-app-scheme",
"ios": {
"bundleIdentifier": "com.yourcompany.yourappname"
},
"android": {
"package": "com.yourcompany.yourappname"
}
}
}
```
After updating your app.json, run the following command to rebuild native iOS and Android files:
```bash
npx expo prebuild --clean
```
For Expo Router apps, create a `+native-intent.tsx` file in your project root to handle deeplinks:
```typescript
export function redirectSystemPath({ path, initial }: { path: string; initial: boolean }) {
if (path.includes("para?method=login") && !initial) {
return `/auth/your-oauth-screen-path`;
}
return path;
}
```
The browser packages used for OAuth share cookies, cache, and session data with the device's native browser. This means users already logged into OAuth providers in their device browser will stay logged in. If you encounter authentication issues, try clearing the cache and history in the device's native browser.
### Implementing Authentication
```typescript Standard OAuth
import { para } from "../your-para-client";
import { OAuthMethod } from "@getpara/react-native-wallet";
import { openAuthSessionAsync } from "expo-web-browser";
const APP_SCHEME = "your-app-scheme";
async function handleOAuthLogin(provider: OAuthMethod) {
if (provider === OAuthMethod.FARCASTER) {
return handleFarcasterLogin();
}
const oauthUrl = await para.getOAuthURL({
method: provider,
deeplinkUrl: APP_SCHEME,
});
await openAuthSessionAsync(oauthUrl, APP_SCHEME, {
preferEphemeralSession: false,
});
const { email, userExists } = await para.waitForOAuth();
if (userExists) {
await para.login({ email: email! });
// Navigate to your authenticated screen
} else {
const biometricId = await para.getSetUpBiometricsURL({
isForNewDevice: false,
authType: "email",
});
if (biometricId) {
await para.registerPasskey({ email: email!, biometricsId: biometricId });
// Navigate to your authenticated screen
}
}
}
```
```typescript Farcaster Login
import { openURL } from "expo-linking";
import { para } from "../your-para-client";
async function handleFarcasterLogin() {
const farcasterUrl = await para.getFarcasterConnectURL();
await openURL(farcasterUrl);
const { userExists, username } = await para.waitForFarcasterStatus();
if (userExists) {
await para.login({ email: username! });
// Navigate to your authenticated screen
} else {
const biometricId = await para.getSetUpBiometricsURL({
isForNewDevice: false,
authType: "email",
});
if (biometricId) {
await para.registerPasskey({ email: username!, biometricsId: biometricId });
// Navigate to your authenticated screen
}
}
}
```
OAuth authentication with Para still requires creating a native passkey to secure the user's wallets. After OAuth completes, Para associates a native passkey with the user's account. For returning users, the native passkey is used for authentication. The passkey is associated on a per-app basis, making authentication streamlined, and users will only see passkey options they created for your specific app.
## Examples
Explore our complete example implementations for OAuth authentication with Para:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Solana Support in React Native
Source: https://docs.getpara.com/react-native/guides/solana
Using Para with Solana Web3.js and Anchor in React Native applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para's React Native SDK provides comprehensive support for Solana blockchain operations through libraries like Solana Web3.js and Anchor. After authenticating users with native passkeys in your React Native or Expo application, all Solana-related operations work exactly the same as in our web SDKs.
## Implementation and References
Once authentication is complete with the Para React Native SDK using native passkeys, you can immediately sign Solana transactions and messages, interact with Solana programs, connect to different Solana networks (mainnet, devnet, testnet), and leverage Solana Pay and other ecosystem tools. Both Solana Web3.js and Anchor framework work seamlessly with Para's React Native SDK, functioning identically to their web implementations.
## Key Considerations for Mobile
When implementing Solana functionality in React Native applications:
* Ensure proper initialization of the Para SDK with your project ID
* Complete authentication with native passkeys before attempting any signing operations
* Design mobile-friendly UI for transaction approval flows
* Test transaction signing on actual devices to verify the end-to-end experience
The underlying code for Solana transaction construction and signing remains identical between web and mobile implementations, making it easy to maintain consistency across platforms.
# Overview
Source: https://docs.getpara.com/react-native/overview
An introduction to mobile integrations with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para supports modern mobile development frameworks, giving you the flexibility to implement our SDKs in your native mobile applications. Each integration guide provides step-by-step instructions tailored to specific mobile development environments.
## Getting Started with Mobile Integrations
## Blockchain Ecosystem Integrations
Para works seamlessly with major blockchain ecosystems in your mobile apps, allowing you to leverage Para's authentication alongside chain-specific libraries and development tools.
## Advanced Configuration
Customize Para's behavior and extend its functionality with these advanced configuration guides.
## Extended Authentication Options
Enhance your application's security and user experience with additional authentication methods.
# Expo
Source: https://docs.getpara.com/react-native/setup/expo
Learn how to integrate the Para SDK with Expo projects.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
is a framework built on React Native that provides many out-of-the-box features, similar to NextJS for web but designed
for mobile development. Para provides a `@getpara/react-native-wallet` package that works seamlessly in both React Native bare and Expo workflows, utilizing the device's Native Passkeys for secure wallet management.
## 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.
To ensure passkey functionality works correctly:
* Enable biometric or device unlock settings (fingerprint, face unlock, or PIN)
* On Android sign in to a Google account on the device (required for Google Play Services passkey management)
## Dependency Installation
Install the required dependencies:
```bash npm
npm install @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto
```
```bash yarn
yarn add @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto
```
```bash pnpm
pnpm add @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto
```
```bash bun
bun add @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto
```
## Project Setup
To use the `@getpara/react-native-wallet` package in your Expo project, you will need to do some initial project setup.
For passkeys to work correctly we have to setup the for both iOS and Android. This ensures passkeys are bound to the domains they were created for.
Your apps must be registered with Para to link the passkey to the correct domain. You can find this configuration options
your under the 'Configuration' tab of the API key label as Native Passkey Configuration.
### Platform-Specific Configuration
Configure your `app.json` file to enable passkey functionality and secure communication:
```json app.json
{
"expo": {
"ios": {
"bundleIdentifier": "your.app.bundleIdentifier",
"associatedDomains": [
"webcredentials:app.beta.usecapsule.com?mode=developer",
"webcredentials:app.usecapsule.com"
]
}
}
}
```
**Important**: Your `teamId + bundleIdentifier` must be registered with the Para team to set up associated domains. For example, if your Team ID is `A1B2C3D4E5` and Bundle Identifier is `com.yourdomain.yourapp`, provide `A1B2C3D4E5.com.yourdomain.yourapp` to Para. This is required by Apple for passkey security. Allow up to 24 hours for domain propagation. You can find this setting in the Developer Portal under the 'Configuration' tab of the API key label as Native Passkey Configuration.
For Android setup in Expo, you'll need to configure your package name and provide your SHA-256 certificate fingerprint.
**Quick Testing Option**: You can use `com.getpara.example.expo` as your package name in `app.json` for immediate testing. This package name is pre-registered but only works with the default debug.keystore generated by Expo.
Configure your `app.json`:
```json app.json
{
"expo": {
"android": {
"package": "com.getpara.example.expo" // For testing
// or your actual package name for production
}
}
}
```
**Important**: Your SHA-256 certificate fingerprint must be registered with the Para team to set up associated domains. This is required by Google for passkey security. Allow up to 24 hours for domain propagation. You can find this setting in the Developer Portal under the 'Configuration' tab of the API key label as Native Passkey Configuration.
### Configure Metro Bundler
Create or update `metro.config.js` in your project with the following node module resolutions. This will ensure that any library that depends on global modules like `crypto` or `buffer` will be properly resolved in the Expo environment.
```javascript metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const config = getDefaultConfig(__dirname);
config.resolver.extraNodeModules = {
crypto: require.resolve("react-native-quick-crypto"),
buffer: require.resolve("@craftzdog/react-native-buffer"),
};
module.exports = config;
```
### Import Required Shims
Import the Para Wallet shim in your root layout file to ensure proper global module shimming. This ensures that the necessary modules are available globally in your application. Ensure this is the very first import in your root layout file.
```typescript app/_layout.tsx
import "@getpara/react-native-wallet/dist/shim";
// ... rest of your imports and layout code
```
Alternatively, you can create a custom entry point to handle the shimming. This will ensure that the shim occurs before the Expo Router entry point.
Create `index.js` in your project root and add the following imports:
```javascript index.js
import "@getpara/react-native-wallet/dist/shim";
import "expo-router/entry";
```
Update `package.json` to point to your new entry file:
```json package.json
{
"main": "index.js"
}
```
### Prebuild and Run
Since native modules are required, you'll need to use Expo Development Build to ensure that linking is successful. This means using the `expo prebuild` command to generate the necessary native code and then run your app using `expo run:ios` or `expo run:android`.
```bash
npx expo prebuild
npx expo run:ios
npx expo run:android
```
You **cannot** use Expo Go as it doesn't support native module linking. When running via `yarn start`, switch to development mode by pressing `s`, then `i` for iOS or `a` for Android.
## Using the Para SDK
The `@getpara/react-native-wallet` provides two main authentication methods: email-based and phone-based. Both flows utilize Native Passkeys for secure and seamless authentication.
On mobile Para doesn't provide a modal component. Instead you can create your own auth screens with either email, phone
number, or oauth using the available methods in the Para SDK.
### Setup the Para Client
First, set up the Para client singleton and initialize it in your app:
```typescript para.ts
import { ParaMobile, Environment } from "@getpara/react-native-wallet";
export const para = new ParaMobile(Environment.BETA, YOUR_API_KEY);
```
Then initialize it in your app entry point:
```typescript app/_layout.tsx
import { para } from "../para";
import { useEffect } from "react";
export default function Layout() {
useEffect(() => {
const initPara = async () => {
await para.init();
};
initPara();
}, []);
// ... rest of your layout code
}
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
**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.
### Authentication Methods
#### Create a User with Email
Implement a user registration flow with email verification:
```typescript
const handleUserRegistration = async (email: string) => {
const userExists = await para.checkIfUserExists({ email });
if (userExists) {
await para.login({ email });
return true; // User logged in
}
await para.createUser({ email });
return false; // Verification needed
};
const handleVerification = async (email: string, verificationCode: string) => {
const biometricsId = await para.verifyEmailBiometricsId({ verificationCode });
if (biometricsId) {
await para.registerPasskey({ email, biometricsId });
return true;
}
return false;
};
// Usage example:
const needsVerification = await handleUserRegistration("user@example.com");
if (!needsVerification) {
await handleVerification("user@example.com", "123456");
}
```
#### Login with Email
Authenticate an existing user with their email:
```typescript
const handleLogin = async (email: string): Promise => {
try {
const userExists = await para.checkIfUserExists({ email });
if (userExists) {
await para.login({ email });
return true; // User logged in successfully
} else {
return false; // User does not exist
}
} catch (error) {
// Handle error
return false;
}
};
```
#### Create a Wallet After Authentication
After the user has successfully authenticated, you can create their wallet:
```typescript
const createWallet = async (): Promise => {
try {
const { recoverySecret } = await para.createWalletPerMissingType({ skipDistribute: false });
return recoverySecret;
} catch (error) {
// Handle error
return undefined;
}
};
```
Make sure to securely store the recovery secret returned from wallet creation. This is essential for account recovery.
#### Create a User with Phone Number
Implement a user registration flow with phone verification:
```typescript
const handlePhoneRegistration = async (phone: string, countryCode: string) => {
const userExists = await para.checkIfUserExistsByPhone({ phone, countryCode });
if (userExists) {
await para.login({ phone, countryCode });
return true; // User logged in
}
await para.createUserByPhone({ phone, countryCode });
return false; // Verification needed
};
const handlePhoneVerification = async (verificationCode: string, phone: string, countryCode: string) => {
const biometricsId = await para.verifyPhoneBiometricsId({ verificationCode });
if (biometricsId) {
await para.registerPasskey({ phone, countryCode, biometricsId });
return true;
}
return false;
};
// Resend verification code if needed
const resendPhoneVerificationCode = async () => {
await para.resendVerificationCode();
};
// Usage example:
const needsVerification = await handlePhoneRegistration("+1234567890", "US");
if (!needsVerification) {
await handlePhoneVerification("123456", "+1234567890", "US");
}
```
#### Login with Phone
Authenticate an existing user with their phone number:
```typescript
const handlePhoneLogin = async (phone: string, countryCode: string): Promise => {
try {
const userExists = await para.checkIfUserExistsByPhone({ phone, countryCode });
if (userExists) {
await para.login({ phone, countryCode });
return true; // User logged in successfully
} else {
return false; // User does not exist
}
} catch (error) {
// Handle error
return false;
}
};
```
#### Create a Wallet After Authentication
After the user has successfully authenticated, you can create their wallet:
```typescript
const createWallet = async (): Promise => {
try {
const { recoverySecret } = await para.createWalletPerMissingType({ skipDistribute: false });
return recoverySecret;
} catch (error) {
// Handle error
return undefined;
}
};
```
Make sure to securely store the recovery secret returned from wallet creation. This is essential for account recovery.
#### Create a User with OAuth
OAuth authentication flow is coming soon. Please contact Para support for more information and early access.
By following these steps, you can implement a secure and user-friendly authentication system in your Expo application using the Para SDK.
## Examples
For practical implementations of the Para SDK in Expo environments, check out our GitHub repository:
## Troubleshooting
If you encounter issues during the integration or usage of the Para SDK in your Expo application, here are some common
problems and their solutions:
If you're having trouble initializing the Para SDK:
* Ensure that you've called `para.init()` after creating the Para instance.
* Verify that you're using the correct API key and environment.
* Check that all necessary dependencies are installed and linked properly.
* Look for any JavaScript errors in your Expo bundler console.
If you're seeing errors about missing native modules:
* Ensure you've run `expo prebuild` to generate native code.
* Run `expo run:ios` and `expo run:android` to rebuild your app after adding new native dependencies.
* Verify that your `app.json` file includes the necessary configurations for native modules.
If passkey creation, retrieval, or usage isn't working:
* Verify that you've set up associated domains correctly in your `app.json` file for both iOS and Android.
* Check that you've provided the correct SHA-256 fingerprint to the Para team for Android.
* Ensure your Expo SDK version is compatible with the Para SDK.
If you're seeing errors related to crypto functions:
* Ensure you've properly set up the `expo-crypto` and `react-native-quick-crypto` polyfills.
* Verify that your `metro.config.js` file is configured correctly to include the necessary Node.js core modules.
* Check that you've imported the shim file at the top of your root `index.js` or `App.js` file.
* Make sure you're using the latest version of Expo SDK that's compatible with the Para SDK.
If you're experiencing authentication issues:
* Double-check that your API key is correct and properly set in your environment variables.
* Verify you're using the correct environment (`BETA` or `PRODUCTION`) that matches your API key.
* Ensure your account has the necessary permissions for the operations you're attempting.
* Check your network requests for any failed API calls and examine the error messages.
* If using Expo Go, remember that some native features might not work; consider using a development build or EAS.
If you're encountering problems during the Expo build process:
* Ensure you're using a compatible Expo SDK version with all your dependencies.
* Run `expo doctor` to check for any configuration issues in your project.
* If using managed workflow, consider switching to bare workflow for full native module support.
* For EAS builds, check your `eas.json` configuration and ensure it's set up correctly for both iOS and Android.
For a more comprehensive list of solutions, including Expo-specific issues, visit our troubleshooting guide:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Bare Workflow
Source: https://docs.getpara.com/react-native/setup/react-native
Learn how to integrate the Para SDK with React Native projects.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
The Para SDK for React Native allows you to easily integrate secure and scalable wallet functionalities into your mobile
applications, utilizing the device's Native Passkeys for secure wallet management. This guide covers the installation,
setup, and usage of the Para SDK.
## 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.
## Dependency Installation
Install the required packages for Para SDK integration:
```bash npm
npm install @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto readable-stream
```
```bash yarn
yarn add @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto readable-stream
```
```bash pnpm
pnpm add @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto readable-stream
```
```bash bun
bun add @getpara/react-native-wallet @react-native-async-storage/async-storage react-native-keychain react-native-modpow react-native-passkey react-native-quick-base64 @craftzdog/react-native-buffer react-native-quick-crypto readable-stream
```
## Project Setup
### Platform-Specific Configuration
In order for passkeys to work, you need to set up associated domains in your Xcode project linked to the Para domain.
#### Set Up Associated Domains
1. Open your project in Xcode
2. Select your target and go to "Signing & Capabilities"
3. Click "+ Capability" and add "Associated Domains"
4. Add the following domains:
* webcredentials:app.beta.usecapsule.com
* webcredentials:app.usecapsule.com
For additional information on associated domains, refer to the .
**Important**: Your `teamId + bundleIdentifier` must be registered with the Para team to set up associated domains. For example, if your Team ID is `A1B2C3D4E5` and Bundle Identifier is `com.yourdomain.yourapp`, provide `A1B2C3D4E5.com.yourdomain.yourapp` to Para. This is required by Apple for passkey security. **Note:** Allow up to 24 hours for domain propagation.
#### Install CocoaPods for native dependencies:
```bash
cd ios
bundle install
bundle exec pod install
cd ..
```
Remember to run `pod install` after adding new dependencies to your project.
#### Set Up Digital Asset Links
For Android setup, you need to provide your app's SHA-256 certificate fingerprint to Para.
**Quick Testing Option**: You can use `com.getpara.example.reactnative` as your package name for immediate testing. This package name is pre-registered and works with the SHA-256 certificate from the default debug.keystore.
To get your SHA-256 fingerprint:
* For debug builds: `keytool -list -v -keystore ~/.android/debug.keystore`
* For release builds: `keytool -list -v -keystore `
For production apps, you'll need to: 1. Upgrade your plan in the Developer Portal 2. Register your actual package name 3. Provide your app's SHA-256 fingerprint 4. Wait up to 24 hours for the Digital Asset Links to propagate
#### Device Requirements
To ensure passkey functionality works correctly:
* Enable biometric or device unlock settings (fingerprint, face unlock, or PIN)
* Sign in to a Google account on the device (required for Google Play Services passkey management)
### Configure Metro Bundler
Create or update `metro.config.js` in your project root:
```javascript metro.config.js
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const config = {
resolver: {
extraNodeModules: {
crypto: require.resolve("react-native-quick-crypto"),
buffer: require.resolve("@craftzdog/react-native-buffer"),
stream: require.resolve("readable-stream"),
},
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
```
### Add Para Shim
Import the Para shim as the FIRST import in your application's entry file (typically `index.js`):
```javascript
import "@getpara/react-native-wallet/dist/shim";
// Other imports...
```
**Important**: The shim import must come before any other imports to ensure required modules are available.
## Using the Para SDK
The Para SDK provides two main authentication methods: email-based and phone-based. Each method supports creating a new user and logging in an existing user. Both flows utilize Native Passkeys for secure and seamless authentication. Follow the steps below to implement these flows in your React Native 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.
### Create New User with Email
This flow guides you through the process of registering a new user with email, verifying their email, and setting up their wallet.
#### Initialize Para Client
First, set up the Para client to enable SDK interactions:
```typescript
import { ParaMobile, Environment } from "@getpara/react-native-wallet";
const para = new ParaMobile(Environment.BETA, YOUR_API_KEY);
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
#### Register New User
Create a new user account by calling the `createUser` method. This will automatically send a verification email to the user:
```typescript
const handleCreateUser = async (email: string): Promise => {
try {
await para.createUser({ email });
} catch (error) {
// Handle error
}
};
```
#### Verify Email
Once the user receives the verification code, call the `verifyEmailBiometricsId` method to confirm their email:
```typescript
const handleVerifyEmail = async (code: string): Promise => {
try {
const biometricsId = await para.verifyEmailBiometricsId(code);
return biometricsId;
} catch (error) {
// Handle error
}
};
```
#### Register Passkey and Create Wallet
Use the returned biometricsId to register the user's passkey and create their wallet:
```typescript
import { webcrypto } from "crypto";
const handleRegisterPasskeyAndCreateWallet = async (
email: string,
biometricsId: string
): Promise<{ wallets: any; recoverySecret: string } | undefined> => {
try {
await para.registerPasskey({ email, biometricsId, webcrypto });
const { wallets, recoverySecret } = await para.createWalletPerMissingType({ skipDistribute: false });
return { wallets, recoverySecret };
} catch (error) {
// Handle error
}
};
```
#### Complete New User Flow
Implement the full flow by combining all the previous steps:
```typescript
const handleNewUserFlow = async (email: string, verificationCode: string): Promise => {
await handleCreateUser(email);
const biometricsId = await handleVerifyEmail(verificationCode);
if (biometricsId) {
const result = await handleRegisterPasskeyAndCreateWallet(email, biometricsId);
if (result) {
// Securely display or store the recovery secret for the user
}
}
};
```
It's crucial to securely share the `recoverySecret` with the user. This secret is necessary for account recovery in case the user loses access to their device. Ensure you have a secure method to display or store this secret for the user.
### Login Existing User with Email
This flow demonstrates how to authenticate an existing user using their email and passkey.
#### Check User Existence and Login
Verify if the user exists and log them in using their passkey:
```typescript
const handleLogin = async (email: string): Promise => {
try {
const userExists = await para.checkIfUserExists({ email });
if (userExists) {
await para.login({ email });
} else {
// Handle new user case
}
} catch (error) {
// Handle error
}
};
```
The `login` method allows passing in an email if you want to specifically select a passkey for that email. If no email is provided, the user will be prompted to select a passkey from the available options.
### Create New User with Phone
This flow guides you through the process of registering a new user with a phone number, verifying it, and setting up their wallet.
#### Initialize Para Client
First, set up the Para client to enable SDK interactions:
```typescript
import { ParaMobile, Environment } from "@getpara/react-native-wallet";
const para = new ParaMobile(Environment.BETA, YOUR_API_KEY);
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
#### Register New User
Create a new user account by calling the `createUserByPhone` method. This will automatically send a verification code to the user's phone:
```typescript
const handleCreateUser = async (phone: string, countryCode: string): Promise => {
try {
await para.createUserByPhone({ phone, countryCode });
} catch (error) {
// Handle error
}
};
```
#### Verify Phone
Once the user receives the verification code, call the `verifyPhoneBiometricsId` method to confirm their phone number:
```typescript
const handleVerifyPhone = async (verificationCode: string): Promise => {
try {
const biometricsId = await para.verifyPhoneBiometricsId({ verificationCode });
return biometricsId;
} catch (error) {
// Handle error
}
};
```
#### Register Passkey
Use the returned biometricsId to register the user's passkey:
```typescript
const handleRegisterPasskey = async (
phone: string,
countryCode: string,
biometricsId: string
): Promise => {
try {
await para.registerPasskey({ phone, countryCode, biometricsId });
} catch (error) {
// Handle error
}
};
```
#### Create Wallet
After passkey registration, create the user's wallet:
```typescript
const handleCreateWallet = async (): Promise<{ wallets: any; recoverySecret: string } | undefined> => {
try {
const { wallets, recoverySecret } = await para.createWalletPerMissingType({ skipDistribute: false });
return { wallets, recoverySecret };
} catch (error) {
// Handle error
}
};
```
#### Complete Phone-based Flow
Here's an example of a complete phone-based authentication flow:
```typescript
const handleContinue = async (phone: string, countryCode: string): Promise => {
try {
const userExists = await para.checkIfUserExistsByPhone({ phone, countryCode });
if (userExists) {
await para.login({ phone, countryCode });
// Navigate to home screen
} else {
await para.createUserByPhone({ phone, countryCode });
// Show OTP input screen
}
} catch (error) {
// Handle error
}
};
const handleVerify = async (verificationCode: string, phone: string, countryCode: string): Promise => {
try {
const biometricsId = await para.verifyPhoneBiometricsId({ verificationCode });
if (biometricsId) {
await para.registerPasskey({ phone, countryCode, biometricsId });
const { recoverySecret } = await para.createWalletPerMissingType({ skipDistribute: false });
// Store recovery secret and navigate to home screen
}
} catch (error) {
// Handle error
}
};
// Resend verification code if needed
const resendOTP = async (): Promise => {
await para.resendVerificationCode();
};
```
It's crucial to securely share the `recoverySecret` with the user. This secret is necessary for account recovery in case the user loses access to their device. Ensure you have a secure method to display or store this secret for the user.
### Login Existing User with Phone
This flow demonstrates how to authenticate an existing user using their phone number and passkey.
#### Check User Existence and Login
Verify if the user exists and log them in using their passkey:
```typescript
const handleLogin = async (phone: string, countryCode: string): Promise => {
try {
const userExists = await para.checkIfUserExistsByPhone({ phone, countryCode });
if (userExists) {
await para.login({ phone, countryCode });
} else {
// Handle new user case
}
} catch (error) {
// Handle error
}
};
```
The phone-based `login` method requires providing both the `phone` and `countryCode` parameters to select the appropriate passkey.
By following these steps, you can implement a secure and user-friendly authentication system in your React Native application using the Para SDK, with support for both email and phone-based authentication methods.
## Examples
For practical implementation of the Para SDK in React Native environments, check out our GitHub repository:
## Troubleshooting
If you encounter issues during the integration or usage of the Para SDK in your React Native application, here are some
common problems and their solutions:
If you're having trouble initializing the Para SDK:
* Ensure that you've called `para.init()` after creating the Para instance.
* Verify that you're using the correct API key and environment.
* Check that all necessary dependencies are installed and linked properly.
* Look for any JavaScript errors in your Metro bundler console.
If you're seeing errors about missing native modules: - Run `pod install` in the `ios` directory to ensure all
CocoaPods dependencies are installed. - For Android, make sure your `android/app/build.gradle` file includes the
necessary dependencies. - Rebuild your app after adding new native dependencies. - If using Expo, ensure you've run
`expo prebuild` to generate native code.
If passkey creation, retrieval, or usage isn't working: - Verify that you've set up associated domains correctly for
both iOS and Android. - For iOS, check that your `entitlements` file includes the necessary associated domains. - For
Android, ensure your `asset_statements` are correctly configured in your `AndroidManifest.xml`. - Make sure you've
provided the correct SHA-256 fingerprint to the Para team for Android. - Check for any permission-related errors in
your logs.
If you're seeing errors related to crypto functions: - Ensure you've properly set up the
`react-native-get-random-values` and `react-native-quick-crypto` polyfills. - Verify that your `metro.config.js` file
is configured correctly to include the necessary Node.js core modules. - Check that you've imported the shim file at
the top of your root `index.js` file.
If you're experiencing authentication issues:
* Double-check that your API key is correct and properly set in your environment variables.
* Verify you're using the correct environment (`BETA` or `PRODUCTION`) that matches your API key.
* Ensure your account has the necessary permissions for the operations you're attempting.
* Check your network requests for any failed API calls and examine the error messages.
For a more comprehensive list of solutions, visit our troubleshooting guide:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# React Native and Expo Troubleshooting
Source: https://docs.getpara.com/react-native/troubleshooting
Resolving integration issues for Para in React Native and Expo environments
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
When incorporating Para into React Native or Expo applications, developers may face specific hurdles. This guide offers
solutions to common problems and provides best practices for a smooth integration process.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before addressing specific issues, try these general troubleshooting steps for both React Native and Expo projects:
```bash
# For React Native
rm -rf node_modules
npm cache clean --force
npm install
# For Expo
expo r -c
```
`bash expo prebuild --clean `
```bash
# For React Native
npx react-native run-ios
npx react-native run-android
# For Expo
expo run:ios
expo run:android
```
## Common Issues and Solutions
**Error**: Errors related to missing modules like `crypto`, `buffer`, or `stream`.
**Solution**: Update your `metro.config.js` to include necessary polyfills:
```javascript
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const nodeLibs = require("node-libs-react-native");
const config = {
resolver: {
extraNodeModules: {
...nodeLibs,
crypto: require.resolve("react-native-quick-crypto"),
buffer: require.resolve("@craftzdog/react-native-buffer"),
},
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
```
**Error**: Errors persist despite Metro configuration, often related to global objects.
**Solution**: Create a `shim.js` file at the root of your project and import it in your entry file.
For the complete `shim.js` setup, refer to the Polyfills and Shims section in the and setup guides.
**Error**: Errors related to `crypto.subtle` API or missing cryptographic functions.
**Solution**: Add the `PolyfillCrypto` component to your root `App` component.
````jsx
import PolyfillCrypto from "react-native-webview-crypto";
export default function App() {
return (
<>
{/* Your app components */}
>
);
}
**Error**: Passkey functionality not working. Errors related to `ASAuthorizationController` or `ASWebAuthenticationSession` on iOS and `CredenitalManager` on Android.
**Solution**:
Passkeys requires configuring both the iOS and Android environments with the Para associated domains and web credentials.
Please check Step one of the Project Setup section for the and setup guides for detailed instructions.
**Error**: Passkeys not functioning despite correct configuration.
**Solution**: Ensure your app's bundle identifier and team ID are registered with Para.
Contact Para support to associate your app correctly. Provide your app's bundle identifier and team ID. You can find your team ID in the Apple Developer portal.
**Error**: Native modules not working in Expo Go.
**Solution**: Para is reliant on native modules and will not work with Expo Go. Use Expo's build service to create a standalone app. You can do this by running `expo build:ios` or `expo build:android`. This will create the corresponding iOS or Android folders in your project and link the native modules correctly. Alternative use expo prebuild to create the native folders for both platforms.
**Error**: Native modules not linking correctly. Build errors related to missing pods. Build stalls at the linking stage.
**Solution**: iOS in React Native projects requires manual linking of pods. Ensure the pods are correctly linked by running `pod install` in the `ios` directory. Expo auto links the pods, but you can run `expo prebuild --clean` to ensure the pods are correctly linked.
```bash
cd ios
pod install
cd ..
npx react-native run-ios
````
**Error**: Errors retrieving stored items.
**Solution**: Ensure React Native Async Storage and Keychain are installed and linked. Additionally ensure that you run `para.init()` as it asynchronusly initializes the storage.
### Best Practices
1. Implement robust error handling for all Para operations.
2. Use secure storage methods for sensitive data.
3. Keep your project's native code up to date with the latest Para SDK requirements.
For comprehensive setup instructions and the most up-to-date integration guide, please refer to our
and quick start guides.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please contact our team 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.
# Server Examples
Source: https://docs.getpara.com/server/examples
Explore Para's server-side integration examples across Node.js, Bun, and Deno
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para offers a collection of server-side examples to help you integrate our technology across different JavaScript runtime environments. Our examples-hub repository includes server implementations that showcase how to use Para in Node.js, Bun, and Deno environments.
Each example demonstrates how to perform blockchain operations server-side using both pregenerated wallets and imported client sessions. These standalone examples focus on specific Para SDKs or features, providing minimal implementation requirements that you can easily adapt to your specific application needs.
## Runtime Environments
Browse our server examples showcasing Para integration across different JavaScript runtimes:
## Need Something Specific?
Don't see an example for your server-side use case? Para's team is eager to create new examples to help you integrate with different libraries, runtime environments, or blockchain ecosystems. Reach out to our support team for assistance.
# Account Abstraction
Source: https://docs.getpara.com/server/guides/account-abstraction
Implement ERC-4337 Account Abstraction with Para Server SDK
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Account Abstraction (AA) enables powerful smart contract wallet functionality like batched transactions, gas sponsorship, and advanced access controls. Para Server SDK supports integration with leading AA providers, allowing you to implement these capabilities in server-side environments.
When using Account Abstraction with Para, your Para wallet acts as the EOA (Externally Owned Account) signer that controls the smart contract wallet. The AA provider deploys and manages the smart contract wallet on-chain, while Para handles the secure signing required for authorizing transactions.
Server-side AA implementation functions identically to client-side implementation, with the additional requirement of signature byte adjustment for contract verification.
## Understanding Signature Adjustment
Account Abstraction providers use on-chain signature verification within their smart contracts. Para's 2/2 MPC wallet produces non-deterministic signatures where the last byte (`v` recovery byte) may need adjustment for proper on-chain verification.
**Critical**: Para signatures may require adjustment of the last byte for compatibility with AA providers. Without this adjustment, transactions might fail at the network level despite signature success.
## Implementation Steps
Let's break down the process of implementing Account Abstraction on the server into manageable steps:
### 1. Set Up Para Server Client
First, initialize the Para Server SDK and authenticate with an imported session:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
// Initialize Para Server SDK
const para = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Import a client session
await para.importSession(session);
```
If you need more details on setup please refer tot he .
### 2. Create a Viem Client
Next, use Para's Viem integration to create a Viem client:
```typescript
import { createParaAccount, createParaViemClient } from "@getpara/viem-integration";
import { http } from "viem/transport";
import { arbitrumSepolia } from "viem/chains";
// Create a Para account for Viem
const viemParaAccount = createParaAccount(para);
// Initialize the Viem client
const viemClient = createParaViemClient(para, {
account: viemParaAccount,
chain: arbitrumSepolia,
transport: http("YOUR_RPC_URL"),
});
```
### 3. Implement Signature Adjustment
Create a helper function to properly adjust the signature byte for on-chain verification:
```typescript
import { hashMessage, type Hash, type SignableMessage } from "viem";
function hexStringToBase64(hexString) {
return Buffer.from(hexString, "hex").toString("base64");
}
// Custom sign message function with signature byte adjustment
async function customSignMessage(para, message) {
// Get the first wallet from Para client
const wallet = para.wallets ? Object.values(para.wallets)[0] : null;
if (!wallet) {
throw new Error("Para wallet not available for signing.");
}
// Hash and convert the message
const hashedMessage = hashMessage(message);
const messagePayload = hashedMessage.startsWith("0x") ? hashedMessage.substring(2) : hashedMessage;
const messageBase64 = hexStringToBase64(messagePayload);
// Sign with Para
const res = await para.signMessage({
walletId: wallet.id,
messageBase64: messageBase64,
});
if (!("signature" in res)) {
throw new Error("Signature failed");
}
// Adjust the signature's 'v' value for on-chain verification
let signature = res.signature;
const vHex = signature.slice(-2);
const v = parseInt(vHex, 16);
if (!isNaN(v) && v < 27) {
const adjustedVHex = (v + 27).toString(16).padStart(2, "0");
signature = signature.slice(0, -2) + adjustedVHex;
}
return `0x${signature}`;
}
```
As mentioned earlier, this function adjusts the last byte of the signature to ensure compatibility with on-chain verification. This is crucial for successful transaction execution when working with Account Abstraction providers.
### 4. Override Viem Client's Sign Function
Apply the custom signing function to the Viem client:
```typescript
// Override sign message function with custom implementation
viemClient.signMessage = async ({ message }) => customSignMessage(para, message);
```
### 5. Create the AA Provider Client
Finally, create the Account Abstraction client using your preferred provider. Here's an example with Alchemy:
```typescript
import { WalletClientSigner } from "@alchemy/aa-core";
import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy";
// Create Alchemy AA client with the customized Viem client
const walletClientSigner = new WalletClientSigner(viemClient, "para");
const alchemyClient = await createModularAccountAlchemyClient({
apiKey: "YOUR_ALCHEMY_API_KEY",
chain: arbitrumSepolia,
signer: walletClientSigner,
gasManagerConfig: {
policyId: "YOUR_ALCHEMY_GAS_POLICY_ID", // Optional: For gas sponsorship
},
});
```
## Using the AA Smart Wallet
Once your AA client is set up, you can perform transactions through the smart contract wallet:
### Get Smart Wallet Address
First, retrieve the smart wallet address (this is different from your Para wallet address):
```typescript
// Get the smart wallet address
const smartWalletAddress = await alchemyClient.getAddress();
console.log("Smart Wallet Address:", smartWalletAddress);
```
Be aware that funds should be sent to the smart wallet address (returned by `getAddress()`) rather than the Para wallet address. The smart wallet is the entity that holds funds and executes transactions on-chain.
### Send Transactions
Execute transactions through the smart wallet:
```typescript
import { parseEther } from "viem";
// Send a simple transfer
const txHash = await alchemyClient.sendTransaction({
to: "0xRecipientAddress",
value: parseEther("0.01"),
data: "0x", // Optional for simple transfers
});
console.log("Transaction Hash:", txHash);
```
### Execute Contract Interactions
Interact with smart contracts through your AA wallet:
```typescript
// Interact with a smart contract
const tokenContract = "0xTokenContractAddress";
const data = "0x..." // Encoded contract function call
const contractTxHash = await alchemyClient.sendTransaction({
to: tokenContract,
data: data,
});
```
## Supported AA Providers
Para integrates with several leading Account Abstraction providers:
## Troubleshooting
If transactions fail despite successful signing, check that the signature byte adjustment is working correctly. The most common issue is that the 'v' byte isn't properly adjusted for on-chain verification.
First-time use of an AA wallet requires contract deployment, which can be more expensive and complex. Ensure you have sufficient funds in the EOA (Para wallet) for this initial deployment.
If using gas sponsorship, verify your policy ID and settings in the AA provider's dashboard. Also check that the transaction meets the policy requirements (value limits, allowed functions, etc.).
## Best Practices
1. **Always adjust signatures**: Implement the signature adjustment function to ensure compatibility with on-chain verification.
2. **Test thoroughly**: Before deploying to production, test your AA implementation in a test environment with minimal funds.
3. **Handle errors gracefully**: Implement proper error handling for signature failures and transaction rejections.
4. **Monitor gas costs**: Be aware of the additional gas costs associated with AA transactions, especially for contract deployments.
5. **Secure session management**: Follow secure practices for managing Para sessions in server environments.
6. **Consider gas sponsorship**: Many AA providers offer gas sponsorship, which can improve user experience by eliminating gas fees.
## Learn More
For more detailed information on Account Abstraction concepts and implementation, refer to our web documentation:
## Examples
Explore our server-side Account Abstraction examples:
# Cosmos Integration
Source: https://docs.getpara.com/server/guides/cosmos
Use Para Server SDK with Cosmos-based blockchains
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para Server SDK seamlessly integrates with Cosmos-based blockchains through the CosmJS library. Once you've set up and authenticated your Para Server client, the Cosmos integration works identically to the client-side implementation.
Before using this integration, ensure you've completed the server setup by importing a client session or creating a pregenerated wallet. See the for details.
## Installation
Install the required dependencies for Cosmos integration:
```bash
npm install @getpara/cosmjs-v0-integration @cosmjs/stargate @cosmjs/proto-signing
```
## Implementation
The Para integration with CosmJS provides a custom signer that works with Stargate Client:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
import { ParaProtoSigner } from "@getpara/cosmjs-v0-integration";
import { SigningStargateClient } from "@cosmjs/stargate";
// Para server client (already authenticated)
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Create the Para Cosmos Signer
const signer = new ParaProtoSigner(paraServer, "cosmos");
// Connect to the Cosmos network
const rpcUrl = "https://rpc.cosmos.network"; // Replace with your preferred RPC endpoint
const client = await SigningStargateClient.connectWithSigner(rpcUrl, signer);
// Get the wallet address
const address = await signer.getAddress();
console.log(`Wallet address: ${address}`);
// Get account balance
const balance = await client.getBalance(address, "uatom");
console.log(`Balance: ${balance.amount} ${balance.denom}`);
// Send tokens
const recipient = "cosmos1recipient";
const amount = {
denom: "uatom",
amount: "100000", // 0.1 ATOM (uatom is microatom, 1 ATOM = 1,000,000 uatom)
};
const result = await client.sendTokens(
address,
recipient,
[amount],
{
amount: [{ denom: "uatom", amount: "5000" }],
gas: "200000",
}
);
console.log(`Transaction hash: ${result.transactionHash}`);
```
## Chain Support
The Para Cosmos integration supports various Cosmos-based chains. You can specify the chain when creating the signer:
```typescript
// For Cosmos Hub
const cosmosSigner = new ParaProtoSigner(paraServer, "cosmos");
// For Osmosis
const osmosisSigner = new ParaProtoSigner(paraServer, "osmosis");
// For other supported chains
const otherChainSigner = new ParaProtoSigner(paraServer, "chainName");
```
## Best Practices
* Use appropriate gas settings for different types of transactions
* Implement proper error handling for network failures
* Consider retry logic for RPC endpoints
* Always verify transaction details before sending
## Learn More
For detailed examples of using Para with Cosmos, including chain-specific operations and advanced transaction types, refer to our web documentation:
## Examples
Explore our server-side Cosmos integration examples:
# EVM Integration
Source: https://docs.getpara.com/server/guides/evm
Use Para Server SDK with Ethereum and EVM-compatible chains
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para Server SDK provides seamless integration with Ethereum Virtual Machine (EVM) compatible chains through popular libraries like Ethers.js and Viem. Once you've set up and authenticated your Para Server client, the EVM integration works identically to the client-side implementation.
Before using these integrations, ensure you've completed the server setup by importing a client session or creating a pregenerated wallet. See the for details.
## Installation
Install the required dependencies for your preferred EVM library:
```bash Ethers.js
npm install @getpara/ethers-v6-integration ethers
```
```bash Viem
npm install @getpara/viem-integration viem
```
## Implementation
### Ethers.js Integration
The Para Ethers Signer acts as a drop-in replacement for standard Ethers signers:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
import { ParaEthersSigner } from "@getpara/ethers-v6-integration";
import { ethers } from "ethers";
// Para server client (already authenticated)
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Set up the provider with your RPC URL
const provider = new ethers.JsonRpcProvider("YOUR_RPC_URL");
// Create the Para Ethers Signer
const signer = new ParaEthersSigner(paraServer, provider);
// Now you can use the signer with any Ethers.js operations
const balance = await provider.getBalance(await signer.getAddress());
console.log(`Balance: ${ethers.formatEther(balance)} ETH`);
// Sign a message
const signature = await signer.signMessage("Hello from Para Server!");
// Send a transaction
const tx = await signer.sendTransaction({
to: "0xRecipientAddress",
value: ethers.parseEther("0.001")
});
```
### Viem Integration
Para's Viem integration provides a custom Viem client compatible with all Viem operations:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
import { createParaAccount, createParaViemClient } from "@getpara/viem-integration";
import { http } from "viem";
import { sepolia } from "viem/chains";
// Para server client (already authenticated)
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Create a Para Account
const account = await createParaAccount(paraServer);
// Create the Para Viem WalletClient
const walletClient = createParaViemClient(paraServer, {
account: account,
chain: sepolia,
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
});
// Now you can use the walletClient with any Viem operations
const hash = await walletClient.sendTransaction({
to: "0xRecipientAddress",
value: 1000000000000000n // 0.001 ETH
});
```
## Best Practices
* Use environment variables for API keys and RPC URLs
* Implement proper error handling for network failures
* Consider gas price management for production applications
* Cache network calls where appropriate to reduce RPC usage
## Learn More
For detailed examples of using Para with EVM chains, including smart contract interactions, ERC-20 transfers, and more, refer to our web documentation:
## Examples
Explore our server-side EVM integration examples:
# Wallet Pregeneration
Source: https://docs.getpara.com/server/guides/pregen
Create and manage server-side pregenerated wallets using Para's Server SDK
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Pregenerated wallets are a powerful feature of Para that enable server-side wallet creation without requiring user authentication. This approach allows your application to perform blockchain operations autonomously, making it ideal for agent-based systems, automated workflows, and backend services.
The pregeneration flow works by creating a wallet associated with an identifier of your choice (email, phone, username, or custom ID). Para then provides you with the user share of the 2/2 MPC key, which you must securely store on your server until it's either claimed by a user or used for signing operations.
## Key Benefits
* **Authentication-free operations**: Create and use wallets without requiring user authentication
* **Autonomous agents**: Provide blockchain capabilities to AI agents or automated systems
* **User pre-provisioning**: Create wallets for existing user bases before they interact with your application
* **Server-side signing**: Perform blockchain operations entirely from your backend
## Creating a Pregenerated Wallet
```typescript Node.js
import { Para as ParaServer, Environment, WalletType } from "@getpara/server-sdk";
// Initialize the Para Server SDK
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Check if a wallet already exists for this identifier
const hasWallet = await paraServer.hasPregenWallet({
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
// Create a pregenerated wallet if needed
if (!hasWallet) {
const pregenWallet = await paraServer.createPregenWallet({
type: WalletType.EVM, // or WalletType.SOLANA, WalletType.COSMOS
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
// Now use the pregenerated wallet
}
```
```typescript Bun
import { Para as ParaServer, Environment, WalletType } from "@getpara/server-sdk";
// Initialize the Para Server SDK with WebSockets disabled for Bun
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY", {
disableWebSockets: true
});
// Check if a wallet already exists for this identifier
const hasWallet = await paraServer.hasPregenWallet({
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
// Create a pregenerated wallet if needed
if (!hasWallet) {
const pregenWallet = await paraServer.createPregenWallet({
type: WalletType.EVM, // or WalletType.SOLANA, WalletType.COSMOS
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
// Now use the pregenerated wallet
}
```
```typescript Deno
import { Para as ParaServer, Environment, WalletType } from "@getpara/server-sdk";
// Initialize the Para Server SDK with WebSockets disabled for Deno
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY", {
disableWebSockets: true
});
// Check if a wallet already exists for this identifier
const hasWallet = await paraServer.hasPregenWallet({
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
// Create a pregenerated wallet if needed
if (!hasWallet) {
const pregenWallet = await paraServer.createPregenWallet({
type: WalletType.EVM, // or WalletType.SOLANA, WalletType.COSMOS
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
// Now use the pregenerated wallet
}
```
### Method Parameters
The type of wallet to create (WalletType.EVM, WalletType.SOLANA, WalletType.COSMOS)
The string identifier for the new wallet
The identifier type. Allowed types are 'EMAIL', 'PHONE', 'DISCORD', 'TWITTER', or 'CUSTOM\_ID'
The identifier can be anything from an email or phone number to a unique UUID. Choose an identifier that works best for your application architecture.
## Securing the User Share
After creating a pregenerated wallet, you must securely store the user share. This component is critical for the wallet's operation and security.
```typescript
// Retrieve the user share for the pregenerated wallet
const userShare = await paraServer.getUserShare();
// Store this user share securely in your database
// NEVER store this in plain text - always encrypt it!
```
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.
### Secure Storage Best Practices
We strongly recommend implementing robust encryption for user shares both in transit and at rest. Consider using a high-entropy encryption key with AES-GCM encryption. Do not store encryption keys in the same database as the encrypted data.
Para offers pre-launch security reviews for teams in the Growth tier or above. Reach out to the Para team for assistance with your implementation!
### Storage Security Recommendations
* **Encrypt** user shares in-transit and at-rest
* Implement **access controls** for your share database
* Maintain regular **database backups** to prevent data loss
* Create **disaster recovery** processes for compromise scenarios
* Have a **key rotation** plan in case of security incidents
* Complete **security fire drills** before launching
## Using a Pregenerated Wallet
To use a pregenerated wallet for blockchain operations, you need to:
1. Retrieve the encrypted user share from your database
2. Decrypt the user share
3. Load it into your Para client instance
```typescript
// Retrieve and decrypt the user share from your database
const encryptedUserShare = await database.getUserShare(walletId);
const userShare = decryptUserShare(encryptedUserShare);
// Load the user share into the Para client
await paraServer.setUserShare(userShare);
```
When implementing `setUserShare` in API routes or serverless functions, it's critical to create a new Para client instance for each request. This prevents different users' shares from conflicting with each other. Creating a new Para client has minimal overhead and won't impact request performance.
### Signing Operations
Once the user share is loaded, you can test the wallet functionality by signing a message. However, for production use, we recommend using the blockchain library integrations described in the next section.
```typescript
// Test signing a message
const messageBase64 = Buffer.from("Hello, World!").toString('base64');
const signature = await paraServer.signMessage({
walletId,
messageBase64,
});
```
### Using with Blockchain Libraries
Pregenerated wallets work seamlessly with all blockchain integration libraries. Here are some examples:
## Wallet Claiming Flow
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.
For applications that need to transfer ownership of pregenerated wallets to users, you'll need to implement a client-side claiming flow. This process involves:
1. Securely transferring the user share from your server to the client
2. Loading the share into the client-side Para instance
3. Using the client SDK to claim the wallet
For a comprehensive guide on implementing the claiming flow, refer to our web documentation:
## Core Pregeneration Methods
Create a new pregenerated wallet for an identifier
Check if a pregenerated wallet exists for an identifier
Retrieve pregenerated wallets for a given identifier
Get the user share that must be securely stored
Load a previously stored user share
Update the identifier of a pregenerated wallet
## Use Cases
Pregenerated wallets enable autonomous AI agents to perform blockchain operations. For example, an AI trading agent could analyze market conditions and execute trades using its own wallet, without requiring human intervention or authentication.
Create automation systems that perform recurring blockchain operations on predetermined schedules. For instance, a DeFi yield harvesting service could automatically collect and reinvest yields at optimal times using pregenerated wallets.
Create wallets for your existing user base before they engage with blockchain features. When users are ready to interact with these features, they can claim ownership of their pregenerated wallet through a seamless onboarding process.
Build backend services that perform blockchain operations on behalf of users or systems. For example, a gas fee management service could optimize transaction timing based on network conditions without requiring user input.
Maintain consistent wallet identities across different platforms by using the same pregenerated wallets. This allows users to have a unified experience regardless of which platform they're using to access your service.
## Best Practices
* **Choose appropriate identifiers** that align with your application architecture
* **Implement robust encryption** for user share storage
* **Create backup systems** to prevent data loss
* **Use a separate database** for user share storage with enhanced security
* **Monitor for suspicious activity** in your pregenerated wallet systems
* **Implement rate-limiting** to prevent abuse of wallet creation
* **Document your recovery procedures** for security incidents
* **Consider multi-region replication** for high-availability systems
## Example Implementation
Here's a reference example demonstrating the creation and secure storage of pregenerated wallets:
## Community Showcase
Check out some novel use cases of pregenerated wallets created by our community:
# Session Management
Source: https://docs.getpara.com/server/guides/sessions
Manage client sessions in server-side environments with Para Server SDK
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para's Server SDK enables you to import and manage client-side sessions within your server environment. This allows your server to perform authenticated blockchain operations on behalf of users without requiring them to re-authenticate. The server can also validate existing client sessions using either the Para client or dedicated verification endpoints.
## Importing Client Sessions
To use a client session on your server, you need to:
1. Export the session from the client-side Para instance
2. Transfer the session to your server securely
3. Import the session into your server-side Para instance
### Client-Side Session Export
First, have your client-side application export the active session:
```typescript
// Client-side
const para = new Para(Environment.BETA, "YOUR_API_KEY");
// Export the current session
const serializedSession = await para.exportSession();
// Send this to your server endpoint
await fetch("/api/import-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session: serializedSession }),
});
```
If signing on the server isn't required, you can pass `{ excludeSigners: true }` as an argument to `exportSession` to remove the signer data from the exported wallets, enhancing security:
```typescript
const serializedSession = await para.exportSession({ excludeSigners: true });
```
### Server-Side Session Import
On your server, import the session into a Para Server SDK instance:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
// Initialize the Para Server SDK
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
app.post("/api/import-session", async (req, res) => {
try {
// Import the session from the client
await paraServer.importSession(req.body.session);
// Now you can use the paraServer instance for authenticated operations
// ...
res.status(200).json({ success: true });
} catch (error) {
res.status(500).json({ error: "Failed to import session" });
}
});
```
Create a new Para client instance for each request when handling multiple users. This prevents session conflicts between different users' requests and ensures security isolation.
## Session Validation
You can validate sessions on the server side to ensure they're still active before performing operations.
### Using the Para Client
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
app.post("/api/authenticated-action", async (req, res) => {
try {
// Import the session
await paraServer.importSession(req.body.session);
// Check if the session is still active
const isActive = await paraServer.isSessionActive();
if (!isActive) {
return res.status(401).json({ error: "Session expired" });
}
// Proceed with authenticated operations
// ...
res.status(200).json({ success: true });
} catch (error) {
res.status(500).json({ error: "Operation failed" });
}
});
```
### Using Verification Tokens
For non-Node.js servers or scenarios where you only need to validate a session without importing it, Para provides dedicated verification endpoints:
```typescript
// Client-side: Get a verification token
const verificationToken = await para.getVerificationToken();
// Send to your server
await fetch("/api/verify-session", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ verificationToken }),
});
```
On your server, verify the token against Para's API:
```typescript Node.js
// Server-side verification
app.post("/api/verify-session", async (req, res) => {
const { verificationToken } = req.body;
if (!verificationToken) {
return res.status(400).json({ error: "Missing verification token" });
}
// Set the correct URL based on your environment
const verifyUrl = "https://api.beta.getpara.com/sessions/verify";
try {
const response = await fetch(verifyUrl, {
method: "POST",
headers: {
"content-type": "application/json",
"x-external-api-key": "YOUR_API_KEY"
},
body: JSON.stringify({ verificationToken }),
});
if (response.status === 403) {
return res.status(403).json({ error: "Session expired" });
}
const userData = await response.json();
// userData contains { authType, identifier }
// Proceed with authenticated operations
res.status(200).json({ userData });
} catch (error) {
res.status(500).json({ error: "Verification failed" });
}
});
```
```python Python
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/api/verify-session', methods=['POST'])
def verify_session():
data = request.get_json()
verification_token = data.get("verificationToken")
if not verification_token:
return jsonify({"error": "Missing verification token"}), 400
# Set the correct URL based on your environment
verify_url = "https://api.beta.getpara.com/sessions/verify"
response = requests.post(
url=verify_url,
json={"verificationToken": verification_token},
headers={
"content-type": "application/json",
"x-external-api-key": "YOUR_API_KEY"
}
)
if response.status_code == 403:
return jsonify({"error": "Session expired"}), 403
user_data = response.json()
# user_data contains { authType, identifier }
# Proceed with authenticated operations
return jsonify({"userData": user_data})
if __name__ == '__main__':
app.run(debug=True)
```
The verification endpoints are environment-specific:
| Environment | Verification URL |
| ----------- | ------------------------------------------------- |
| SANDBOX | `https://api.sandbox.getpara.com/sessions/verify` |
| BETA | `https://api.beta.getpara.com/sessions/verify` |
| PROD | `https://api.getpara.com/sessions/verify` |
The verification response will contain the authentication type, identifier, and optionally the OAuth method used:
```typescript
{
authType: "email" | "phone" | "farcaster" | "telegram" | "externalWallet";
identifier: string;
oAuthMethod?: "google" | "x" | "discord" | "facebook" | "apple";
}
```
## Session Management
### Maintaining Session Validity
To extend the validity of an imported session, you can use the `keepSessionAlive` method:
```typescript
try {
const success = await paraServer.keepSessionAlive();
if (!success) {
// Session couldn't be extended
// The client may need to re-authenticate
}
} catch (error) {
console.error("Failed to maintain session:", error);
}
```
You can configure session duration (up to 30 days) in the . This affects how long sessions remain valid without explicit extension.
### Best Practices
1. **Create new Para instances per request**: Initialize a fresh Para Server SDK instance for each request to prevent session conflicts between users.
2. **Secure session transport**: Always use HTTPS and consider additional encryption when transferring sessions between client and server.
3. **Exclude signers when possible**: Use `{ excludeSigners: true }` when exporting sessions if server-side signing isn't needed.
4. **Validate before operations**: Always check if a session is active before performing blockchain operations.
5. **Handle expiration gracefully**: Implement proper error handling for expired sessions, guiding users to re-authenticate when necessary.
6. **Consider session verification tokens**: For simple authentication checks without full session import, use verification tokens.
7. **Set appropriate session duration**: Configure session length in the developer portal based on your security requirements.
## Learn More
For more information about client-side session management and authentication, refer to our web documentation:
## Examples
To learn more about using sessions on the server, check out this example. Each example route will have both pregen and session based routes for you to test with.
# Solana Integration
Source: https://docs.getpara.com/server/guides/solana
Use Para Server SDK with Solana blockchain
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para Server SDK provides seamless integration with Solana blockchain through both Solana Web3.js and Anchor frameworks. Once you've set up and authenticated your Para Server client, the Solana integration works identically to the client-side implementation.
Before using these integrations, ensure you've completed the server setup by importing a client session or creating a pregenerated wallet. See the for details.
## Installation
Install the required dependencies for your preferred Solana library:
```bash Solana Web3.js
npm install @getpara/solana-web3.js-v1-integration @solana/web3.js
```
```bash Anchor
npm install @getpara/solana-web3.js-v1-integration @solana/web3.js @coral-xyz/anchor
```
## Implementation
### Solana Web3.js Integration
The Para Solana Web3 Signer works seamlessly with the Solana Web3.js library:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration";
import { Connection, clusterApiUrl, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
// Para server client (already authenticated)
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Set up Solana connection
const solanaConnection = new Connection(clusterApiUrl("testnet"));
// Create the Para Solana Signer
const solanaSigner = new ParaSolanaWeb3Signer(paraServer, solanaConnection);
// Get the wallet address
const walletAddress = solanaSigner.sender.toBase58();
console.log(`Wallet address: ${walletAddress}`);
// Create and send a transaction
const transaction = await solanaSigner.createTransaction({
instructions: [
SystemProgram.transfer({
fromPubkey: solanaSigner.sender,
toPubkey: new PublicKey("RecipientPublicKeyHere"),
lamports: 0.01 * LAMPORTS_PER_SOL,
}),
],
});
const signature = await solanaSigner.sendTransaction(transaction);
console.log(`Transaction signature: ${signature}`);
```
### Anchor Integration
Para can be used with Anchor by creating an Anchor-compatible wallet wrapper:
```typescript
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration";
import { Connection, clusterApiUrl, Transaction, VersionedTransaction } from "@solana/web3.js";
import * as anchor from "@coral-xyz/anchor";
// Para server client (already authenticated)
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
// Set up Solana connection
const solanaConnection = new Connection(clusterApiUrl("testnet"));
const solanaSigner = new ParaSolanaWeb3Signer(paraServer, solanaConnection);
// Create an Anchor-compatible wallet
const anchorWallet = {
publicKey: solanaSigner.sender,
signTransaction: async (tx: T): Promise => {
return await solanaSigner.signTransaction(tx);
},
signAllTransactions: async (txs: T[]): Promise => {
return await Promise.all(txs.map((tx) => solanaSigner.signTransaction(tx)));
},
};
// Create the Anchor provider
const provider = new anchor.AnchorProvider(
solanaConnection,
anchorWallet,
{ commitment: "confirmed" }
);
// Now you can use this provider with any Anchor program
const program = new anchor.Program(
YOUR_IDL,
"PROGRAM_ID_HERE",
provider
);
// Interact with your program
await program.methods
.yourProgramMethod()
.accounts({
// Your accounts here
})
.rpc();
```
## Best Practices
* Use higher commitment levels (`confirmed` or `finalized`) for critical transactions
* Implement proper error handling for network failures
* Consider retry logic for Solana RPC endpoints, which can occasionally be unreliable
* Cache account data where appropriate to reduce RPC usage
## Learn More
For detailed examples of using Para with Solana, including SPL token transfers, NFT interactions, and more, refer to our web documentation:
## Examples
Explore our server-side Solana integration examples:
# Para Server SDK
Source: https://docs.getpara.com/server/overview
An introduction to server-side integrations with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para's Server SDK enables you to perform blockchain operations on the server-side across Node.js, Bun, and Deno environments. The server SDK shares the same core functionality as the web SDK, with authentication being the primary difference.
## Getting Started
## Blockchain Ecosystem Support
Para works seamlessly with major blockchain ecosystems on the server-side, allowing you to leverage Para's authentication alongside chain-specific libraries:
## Authentication Options
Para offers two authentication methods for server-side applications:
## Resources
# Server Setup
Source: https://docs.getpara.com/server/setup
Install and configure the Para Server SDK across Node.js, Bun, and Deno environments
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
The Para Server SDK enables secure server-side blockchain operations across different JavaScript runtime environments.
With nearly identical functionality to client-side implementations, the server SDK allows you to perform signing
operations server-side by either importing client-side sessions or using pregenerated wallets.
## Installation
Install the Para Server SDK in your preferred JavaScript runtime environment:
```bash npm
npm install @getpara/server-sdk
```
```bash yarn
yarn add @getpara/server-sdk
```
```bash pnpm
pnpm add @getpara/server-sdk
```
```bash bun
bun add @getpara/server-sdk
```
```bash deno
deno install npm:@getpara/server-sdk
```
## Initialization
Initialize the Para Server SDK with your API key. The initialization process varies slightly depending on your runtime
environment:
```typescript Node.js
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
// Standard initialization for Node.js const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY");
```
```typescript Bun
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
// Bun requires disabling WebSockets
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY", {
disableWebSockets: true
});
```
```typescript Deno
import { Para as ParaServer, Environment } from "@getpara/server-sdk";
// Deno requires disabling WebSockets
const paraServer = new ParaServer(Environment.BETA, "YOUR_API_KEY", {
disableWebSockets: true,
});
```
## Authentication Methods
After initializing the Para Server SDK, you need to authenticate it before performing any operations. The server SDK
supports two authentication methods:
### Option 1: Importing Client Sessions
Import an active session from your client application to create a server-side replica that can perform the same
operations.
First, in your client application, export the active session to get a serialized string:
```typescript
// Client-side
const serializedSession = await para.exportSession();
// Send this string to your server endpoint
```
This serialiazed sessions tring can then be sent to your server via an API endpoint or any other secure method.
If signing on the server isn't required, you can pass `{ excludeSigners: true }` as an argument to `exportSession` to remove the signer data from the exported wallets:`await para.exportSession({ excludeSigners: true });` before sending it to your server.
Then, on your server, import the serialized session to create a functional server-side client:
```typescript
// Server-side
await paraServer.importSession(serializedSession);
```
With a session no loaded into the para server instance, you can now perform all operations that the Para Server SDK supports.
### Option 2: Using Pregenerated Wallets
Generate and use deterministic wallets server-side without requiring client-side authentication. This pregen wallet will load the para server instance with a wallet that can be used for all operations that the Para Server SDK supports.
```typescript
import { WalletType } from "@getpara/server-sdk";
await paraServer.createPregenWallet({
type: WalletType.EVM, // or WalletType.SOLANA, WalletType.COSMOS
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
```
When using pregenerated wallets, you'll need to manage the user share of the wallet and store it securely.
Learn more about pregen wallets and their usage on the server in our pregen wallets guide.
## Examples
Explore our example implementations of the Para Server SDK across different runtime environments:
## Troubleshooting
If you encounter issues while setting up the Para Server SDK, check out our troubleshooting guide:
## Next Steps
Once your Para Server SDK is set up and authenticated, you can start performing blockchain operations. The server SDK
provides the same functionality as the client SDK, allowing you to:
# Web & React SDK APIs
Source: https://docs.getpara.com/web/api/sdk
## CorePara Class
The `ParaCore` class provides the core Javascript implementation of the Para SDK, managing wallet operations,
authentication, and transactions.
### Properties
Static version of the CorePara class.
Context object for the CorePara instance.
The IDs of the currently active wallets, for each supported wallet type.
Wallets associated with the CorePara instance.
The addresses of the currently active external wallets.
External wallets associated with the CorePara instance.
Whether the instance has multiple wallets connected.
Base theme for the emails sent from this Para instance.
Hex color to use as the primary color in the emails.
Linkedin URL to link to in the emails.
Github URL to link to in the emails.
X (Twitter) URL to link to in the emails.
Support URL to link to in the emails.
URL for your home landing page.
Encryption key pair generated from loginEncryptionKey.
Theme to use for the portal.
Types of wallets supported by this CorePara instance.
### Methods
Constructs a new CorePara instance.
Environment to use.
API key to use.
Additional constructor options.
Initialize storage relating to a CorePara instance.
Sets the email associated with the CorePara instance.
Email to set.
Sets the phone number associated with the CorePara instance.
Phone number to set.
Country Code to set.
Creates a new user.
Email to use for creating the user.
Passes the email code obtained from the user for verification.
Verification code to verify.
Checks if a session is active and a wallet exists.
Creates several new wallets with the desired types.
If true, the wallets' recovery share will not be distributed.
The types of wallets to create.
Signs a message.
ID of the wallet to sign with.
Base64 encoding of exact message that should be signed.
Signs a transaction.
ID of the wallet to sign the transaction from.
RLP encoded tx as base64 string.
Chain ID of the chain the transaction is being sent on.
Logs the user out.
Preserves the stored pregen wallets in memory after the logout.
# Web Examples
Source: https://docs.getpara.com/web/examples
Explore Para's web integration examples across popular frameworks
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
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.
# Alchemy AccountKit
Source: https://docs.getpara.com/web/guides/account-abstraction/alchemy
Learn how to integrate Alchemy AccountKit with a Para Signer for smart account creation and UserOp management in your application.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Introduction
enables smart account creation and management for every user in your application. Using Account Kit with Para, you can:
* Create and manage smart accounts directly in your app
* Submit gasless transactions using Alchemy's Gas Manager
* Execute batch transactions via UserOperations
* Handle transactions without requiring users to leave your application
## Prerequisites
Before you begin, ensure you have:
* A Para API key from the
* An Alchemy API key from the
* Optional: An Alchemy Gas Policy ID for gasless transactions
* Already set up authentication with Para. See our
for details.
For gasless transactions, you'll need to set up a Gas Manager Policy in your Alchemy Dashboard. This allows you to
sponsor gas fees for your users' transactions.
## Installation
```bash npm
npm install @alchemy/aa-alchemy @alchemy/aa-core viem
```
```bash yarn
yarn add @alchemy/aa-alchemy @alchemy/aa-core viem
```
```bash pnpm
pnpm add @alchemy/aa-alchemy @alchemy/aa-core viem
```
```bash bun
bun add @alchemy/aa-alchemy @alchemy/aa-core viem
```
## Setup
### 1. Create Para Viem Client
First, create a Viem client configured with your Para account:
```typescript
import { LocalAccount, WalletClient } from "viem";
import { sepolia } from "viem/chains";
import { http } from "viem";
const viemParaAccount: LocalAccount = createParaAccount(paraClient);
const viemClient: WalletClient = createParaViemClient(paraClient, {
account: viemParaAccount,
chain: sepolia,
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
});
```
### 2. Implement Custom Sign Message
Due to MPC requirements, implement a custom sign message function to handle the signature's v value:
```typescript
async function customSignMessage(para: ParaServer, message: SignableMessage): Promise {
const hashedMessage = hashMessage(message);
const res = await para.signMessage(Object.values(para.wallets!)[0]!.id, hexStringToBase64(hashedMessage));
let signature = (res as SuccessfulSignatureRes).signature;
// Fix the v value of the signature
const lastByte = parseInt(signature.slice(-2), 16);
if (lastByte < 27) {
const adjustedV = (lastByte + 27).toString(16).padStart(2, "0");
signature = signature.slice(0, -2) + adjustedV;
}
return `0x${signature}`;
}
```
### 3. Configure Viem Client with Custom Sign Message
Override the default signMessage method:
```typescript
viemClient.signMessage = async ({ message }: { message: SignableMessage }): Promise => {
return customSignMessage(paraClient, message);
};
```
### 4. Initialize Alchemy Client
Create the Alchemy client with your configuration:
```typescript
import { createModularAccountAlchemyClient } from "@alchemy/aa-alchemy";
import { WalletClientSigner } from "@alchemy/aa-core";
const walletClientSigner = new WalletClientSigner(viemClient, "para");
const alchemyClient = await createModularAccountAlchemyClient({
apiKey: ALCHEMY_API_KEY,
chain: arbitrumSepolia,
signer: walletClientSigner,
gasManagerConfig: {
policyId: ALCHEMY_GAS_POLICY_ID, // Optional: for gasless transactions
},
});
```
## Usage
### Creating UserOperations
UserOperations represent transaction intentions that will be executed by your smart account. Here's an example of
creating and executing a batch of UserOperations:
```typescript
import { encodeFunctionData } from "viem";
import { BatchUserOperationCallData, SendUserOperationResult } from "@alchemy/aa-core";
// Example contract ABI
const exampleAbi = [
{
inputs: [{ internalType: "uint256", name: "_x", type: "uint256" }],
name: "changeX",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
];
// Create batch operations
const demoUserOperations: BatchUserOperationCallData = [1, 2, 3, 4, 5].map((x) => ({
target: EXAMPLE_CONTRACT_ADDRESS,
data: encodeFunctionData({
abi: exampleAbi,
functionName: "changeX",
args: [x],
}),
}));
// Execute the operations
const userOperationResult: SendUserOperationResult = await alchemyClient.sendUserOperation({
uo: demoUserOperations,
});
// Optional: Wait for the operation to be included in a block
const txHash = await alchemyClient.waitForUserOperationTransaction({
hash: userOperationResult.hash,
});
```
UserOperations are bundled together and executed in a single transaction, making them more gas-efficient than
executing multiple separate transactions.
### Handling Gas Management
When using Alchemy's Gas Manager, transactions can be executed without requiring users to have ETH in their account:
```typescript
// Configuration with gas management
const alchemyClientWithGas = await createModularAccountAlchemyClient({
apiKey: ALCHEMY_API_KEY,
chain: arbitrumSepolia,
signer: walletClientSigner,
gasManagerConfig: {
policyId: ALCHEMY_GAS_POLICY_ID,
entryPoint: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789", // Optional: custom entryPoint
},
});
// Transactions will now be gasless for users
const gaslessOperation = await alchemyClientWithGas.sendUserOperation({
uo: demoUserOperations,
});
```
## Error Handling
When working with UserOperations, handle potential errors appropriately:
```typescript
try {
const result = await alchemyClient.sendUserOperation({
uo: demoUserOperations,
});
// Wait for transaction confirmation
const txHash = await alchemyClient.waitForUserOperationTransaction({
hash: result.hash,
timeout: 60000, // Optional: timeout in milliseconds
});
} catch (error) {
if (error.code === "USER_OPERATION_REVERTED") {
console.error("UserOperation reverted:", error.message);
} else if (error.code === "TIMEOUT") {
console.error("Operation timed out:", error.message);
} else {
console.error("Unexpected error:", error);
}
}
```
# Biconomy
Source: https://docs.getpara.com/web/guides/account-abstraction/biconomy
Learn how to integrate Biconomy SDK with a Para Signer for Account Abstraction and smart account management in your application.
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Introduction
The SDK is an Account Abstraction toolkit that enables
simple UX on your dApp, wallet or appchain. Built on top of the ERC 4337 solution for Account Abstraction, we offer a
full-stack solution for tapping into the power of our Smart Accounts Platform, Paymasters, and Bundlers.
## Getting Started
Get started with Biconomy at
## Connecting Biconomy to a Para Signer
### Dependencies
You will need the following dependencies to create a Smart Account:
```bash npm
npm install @biconomy/account @biconomy/bundler @biconomy/common @biconomy/core-types @biconomy/modules @biconomy/paymaster @getpara/react-sdk ethers@5.7.2
```
```bash yarn
yarn add @biconomy/account @biconomy/bundler @biconomy/common @biconomy/core-types @biconomy/modules @biconomy/paymaster @getpara/react-sdk ethers@5.7.2
```
### Connecting Biconomy with Para
```tsx Biconomy Integration
// Biconomy Imports
import { IPaymaster, BiconomyPaymaster } from "@biconomy/paymaster";
import { IBundler, Bundler } from "@biconomy/bundler";
import { BiconomySmartAccountV2, DEFAULT_ENTRYPOINT_ADDRESS } from "@biconomy/account";
import { Wallet, providers, ethers } from "ethers";
import { ChainId } from "@biconomy/core-types";
import { ECDSAOwnershipValidationModule, DEFAULT_ECDSA_OWNERSHIP_MODULE } from "@biconomy/modules";
CHAIN = ChainId.POLYGON_MUMBAI; // or any supported chain of your choice
// Set up instances of Bundler and Paymaster.
// Alternatively you can also use the Multi chain Module this way.
const bundler: IBundler = new Bundler({
// get from biconomy dashboard https://dashboard.biconomy.io/
bundlerUrl: "",
chainId: CHAIN,
entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS,
});
const paymaster: IPaymaster = new BiconomyPaymaster({
// get from biconomy dashboard https://dashboard.biconomy.io/
paymasterUrl: "",
});
// create para ethers signer
const provider = new ethers.JsonRpcProvider(CHAIN_PROVIDER, CHAIN);
const signer = new ParaEthersSigner(para, provider);
// create the biconomy smart account with a para ethers signer
const connect = async () => {
try {
const module = await ECDSAOwnershipValidationModule.create({
signer: signer,
moduleAddress: DEFAULT_ECDSA_OWNERSHIP_MODULE,
});
let biconomySmartAccount = await BiconomySmartAccountV2.create({
chainId: ChainId.POLYGON_MUMBAI,
bundler: bundler,
paymaster: paymaster,
entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS,
defaultValidationModule: module,
activeValidationModule: module,
});
const address = await biconomySmartAccount.getAccountAddress();
} catch (error) {
console.error(error);
}
};
```
For simplicity, Para imports are not included in the above example. It is assumed that the Para object has been
instantiated and the user has created a wallet.
# Overview
Source: https://docs.getpara.com/web/guides/account-abstraction/overview
An introduction to account abstraction and available integrations with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Account Abstraction (AA) is a groundbreaking technology in the blockchain space that aims to improve user experience and
enhance the functionality of blockchain applications. It allows for more flexible account types and transaction
handling, enabling features like gasless transactions, multi-signature wallets, transaction batching, and more.
Para supports integration with several leading Account Abstraction providers, allowing you to leverage the power of AA
in your applications using your provider of choice, all while maintaining the security and ease of use that Para offers.
## Available Integrations
Explore our guides for integrating Para with various Account Abstraction providers:
## Why Use Account Abstraction?
Account Abstraction offers several benefits for both developers and end-users:
1. **Improved User Experience**: Simplify onboarding and transactions for users who may not be familiar with blockchain
complexities.
2. **Gasless Transactions**: Allow users to interact with dApps without needing to hold native tokens for gas fees.
3. **Enhanced Security**: Implement features like multi-factor authentication and account recovery directly at the smart
contract level.
4. **Flexible Transaction Logic**: Customize how transactions are initiated, signed, and executed to fit your
application's needs.
5. **Programmable Accounts**: Create accounts with built-in rules and limitations, perfect for corporate or multi-user
scenarios.
By integrating Account Abstraction with Para, you can offer these advanced features while maintaining the robust
security and key management that Para provides.
## Getting Started
To get started with Account Abstraction using Para, choose one of the integrations above and follow the guide. Each
integration offers unique features and capabilities, so consider your project's specific needs when selecting a
provider.
Regardless of which AA provider you choose, Para ensures that your users' private keys remain secure and under their
control.
# Pimlico
Source: https://docs.getpara.com/web/guides/account-abstraction/pimlico
Learn how to integrate Pimlico's account abstraction infrastructure with a Para Signer using permissionless.js
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Introduction
is an account abstraction infrastructure platform. Leverage Pimlico's paymasters and bundlers to create smart accounts
for your application. Pimlico's core product is permissionless.js which is a TypeScript library built on viem for building
with ERC-4337 smart accounts, bundlers, paymasters, and user operations. Permissionless.js focuses on having no dependencies,
maximum viem compatibility and a small bundle size. Permissionless.js also supports the major ERC-4337 smart accounts including
Safe, Kernel, Biconomy, and SimpleAccount.
## Getting Started
Get started with Permissionless.js by following
. Follow these steps to create a
.
## Connecting Pimlico to a Para
After creating a Para signer, you will have access to a `ParaWeb3Provider` object that you can use to create a
`SmartAccountSigner` object
```tsx Pimlico Integration
import Para from "@getpara/web-sdk";
import { createParaViemClient } from "@getpara/viem-v2-integration";
import { walletClientToSmartAccountSigner } from "permissionless";
import { http } from "viem";
import { sepolia } from "viem/chains";
// Param options here will be specific to your project. See the Para docs for more info.
const para = new Para(env, apiKey);
// Convert a Para viem client to a SmartAccountSigner
// Follow the Para docs for more instructions on creating the Viem client https://docs.getpara.com/integration-guide/signing-transactions
const viemClient = createParaViemClient(para, {
chain: sepolia,
transport: http("https://rpc.ankr.com/eth_sepolia"),
});
const smartAccountSigner = walletClientToSmartAccountSigner(viemClient);
```
## Use with permissionless.js
```tsx Simple Account
import { signerToSimpleSmartAccount } from "permissionless/accounts"
import { createPublicClient, http } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
export const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_sepolia"),
chain: sepolia,
})
const smartAccount = await signerToSimpleSmartAccount(publicClient, {
signer: smartAccountSigner,
factoryAddress: "0x9406Cc6185a346906296840746125a0E44976454",
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
```
```tsx Safe Account
import { signerToSafeSmartAccount } from "permissionless/accounts"
import { createPublicClient, http } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
export const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_sepolia"),
chain: sepolia,
})
const smartAccount = await signerToSafeSmartAccount(publicClient, {
signer: smartAccountSigner,
safeVersion: "1.4.1",
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
```
```tsx Kernel Account
import { signerToEcdsaKernelSmartAccount } from "permissionless/accounts"
import { createPublicClient, http } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
export const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_sepolia"),
chain: sepolia,
})
const smartAccount = await signerToEcdsaKernelSmartAccount(publicClient, {
signer: smartAccountSigner,
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
```
```tsx Biconomy Account
import { signerToBiconomySmartAccount } from "permissionless/accounts"
import { createPublicClient, http } from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { sepolia } from "viem/chains"
export const publicClient = createPublicClient({
transport: http("https://rpc.ankr.com/eth_sepolia"),
chain: sepolia,
})
const smartAccount = await signerToBiconomySmartAccount(publicClient, {
signer: smartAccountSigner,
entryPoint: ENTRYPOINT_ADDRESS_V06,
})
```
# Safe
Source: https://docs.getpara.com/web/guides/account-abstraction/safe
Integrate Safe Smart Accounts with Para for enhanced wallet functionality
## Introduction
Safe Smart Accounts is a product by Safe (formerly Gnosis Safe) that enables the creation of ERC-4337-compatible smart
accounts for your users. This integration allows you to leverage the power of Account Abstraction and other Safe
features within your Para-powered application.
Safe Smart Accounts use the user's Externally Owned Account (EOA) as the smart account's signer. This builds upon the
robust smart contract infrastructure that powers the widely-used Safe wallet.
## Understanding EOAs and Smart Accounts
An EOA (Externally Owned Account) is an Ethereum account controlled by a private key. Para's embedded wallets and most external wallets (like MetaMask or Coinbase Wallet) are EOAs.
EOAs differ from contract accounts, which are controlled by smart contract code and don't have their own private key.
Safe's smart wallet is a contract account, offering enhanced capabilities such as gas sponsorship and batched
transactions.
In this integration:
* The user's EOA (from Para) serves as the signer for their smart wallet (from Safe).
* The smart wallet (Safe) holds all assets and submits transactions to the network.
* The signer (Para) is responsible for producing signatures and initiating transaction flows.
## Integration Steps
To create Safe smart accounts for your users with Para, follow these steps:
First, install the necessary dependencies:
```bash npm
npm install @getpara/viem-v2-integration viem @safe-global/protocol-kit @safe-global/api-kit @safe-global/safe-core-sdk-types
```
```bash yarn
yarn add @getpara/viem-v2-integration viem @safe-global/protocol-kit @safe-global/api-kit @safe-global/safe-core-sdk-types
```
Import the necessary modules in your project:
```typescript
import { createParaViemClient } from '@getpara/viem-v2-integration';
import { http, createPublicClient } from 'viem';
import { sepolia } from 'viem/chains';
import Safe, { EthersAdapter } from '@safe-global/protocol-kit';
import { SafeAccountConfig } from '@safe-global/safe-core-sdk-types';
```
Set up the Para VIEM client:
```typescript
const paraViemClient = createParaViemClient(para, {
chain: sepolia, // Replace with your desired chain
transport: http('https://rpc.sepolia.org'), // Replace with your RPC URL
});
```
Define the configuration for the Safe account:
```typescript
const safeAccountConfig: SafeAccountConfig = {
owners: [await paraViemClient.account.address],
threshold: 1,
};
```
Create an instance of the Safe SDK:
```typescript
const ethAdapter = new EthersAdapter({
ethers: paraViemClient,
signerOrProvider: paraViemClient.account,
});
const safeSdk = await Safe.create({ ethAdapter, safeAccountConfig });
```
Deploy the Safe smart account:
```typescript
const safeAddress = await safeSdk.getAddress();
const safeTransaction = await safeSdk.createTransaction({ safeTransactionData: [] });
const safeTxHash = await safeSdk.getTransactionHash(safeTransaction);
const senderSignature = await safeSdk.signTransactionHash(safeTxHash);
await safeSdk.executeTransaction(safeTransaction, senderSignature);
```
Now you can interact with the Safe smart account using the Para VIEM client:
```typescript
// Example: Send a transaction through the Safe smart account
const transaction = await paraViemClient.sendTransaction({
account: safeAddress,
to: '0x...', // Recipient address
value: 1000000000000000000n, // 1 ETH in wei
});
console.log('Transaction hash:', transaction);
```
# ZeroDev
Source: https://docs.getpara.com/web/guides/account-abstraction/zerodev
Learn how to integrate ZeroDev's embedded AA wallet with Para Signer
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
is an embedded AA wallet, currently powering many AA wallets deployed on EVM chains. ZeroDev is known for its large AA feature set, including not just gas sponsoring, but also session keys, recovery, multisig, .
## Installation
Install the required packages:
```bash npm
npm install @zerodev/sdk @zerodev/sdk/paymaster para-xyz viem
```
```bash yarn
yarn add @zerodev/sdk @zerodev/sdk/paymaster para-xyz viem
```
```bash bun
bun add @zerodev/sdk @zerodev/sdk/paymaster para-xyz viem
```
## Connecting ZeroDev to a Para Signer
Start by importing all the necessary packages:
```typescript
// ZeroDev imports
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator";
import { createKernelAccount, createKernelAccountClient, createZeroDevPaymasterClient } from "@zerodev/sdk";
import { getEntryPoint, KERNEL_V3_1 } from "@zerodev/sdk/constants";
// Para imports
// NOTE: These imports are available on web via the @getpara/web-sdk and or @getpara/react-sdk, but for Node.js or server-side environments, use the @getpara/server-sdk.
import { Para as ParaServer, Environment, hexStringToBase64, SuccessfulSignatureRes } from "@getpara/server-sdk";
import { createParaAccount, createParaViemClient } from "@getpara/viem-v2-integration";
// Viem imports
import { http, encodeFunctionData, hashMessage, SignableMessage, Hash, LocalAccount, WalletClient } from "viem";
import { arbitrumSepolia } from "viem/chains";
```
When working with Para MPC and AA providers, we need a function to handle the V value in signatures so that transactions can be validated correctly. This is due to the way Para handles signatures in its multi-party computation (MPC) setup. Below is the implementation of the `customSignMessage` function:
```typescript
// Helper function to convert hex to base64
function hexStringToBase64(hexString: string): string {
const bytes = new Uint8Array(hexString.length / 2);
for (let i = 0; i < hexString.length; i += 2) {
bytes[i / 2] = parseInt(hexString.substring(i, i + 2), 16);
}
return btoa(String.fromCharCode.apply(null, Array.from(bytes)));
}
// Custom sign message function for Para MPC
async function customSignMessage(para: ParaServer, message: SignableMessage): Promise {
const wallet = para.wallets ? Object.values(para.wallets)[0] : null;
const hashedMessage = hashMessage(message);
const messagePayload = hashedMessage.startsWith("0x") ? hashedMessage.substring(2) : hashedMessage;
const messageBase64 = hexStringToBase64(messagePayload);
const res = await para.signMessage({
walletId: wallet.id,
messageBase64: messageBase64,
});
let signature = res.signature;
// Adjust the last byte for the correct V value
const vHex = signature.slice(-2);
const v = parseInt(vHex, 16);
if (!isNaN(v) && v < 27) {
const adjustedVHex = (v + 27).toString(16).padStart(2, "0");
signature = signature.slice(0, -2) + adjustedVHex;
}
return `0x${signature}`;
}
```
Set up your Para account and Viem client:
```typescript
// Create a Para Viem account
const viemParaAccount: LocalAccount = createParaAccount(para);
// Override the sign message function with our custom implementation
viemParaAccount.signMessage = async ({ message }) => customSignMessage(para, message);
// Create a Viem client with the Para account
const viemClient: WalletClient = createParaViemClient(para, {
account: viemParaAccount,
chain: arbitrumSepolia,
transport: http("https://sepolia-rollup.arbitrum.io/rpc"), // Replace with your RPC URL
});
```
Ensure you override the `signMessage` method of the `LocalAccount` instance with the `customSignMessage` function. This is crucial for ensuring that the V value in the signature is handled correctly when sending transactions through ZeroDev.
Configure the ZeroDev kernel account with the ECDSA validator:
```typescript
// Set up kernel and validator configurations
const entryPoint = getEntryPoint("0.7");
const kernelVersion = KERNEL_V3_1;
// Create ECDSA validator using your Para account
const ecdsaValidator = await signerToEcdsaValidator(viemClient, {
signer: viemParaAccount,
entryPoint,
kernelVersion,
});
// Create kernel account with the validator
const account = await createKernelAccount(viemClient, {
plugins: { sudo: ecdsaValidator },
entryPoint,
kernelVersion,
});
```
Create the paymaster and kernel client to handle transactions:
For more information on setting up RPCs and managing your ZeroDev project, refer to the .
```typescript
// Replace these with your ZeroDev RPC URLs from the ZeroDev Dashboard
const bundlerRpc = "https://your-bundler-rpc-url.zerodev.app";
const paymasterRpc = "https://your-paymaster-rpc-url.zerodev.app";
// Set up ZeroDev paymaster
const zerodevPaymaster = createZeroDevPaymasterClient({
chain: arbitrumSepolia,
transport: http(paymasterRpc),
});
// Create kernel account client with the paymaster
const kernelClient = createKernelAccountClient({
account,
chain: arbitrumSepolia,
bundlerTransport: http(bundlerRpc),
paymaster: {
getPaymasterData: (userOperation) =>
zerodevPaymaster.sponsorUserOperation({ userOperation }),
},
});
```
Now you can use the kernel client to send user operations:
```typescript
// Example of sending a transaction
const hash = await kernelClient.sendUserOperation({
userOperation: {
callData: account.encodeCallData({
to: "0xYourTargetAddress", // Replace with your target address
data: "0xYourFunctionData", // Replace with your function data
value: 0n // Replace with your value if needed
})
}
});
// Wait for the transaction to be mined
const transactionHash = await kernelClient.waitForUserOperationReceipt({ hash });
console.log("Transaction mined:", transactionHash);
```
The examples above assume that you have already initialized the Para object and created a wallet. For more details on Para initialization, refer to the .
## Example
For an example of using ZeroDev with Para Signer, you can refer to the following GitHub repository:
# Para with CosmJS
Source: https://docs.getpara.com/web/guides/cosmos/cosmjs
Learn how to integrate Para with Cosmos using @getpara/cosmjs-v0-integration
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide demonstrates how to use Para with CosmJS for Cosmos blockchain integration. You'll learn how to set up the
integration, perform basic operations, and handle transactions using the `@getpara/cosmjs-v0-integration` package.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Install the required packages using your preferred package manager:
```bash npm
npm install @getpara/cosmjs-v0-integration @cosmjs/stargate
```
```bash pnpm
pnpm add @getpara/cosmjs-v0-integration @cosmjs/stargate
```
```bash yarn
yarn add @getpara/cosmjs-v0-integration @cosmjs/stargate
```
```bash bun
bun add @getpara/cosmjs-v0-integration @cosmjs/stargate
```
## Setup
For basic setup pass your Para instance and the address prefix to the `ParaProtoSigner`. Once created pass the signer to
the `SigningStargateClient`.
You can also setup a `ParaAminoSigner` if you want to use Amino signing instead of Proto signing. The usage is similar, just replace `ParaProtoSigner` with `ParaAminoSigner`.
```typescript
import { ParaProtoSigner, createTestTransaction } from "@getpara/cosmjs-v0-integration";
import { SigningStargateClient } from "@cosmjs/stargate";
// Create the Para Proto Signer
// 'prefix' is optional and used for non-cosmos addresses (e.g., 'celestia' for celestia1... addresses)
const signer = new ParaProtoSigner(para, "cosmos");
// Connect to the Cosmos network
const rpcUrl = "https://rpc.cosmos.network"; // Replace with your preferred RPC endpoint
const client = await SigningStargateClient.connectWithSigner(rpcUrl, signer);
// The test transaction sends 0.01 testnet ATOM to your own wallet
const signDoc = await createTestTransaction(para);
const { signature } = await protoSigner.signAmino(signDoc);
```
Once setup you can using the `SigningStargateClient` as you would normally with CosmJS. You can reference the for more details.
## Basic Usage
### Get Address and Balance
```typescript
// Get the signer's address
const address = protoSigner.address;
console.log("Signer address:", address);
// Query account balances
const balance = await client.getAllBalances(address);
console.log("Balance:", balance);
```
### Send Tokens
For sending a transaction you can construct a transaction object and call the `sendTokens` method:
```typescript
const recipient = "cosmos1..."; // Replace with actual recipient address
const amount = {
denom: "uatom",
amount: "1000000", // 1 ATOM
};
const fee = {
amount: [{ denom: "uatom", amount: "500" }],
gas: "200000",
};
try {
const result = await client.sendTokens(protoSigner.address, recipient, [amount], fee);
console.log("Transaction hash:", result.transactionHash);
} catch (error) {
console.error("Error sending transaction:", error);
}
```
### Advanced Transaction Signing
Sometimes you want more control over the transaction and not send the transaction directly. You can construct any valid
message and sign it manually before broadcasting:
```typescript
import { StdFee, Coin, MsgSendEncodeObject } from "@cosmjs/stargate";
import { MsgSend } from "cosmjs-types/cosmos/bank/v1beta1/tx";
// Prepare transaction details
const amount: Coin = {
denom: "uatom",
amount: "1000000",
};
const fee: StdFee = {
amount: [{ denom: "uatom", amount: "500" }],
gas: "200000",
};
// Create the message
const message: MsgSend = {
fromAddress: protoSigner.address,
toAddress: "cosmos1...", // recipient address
amount: [amount],
};
const sendMessage: MsgSendEncodeObject = {
typeUrl: "/cosmos.bank.v1beta1.MsgSend",
value: message,
};
// Sign the transaction
const memo = "Signed with Para";
const signedTx = await client.sign(protoSigner.address, [sendMessage], fee, memo);
// Broadcast the transaction
const result = await client.broadcastTx(signedTx);
console.log("Transaction result:", result);
```
## Multi-Chain Support
To use Para with different Cosmos-based chains, specify the appropriate address prefix and RPC endpoint when creating signers and clients:
```typescript
// For Osmosis
const osmosisRPC = "https://osmosis-rpc.polkachu.com";
const osmosisSigner = new ParaProtoSigner(para, "osmo");
const osmosisClient = await SigningStargateClient.connectWithSigner(osmosisRPC, osmosisSigner);
```
Each client will be configured with the appropriate signer for its respective chain, allowing you to interact with multiple Cosmos networks in the same application.
## Server-Side Signing
Para's signers can also be used on the server-side using pregen wallets or an active client side session. To learn more about using para on the server, check out these guides:
## Examples
If you'd like to learn more about how to use the `ParaProtoSigner` or the `ParaAminoSigner` for different transaction types, check out this example in our Examples Hub:
# Overview
Source: https://docs.getpara.com/web/guides/cosmos/overview
An overview of Cosmos integrations supported by Para, including CosmJS and direct integration options, along with adapters for Cosmos Kit, and Graz.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para simplifies building on the Cosmos ecosystem by providing flexible integration options that work with your existing stack. Whether you're developing a new dApp or enhancing an existing one, our Cosmos integrations offer seamless connectivity with Para's secure account and transaction management capabilities.
The following integration option is currently available:
Our Graz and CosmosKit connectors are currently in alpha development. Reach out to us if you're interested in early access to these integration options.
# Custom Chains
Source: https://docs.getpara.com/web/guides/custom-chains
Learn how to use custom EVM, SVM, or Cosmos chains in your application
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para supports a bring-your-own RPC model. This makes it easy to use Para with any network (at any stage), from internal
devnets to any major mainnet network.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Ethers
For Ethers, just provide the RPC URL for your desired chain
```typescript
import { ParaEthersSigner } from "@getpara/ethers-v6-integration";
import { ethers } from "ethers";
import Para from "@getpara/web-sdk";
// Initialize Para
const para = new Para(Environment.BETA, YOUR_API_KEY);
// Set up the provider
const provider = new ethers.JsonRpcProvider(YOUR_RPC_URL);
// Create the Para Ethers Signer
const ethersSigner = new ParaEthersSigner(para, provider);
```
## Viem
To configure a custom chain, just add a new `Chain` type as follows
```typescript
// source: https://wagmi.sh/core/api/chains
import { defineChain } from "viem";
export const mainnet = defineChain({
id: 1,
name: "Ethereum",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
rpcUrls: {
default: { http: ["https://cloudflare-eth.com"] },
},
blockExplorers: {
default: { name: "Etherscan", url: "https://etherscan.io" },
},
contracts: {
ensRegistry: {
address: "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
},
ensUniversalResolver: {
address: "0xE4Acdd618deED4e6d2f03b9bf62dc6118FC9A4da",
blockCreated: 16773775,
},
multicall3: {
address: "0xca11bde05977b3631167028862be2a173976ca11",
blockCreated: 14353601,
},
},
});
// Create the Para Viem Client
const paraViemSigner = createParaViemClient(para, {
account: viemParaAccount,
chain: mainnet,
transport: http("https://cloudflare-eth.com"),
});
```
This chain configuration can now be passed to a viem provider
## Wagmi
If you're also already using , you can easily extend
`@wagmi/chains` and automatically use Para
signers directly with your desired set of supported chains
```typescript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { paraConnector } from "@getpara/wagmi-v2-integration";
import { createConfig, WagmiProvider, type CreateConfigParameters } from "wagmi";
import { http } from "wagmi";
import { sepolia, mainnet } from "wagmi/chains";
import { para } from "./para"; // Your Para client initialization
// Create Para connector
const chains = [sepolia, mainnet];
// see wagmi integration guide for full config options
const connector = paraConnector({
para: para,
chains, // Add your supported chains
appName: "Your App Name",
options: {},
nameOverride: "Para",
idOverride: "para",
});
// Configure Wagmi
const config: CreateConfigParameters = {
chains: chains,
connectors: [connector],
transports: {
[(sepolia.id, mainnet.id)]: http(),
},
};
const wagmiConfig = createConfig(config);
const queryClient = new QueryClient();
```
## Solana-Web3.js
Instantiate the Solana-Web3,js signer with your network's RPC URL of choice
```typescript
import { ParaSolanaWeb3Signer } from "@getpara/solana-web3.js-v1-integration";
import { Connection, clusterApiUrl } from "@solana/web3.js";
import { para } from "./para"; // Your Para client initialization
// Set up SVM connection
const solanaConnection = new Connection(YOUR_RPC_URL);
// Create the Para Solana Signer
const solanaSigner = new ParaSolanaWeb3Signer(para, solanaConnection);
```
## CosmJS
Initialize the signing client with your RPC URL of choice
```typescript
import { ParaProtoSigner } from "@getpara/cosmjs-v0-integration";
import { SigningStargateClient } from "@cosmjs/stargate";
// Create the Para Proto Signer
// 'prefix' is optional and used for non-cosmos addresses (e.g., 'celestia' for celestia1... addresses)
const protoSigner = new ParaProtoSigner(para, "cosmos");
// Connect to the Cosmos network
const rpcUrl = YOUR_RPC_URL; // Replace with your preferred RPC endpoint
const client = await SigningStargateClient.connectWithSigner(rpcUrl, protoSigner);
```
# Two-Factor Authentication
Source: https://docs.getpara.com/web/guides/custom-ui/2fa
Implementing secure wallet recovery with Two-Factor Authentication in Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para supports Two-Factor Authentication (2FA) to enhance wallet recovery security by requiring users to provide two different authentication factors when verifying their identity.
## Implementation Options
There are two ways to implement 2FA with Para:
1. **Using ParaModal (Recommended)**: The simplest approach with 2FA built-in and a complete user interface.
2. **Custom Implementation**: For applications with custom UIs, manually implement 2FA using Para's methods as described below.
The ParaModal enables 2FA by default. See the if you need to disable this feature.
## Custom Implementation Process
Follow these steps to implement 2FA in your custom UI:
### 1. Check 2FA Status
Begin by checking if 2FA is already enabled for the user:
```typescript
const checkTwoFactorStatus = async () => {
const { isSetup } = await para.check2FAStatus();
if (isSetup) {
// 2FA is already set up - proceed to verification if needed
} else {
// 2FA needs to be set up - proceed to setup flow
}
};
```
### 2. Set Up 2FA
If 2FA isn't configured, initiate the setup process:
```typescript
const setupTwoFactor = async () => {
const { uri } = await para.setup2FA();
if (uri) {
// Display the QR code to the user or provide the secret for manual entry
} else {
// Handle case where no URI is returned
}
};
```
The returned `uri` can be used to generate a QR code or extracted to provide the secret key. Most authenticator apps
(like Google Authenticator, Authy, or Microsoft Authenticator) can scan this QR code directly.
### 3. Enable 2FA
After the user has added the 2FA secret to their authenticator app, verify and enable 2FA:
```typescript
const enableTwoFactor = async (verificationCode: string) => {
await para.enable2FA({ verificationCode });
// Notify the user of successful 2FA setup
};
```
### 4. Verify 2FA
When the user authenticates, verify their 2FA code:
```typescript
const verifyTwoFactor = async (email: string, verificationCode: string) => {
await para.verify2FA({ email, verificationCode });
// Proceed with the authentication process
};
```
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Email Login
Source: https://docs.getpara.com/web/guides/custom-ui/email-login
Implementing custom email authentication with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para's SDK enables you to implement custom email-based authentication flows in your application. This guide demonstrates how to create a complete email verification and wallet creation flow with your own UI components.
It's not recommended to create a custom UI but instead use Para's `ParaModal` component for a fully managed authentication experience. However, if you need to implement a custom flow, this guide will help you set it up.
## Implementation Process
### 1. Check User Status
First, determine if the user already exists in your system:
```typescript
const checkUserExists = async (email: string) => {
const isExistingUser = await para.checkIfUserExists({ email });
if (isExistingUser) {
// Proceed with login flow for existing user
} else {
// Proceed with signup flow for new user
}
};
```
### 2. Handle Existing User Login
For existing users, initiate the login process:
```typescript
const loginExistingUser = async (email: string) => {
// Generate authentication URL for login
const webAuthUrlForLogin = await para.initiateUserLogin({
email,
useShortUrl: false
});
// Open authentication popup
const popupWindow = window.open(webAuthUrlForLogin, "loginPopup", "popup=true");
// Wait for login completion and wallet setup
const { needsWallet } = await para.waitForLoginAndSetup({ popupWindow });
// Create wallet if needed
if (needsWallet) {
await para.createWallet({ type: WalletType.EVM, skipDistribute: false });
}
// Get user wallet information
const wallets = Object.values(await para.getWallets());
};
```
### 3. Handle New User Registration
For new users, start the registration process:
```typescript
const registerNewUser = async (email: string) => {
// Create new user with provided email
await para.createUser({ email });
// User will receive verification email
// Proceed to verification step
};
```
### 4. Verify Email and Complete Setup
After the user receives the verification code, complete the setup:
```typescript
const verifyAndCreateWallet = async (verificationCode: string) => {
// Verify email with code from user's inbox
const setupUrl = await para.verifyEmail({ verificationCode });
if (setupUrl) {
// Open popup for passkey creation
const popupWindow = window.open(setupUrl, "signUpPopup", "popup=true");
// Wait for passkey setup and wallet creation
await para.waitForPasskeyAndCreateWallet();
// Get user wallet information
const wallets = Object.values(await para.getWallets());
}
};
```
## Example
For a complete working example of custom email authentication with Para, check out our sample project:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Overview
Source: https://docs.getpara.com/web/guides/custom-ui/overview
Use Para to create a custom authentication UI
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
If you're looking to have a more unified experience with your own UI, the Para SDKs allow you to directly call methods
allowing you to create your own custom UI. This is an advanced scenario where you will be responsible for the UI and UX
of your application.
Creating a Custom UI is not the recommended path for most applications. We recommend you use the `ParaModal` as it
managed all of the UI and UX for you, as well as being fully brandable.
## Authentication Options
Para provides several authentication methods that you can implement with your custom UI:
When creating a custom-ui you will need to handle the full authentication flow, including error handling and user feedback.
The Para SDKs provide methods to help you manage these flows, but you will need to implement the UI components yourself.
# Phone Login
Source: https://docs.getpara.com/web/guides/custom-ui/phone-login
Implementing custom phone number authentication with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Introduction
Para supports phone number authentication, allowing users to verify their identity using SMS verification codes. This guide demonstrates how to implement a custom phone authentication flow with your own UI components.
## Implementation Process
Follow these steps to implement custom phone authentication:
### 1. Check User Status
First, determine if the user already exists in your system:
```typescript
const checkUserExists = async (phoneNumber: string) => {
const isExistingUser = await para.checkIfUserExists({ phoneNumber });
if (isExistingUser) {
// Proceed with login flow for existing user
} else {
// Proceed with signup flow for new user
}
};
```
### 2. Handle Existing User Login
For existing users, initiate the login process:
```typescript
const loginExistingUser = async (phoneNumber: string) => {
// Request SMS verification code to be sent
await para.initiateUserLogin({ phoneNumber });
// Proceed to verification step
};
```
### 3. Handle New User Registration
For new users, start the registration process:
```typescript
const registerNewUser = async (phoneNumber: string) => {
// Create new user with provided phone number
await para.createUser({ phoneNumber });
// SMS verification code will be sent
// Proceed to verification step
};
```
### 4. Verify Phone Number and Complete Setup
After the user receives the SMS verification code:
```typescript
const verifyAndCreateWallet = async (phoneNumber: string, verificationCode: string) => {
try {
// For new users:
const setupUrl = await para.verifyPhoneNumber({
phoneNumber,
verificationCode
});
if (setupUrl) {
// Open popup for passkey creation
const popupWindow = window.open(setupUrl, "signUpPopup", "popup=true");
// Wait for passkey setup and wallet creation
await para.waitForPasskeyAndCreateWallet();
}
// For existing users:
// const { needsWallet } = await para.verifyPhoneAndLogin({
// phoneNumber,
// verificationCode
// });
//
// if (needsWallet) {
// await para.createWallet({ type: WalletType.EVM, skipDistribute: false });
// }
// Get user wallet information
const wallets = Object.values(await para.getWallets());
} catch (error) {
// Handle verification errors
}
};
```
## Example
For a complete working example of custom email authentication with Para, check out our sample project:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# OAuth Social Logins
Source: https://docs.getpara.com/web/guides/custom-ui/social-logins
Add social login options to your Para integration
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para supports various OAuth methods to authenticate users across different platforms. Currently supported platforms
include Google, Facebook, Discord, Twitter, Telegram, Farcaster, and Apple.
Para automatically links wallets with a common email identifier to avoid creating duplicate wallets if users choose
different methods in different sessions. Because of this, **users must have an email associated with their login
account of choice**. Without an email linked to the account, authentication will fail and the user will see a prompt
to either add email to their social account or try a different method.
## Integrating Social Logins
You have two options when for using social logins with Para. You can use the Para Modal and simply pass in your desired
OAuth methods, or you can use the SDK to manually control the OAuth process and create your own custom UI.
### Para Modal
Social Logins can be easily integrated via the `ParaModal` component by passing the desired options as an array to the
`oAuthMethods` property:
```tsx
import { ParaModal, OAuthMethod } from "@getpara/web-sdk";
const para = new Para(Environment.BETA, YOUR_API_KEY);
;
```
You can just use `[Object.values(OAuthMethod)]` to get all the available OAuth methods as an array.
### Custom OAuth Implementation
For developers implementing their own authentication UI, Para provides methods to handle OAuth authentication and
subsequent wallet creation.
```typescript
import { OAuthMethod } from "@getpara/web-sdk";
const handleOAuthAuthentication = async (method: OAuthMethod) => {
const oAuthURL = await para.getOAuthURL(method);
window.open(oAuthURL, "oAuthPopup", "popup=true");
const { email, userExists } = await para.waitForOAuth();
const authUrl = userExists
? await para.initiateUserLogin({ email, useShortUrl: false })
: await para.getSetUpBiometricsURL({ authType: "email", isForNewDevice: false });
const popupWindow = window.open(authUrl, userExists ? "loginPopup" : "signUpPopup", "popup=true");
const result = await (userExists ? para.waitForLoginAndSetup({ popupWindow }) : para.waitForPasskeyAndCreateWallet());
if ("needsWallet" in result && result.needsWallet) {
await para.createWallet({ skipDistribute: false });
}
if ("recoverySecret" in result) {
const recoverySecret = result.recoverySecret;
// Store or handle recovery secret as needed
}
};
```
This implementation provides a complete OAuth flow, from initial authentication through wallet creation and recovery
secret generation. For best practice and ease of use, you can split the code into separate functions for each new user
creation flow, login flow, and wallet creation flow.
**Beta Testing Email** In the `BETA` Environment, you can use any email ending in `@test.getpara.com` (like
[dev@test.getpara.com](mailto:dev@test.getpara.com)). Any OTP code will work for verification with these test emails. These credentials are for beta
testing only. You can delete test users anytime in the beta developer console to free up user slots.
Farcaster authentication follows a similar pattern but uses Farcaster-specific methods. Here's how to implement a complete Farcaster authentication flow:
```typescript
const handleFarcasterAuthentication = async () => {
const connectUri = await para.getFarcasterConnectURL();
window.open(connectUri, "farcasterConnectPopup", "popup=true");
const { userExists, username } = await para.waitForFarcasterStatus();
const authUrl = userExists
? await para.initiateUserLogin({ email: username, useShortUrl: false })
: await para.getSetUpBiometricsURL({ authType: "farcaster", isForNewDevice: false });
const popupWindow = window.open(authUrl, userExists ? "loginPopup" : "signUpPopup", "popup=true");
await (userExists ? para.waitForLoginAndSetup({ popupWindow }) : para.waitForPasskeyAndCreateWallet());
};
```
### Telegram OAuth Login
To implement your own Telegram authentication flow, please refer to the
[official documentation](https://core.telegram.org/widgets/login). Para uses the following bots to handle authentication
requests:
| Environment | Username | Bot ID |
| ----------- | ----------------------------------------------------------- | ---------- |
| `BETA` | [@para\_oauth\_beta\_bot](https://t.me/para_oauth_beta_bot) | 7788006052 |
| `PROD` | [@para\_oauth\_bot](https://t.me/para_oauth_bot) | 7643995807 |
When you receive an authentication payload, you can use `para.verifyTelegram()` to validate the payload hash and either
find or create the Para user for that Telegram user ID, all in one step. As with Farcaster, these users will not have an
associated email or phone number and will thereafter need to use Telegram to sign in to your app.
```javascript
// Example payload:
// {
// id: 123456789,
// first_name: 'John',
// last_name: 'Doe',
// username: 'johndoe',
// photo_url: 'https://example.com/photo.jpg',
// auth_date: Date.now(),
// hash: '...',
// }
// Wait for the user to be authenticated via the OAuth method through Para.
const result = await para.verifyTelegram(payload);
if (!result.isValid) {
// Handle auth failure
} else {
const { telegramUserId, userId: paraUserId, isNewUser, supportedAuthMethods, biometricHints } = result;
if (isNewUser || supportedAuthMethods.length === 0) {
// Create sign-in methods for user if they don't have any
const createPasskeyUrl = await para.getSetUpBiometricsURL({ authType: "telegram", isForNewDevice: false });
} else if (supportedAuthMethods.includes(AuthMethod.PASSKEY)) {
// Log in previously created user
const loginUrl = await para.initiateUserLogin({ useShortUrl: false, email: telegramUserId });
}
}
```
With these implementations, you can seamlessly integrate OAuth social logins into your application with your own custom
UI.
## Account Metadata
To fetch account metadata for a particular user from their most recent OAuth login on your app - for example, their
Google profile image or their Discord clan information - use the `getAccountMetadata` method. This returns an object,
indexed by the third-party service name, containing the date of the login and the metadata returned from the service at
that time.
```ts
type AccountMetadataKey = "google" | "x" | "discord" | "apple" | "facebook" | "telegram" | "farcaster";
type AccountMetadata = Partial<
Record<
AccountMetadataKey,
{
date: string;
metadata: any;
}
>
>;
const para = new Para(Environment.BETA, YOUR_API_KEY);
async function fetchAccountMetadata(): Promise {
const accountMetadata: AccountMetadata = await para.getAccountMetadata();
return accountMetadata;
}
```
Metadata will only be returned for services that the user authenticated with your app specifically. The particular
structure of the metadata will depend on the service in question. A sample response might resemble:
```json
{
"discord": {
"date": "2022-01-01T00:00:00Z",
"metadata": {
"id": "80351110224678912",
"username": "User",
"discriminator": "1337",
"avatar": "8342729096ea3675442027381ff50dfe",
"verified": true,
"email": "user@email.com",
"flags": 64,
"banner": "06c16474723fe537c283b8efa61a30c8",
"accent_color": 16711680,
"premium_type": 1,
"public_flags": 64,
"avatar_decoration_data": {
"sku_id": "1144058844004233369",
"asset": "a_fed43ab12698df65902ba06727e20c0e"
}
}
},
"google": {
"date": "2024-11-01T00:00:00Z",
"metadata": {
"id": "000000000000000000000",
"emails": [
{
"value": "test@email.com",
"verified": true
}
],
"photos": [
{
"value": "https://.../photo.jpg"
}
],
"provider": "google"
}
},
"x": {
"date": "2025-01-06T12:34:56Z",
"metadata": {
"id": 171290000000000500,
"url": null,
"lang": null,
"name": "Test User",
"email": "user@email.com",
"id_str": "171290000000000500",
"entities": {
"description": {
"urls": []
}
},
"location": "",
"verified": false,
"following": false,
"protected": false,
"suspended": false,
"time_zone": null,
"created_at": "Fri Oct 13 19:11:38 +0000 2023",
"utc_offset": null,
"description": "",
"geo_enabled": false,
"screen_name": "TheirXUsername",
"listed_count": 0,
"friends_count": 0,
"is_translator": false,
"notifications": false,
"statuses_count": 0,
"default_profile": true,
"followers_count": 0,
"translator_type": "none",
"favourites_count": 0,
"profile_image_url": "http://pbs.twimg.com/profile_images/171290000000000500/hbDmgt_J_normal.png",
"profile_link_color": "1DA1F2",
"profile_text_color": "333333",
"follow_request_sent": false,
"contributors_enabled": false,
"has_extended_profile": true,
"default_profile_image": false,
"withheld_in_countries": [],
"is_translation_enabled": false,
"profile_background_tile": false,
"profile_image_url_https": "https://pbs.twimg.com/profile_images/171290000000000500/hbDmgt_J_normal.png",
"needs_phone_verification": false,
"profile_background_color": "F5F8FA",
"profile_sidebar_fill_color": "DDEEF6",
"profile_background_image_url": null,
"profile_sidebar_border_color": "C0DEED",
"profile_use_background_image": true,
"profile_background_image_url_https": null
}
}
}
```
## Examples
For an example code implementation of the above custom OAuth methods check out this simple **React + Next.js**
application:
You'll also find custom Phone and Email authentication examples in the same repository.
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Fiat On and Off-Ramps
Source: https://docs.getpara.com/web/guides/customization/fiat-onramps
Enable crypto purchase and selling flows in your Para Modal implementation
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Configure fiat on-ramp (buying crypto) and off-ramp (selling crypto) functionality in your Para Modal implementation
using ,
, and
providers. Enable seamless crypto transactions within your application.
## Setup On-Ramps
### Developer Portal Settings
To start configure your on-ramp and off-ramp settings in the
. You can find the configuration settings under **Projects → API Key → Configuration**.
### On-Ramp Flows
Configure what options your users will see for on- or off-ramping of funds.
### On-Ramp Configuration
#### On-Ramp Providers
Configure which providers to display in your modal. Providers are displayed in the order set, subject to asset
availability and wallet compatibility.
#### On-Ramp Assets
Specify which assets to offer in your modal. Users will be limited to purchasing or selling only the configured assets
through enabled providers.
#### Default Values
Set the default asset and amount that will be pre-populated when users initiate crypto purchases or sales.
### Modal Configuration
While in development or beta you can enable test mode for your on-ramp configuration. This will allow you to test the
on-ramp flow without making actual purchases.
```tsx App.tsx
function App() {
return ( ); }
```
## Provider Setup
Lastly Ramp and Stripe require additional setup to enable on-ramp and off-ramp functionality.
### Ramp Integration
Obtain a production Ramp API key and configure it in the Para Developer Portal. For detailed instructions, see the
[Ramp API Key Documentation](https://docs.ramp.network/api-keys).
### Stripe Integration
Add the required Stripe scripts to your application's HTML head:
```html index.html
Your Application
```
## Additional Resources
For more information, refer to the following resources:
*
*
*
*
# Modal Customization
Source: https://docs.getpara.com/web/guides/customization/modal
Learn how to customize the appearance of the Para modal to match your application's design.
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
The Para Modal is designed to be easily customizable, allowing you to adapt its appearance to fit your application's
design. UI and theming options are props that can be passed to the `ParaModal` component. These props allow you to
configure the modal's appearance, authentication options, and security features.
## Modal Configuration
### Required Props
A few props are required to ensure the modal displays correctly. These include the `appName` and `logo` props, which are
essential for branding and user recognition.
```tsx
```
For optimal display, use a logo image with dimensions of 372px X 160px.
I'll add a section about the `currentStepOverride` prop in the Modal Configuration section, after the Required Props
section. Here's how I would add it:
### Wallet Balance
This prop allow the passing of an RPC URL from which the balance will be fetched and displayed for the selected wallet.
Specify the RPL URL where the wallet balance should be fetched from.
```tsx
```
Balance display is currently only available for EVM wallets.
### Step Override
The `currentStepOverride` prop allows you to control which step of the modal flow is displayed when the modal opens.
This is useful when you want to skip directly to a specific step rather than starting from the beginning of the flow.
Override the initial step shown in the modal. Accepts either uppercase or lowercase step names.
```tsx
```
Available steps include:
* `AUTH_MAIN` - Main authentication options
* `AUTH_MORE` - Additional authentication methods
* `EX_WALLET_MORE` - External wallet options
* `EX_WALLET_SELECTED` - Selected external wallet
* `VERIFICATIONS` - Verification steps
* `BIOMETRIC_CREATION` - Biometric setup
* `PASSWORD_CREATION` - Password creation
* `SECRET` - Recovery secret display
* `ACCOUNT_MAIN` - Main account view
* `ADD_FUNDS_BUY` - Buy crypto view
* `ADD_FUNDS_RECEIVE` - Receive crypto view
* `CHAIN_SWITCH` - Chain selection view
And many more steps for specific flows like OAuth, 2FA setup, and funds management.
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.
### Authentication Options
The authentication options allow you to control the authentication methods available to users. You can enable or disable
email and phone login, as well as specify the order of OAuth methods.
Specify OAuth methods to display. The order of methods in the array determines their display order in the modal.
Disable email login if set to true
Disable phone login if set to true
```tsx
```
### Security Features
Configure additional security steps in the Para modal by setting `twoFactorAuthEnabled` and `recoverySecretStepEnabled`
flags. When enabled, these flags add their respective authentication steps to the modal's UI flow. Two-factor
authentication adds a verification code step, while the recovery secret step allows users to download and store their
account recovery credentials.
Enable two-factor authentication steps
Show wallet recovery option to users
```tsx
```
### External Wallets
This prop allows you to specify which external wallets are supported for login. The order of wallets in the array
determines their display order in the modal.
Specify external wallets to support. The order of wallets in the array determines their display order.
```tsx
```
For more details on external wallet integration in the Para modal, refer to the{" "}
### Auth Layout
The `authLayout` prop can expand or collapse the view of OAuth methods and external wallets. Only one of `Auth:` or
`External:` can be passed into the array, either `CONDENSED` or `FULL` UI versions of each. The order of the array
determines the order that the components will be displayed in the modal.
Configure the layout of auth components
```tsx
```
Use our{" "}
{" "}
to visualize different layout configurations before implementing them in your project.
## Theming
You can customize the overall look of the Para modal by providing a theme object with the following properties all of
which are optional:
```typescript
interface ParaTheme {
foregroundColor?: string; // Primary text and icon color
backgroundColor?: string; // Background color of the modal
accentColor?: string; // Color for interactive elements and highlights
darkForegroundColor?: string; // Foreground color for dark mode
darkBackgroundColor?: string; // Background color for dark mode
darkAccentColor?: string; // Accent color for dark mode
mode?: "light" | "dark"; // Set the overall theme mode
borderRadius?: BorderRadius; // Set the border radius for UI elements
font?: string; // Specify the font family to use
}
```
It's crucial to set the `mode` correctly based on your background color to ensure all components remain visible.
An example of a basic theme object is as follows:
```tsx
```
You can use custom fonts by importing them in your global CSS and specifying the font face in the `font` variable of
the theme.
### Advanced Theme Customization
The para modal also offers more granular control over specific UI elements through the `customPalette` property. This
allows you to define colors for various components, ensuring that the modal aligns perfectly with your application's
design language.
```tsx
```
By leveraging these theming options, you can ensure that the Para modal seamlessly integrates with your application's
design language, providing a cohesive user experience.
## Examples
To see what all of these props look like in practice, check out our live demo:
For an example code implementation of Para Modal with all the props mentioned in this guide, check out our GitHub
repository:
## Next Steps
Now that you have customized the Para modal to fit your application's design, you can explore other features and
integrations to enhance your Para experience. Here are some resources to help you get started:
### Ecosystems
Learn how to use Para with popular Web3 clients and wallet connectors. We'll cover integration with key libraries for
EVM, Solana, and Cosmos ecosystems.
# Overview
Source: https://docs.getpara.com/web/guides/customization/overview
Learn how to customize Para to match your brand and user needs.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para offers extensive customization options to help you create a seamless, branded authentication experience for your
users. These customizations are managed through two main interfaces - the Para Developer Portal for core platform
settings, and the Para Modal for frontend customization. Together, these enable you to control everything from the
visual appearance to authentication methods, security features, and third-party integrations.
## Dev Portal Customization
The is the best place to configure
properties of your Para API keys. There, you can control emails, passkeys, prompts, and more! Check out the
**Configuration** section under your API key to see all the options available.
## Modal Customization
Several configs are set via properties passed into the Para Modal. These mostly govern styling and theming features, and
can be specified programmatically
The is the easiest way to visualize your preferred modal configuration. You can then export the configuration directly into your app.
## Custom UI Authentication Options
## External Wallet Support
## Partner Add-Ons
Extend Para even further with Third Party Integrations!
# Ethers Integration
Source: https://docs.getpara.com/web/guides/evm/ethers
Integrate Para with Ethers.js (v5 or v6) to enable secure transaction signing while preserving the full Ethers API functionality.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
is a library for interacting with Ethereum. This guide shows how to integrate Para's secure signing with both and versions. Build transactions with Ethers API and sign them securely using Para.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Choose your package manager to install the Para Ethers integration along with Ethers.js:
```bash npm
npm install @getpara/ethers-v6-integration ethers@^6
```
```bash yarn
yarn add @getpara/ethers-v6-integration ethers@^6
```
```bash pnpm
pnpm add @getpara/ethers-v6-integration ethers@^6
```
```bash bun
bun add @getpara/ethers-v6-integration ethers@^6
```
```bash npm
npm install @getpara/ethers-v5-integration ethers@^5
```
```bash yarn
yarn add @getpara/ethers-v5-integration ethers@^5
```
```bash pnpm
pnpm add @getpara/ethers-v5-integration ethers@^5
```
```bash bun
bun add @getpara/ethers-v5-integration ethers@^5
```
## Basic Setup
```typescript Ethers v6
import { ParaEthersSigner } from "@getpara/ethers-v6-integration";
import { ethers } from "ethers";
import Para, { Environment } from "@getpara/web-sdk";
// Initialize Para (ensure authentication is completed before signing)
const para = new Para(Environment.BETA, YOUR_API_KEY);
// Set up the provider with a RPC URL
const provider = new ethers.JsonRpcProvider(YOUR_RPC_URL);
// Create the Para Ethers Signer
const signer = new ParaEthersSigner(para, provider);
// Now you can use the signer with any Ethers.js operations
```
```typescript Ethers v5
import { ParaEthersSigner } from "@getpara/ethers-v5-integration";
import { ethers } from "ethers";
import Para, { Environment } from "@getpara/web-sdk";
// Initialize Para (ensure authentication is completed before signing)
const para = new Para(Environment.BETA, YOUR_API_KEY);
// Set up the provider with a RPC URL
const provider = new ethers.providers.JsonRpcProvider(YOUR_RPC_URL);
// Create the Para Ethers Signer
const signer = new ParaEthersSigner(para, provider);
// Now you can use the signer with any Ethers.js operations
```
## Usage Examples
Once you've set up the Para Ethers Signer, you can use it just like any standard Ethers signer. Para handles the secure signing process without changing how you construct transactions. An example of sending ETH and interacting with a smart contract is provided below.
### Sending ETH
```typescript
// Construct a transaction using the standard Ethers API
const transaction = {
to: "0x1234567890123456789012345678901234567890",
value: ethers.parseEther("0.01"),
// Optional: specify gas parameters
// gasLimit: 21000,
};
// Sign and send the transaction
const tx = await signer.sendTransaction(transaction);
console.log("Transaction hash:", tx.hash);
// Wait for confirmation
const receipt = await tx.wait();
console.log("Transaction confirmed in block:", receipt.blockNumber);
```
### Interacting with Smart Contracts
```typescript
// Contract ABI (simplified example for ERC-20 transfer)
const abi = [
"function transfer(address to, uint256 amount) returns (bool)"
];
// Contract address for your contract
const contractAddress = "0xTokenContractAddress";
// Create a contract instance with your ParaEthersSigner passed as the signer
const contract = new ethers.Contract(contractAddress, abi, signer);
// Call the contract method
// Para will handle the signing process without modifying the transaction
const tx = await contract.transfer(
"0xRecipientAddress",
ethers.parseUnits("10", 18) // Adjust decimals based on your token
);
console.log("Transaction hash:", tx.hash);
```
As long as the Para client is authenticated and has wallets available, you can use the full Ethers.js API with the Para Ethers Signer without any additional changes.
## ParaEthersSigner Methods
The `ParaEthersSigner` provides standard Ethers.js signer functionality, allowing you to use Para's secure signing capabilities:
Returns the address of the wallet associated with this signer. By default the signer grabs the first EVM wallet associated with the authenticated user.
```typescript
const address = await signer.getAddress();
console.log("Connected wallet address:", address);
```
Signs an arbitrary message, returning the signature as a string.
```typescript
const signature = await signer.signMessage("Hello, Para!");
console.log("Signature:", signature);
```
Signs a transaction object without sending it, returning a signed transaction string.
```typescript
const signedTx = await signer.signTransaction(transaction);
console.log("Signed Transaction:", signedTx);
```
Signs EIP-712 typed data (for structured data signing).
```typescript
const signature = await signer.signTypedData(domain, types, value);
console.log("Typed Data Signature:", signature);
```
Para handles only the signing process. It does not modify your transaction in any way. All transaction construction, including gas estimations and parameter settings, is your responsibility through the Ethers.js API.
## Important Considerations
When working with the `ParaEthersSigner`, keep in mind:
1. **Authentication requirement**: The Para client must have an authenticated account before attempting any signing operations.
2. **Wallet availability**: Ensure Para has wallets available for the EVM wallet type. If you encounter errors about missing wallets, check your developer portal settings for your API key is configured for EVM wallet types.
3. **Transaction construction**: Para only signs the raw bytes of the transaction you provide. Any issues related to transaction parameters (gas price, gas limit, etc.) or RPC interactions are not related to Para's signing functionality. The RPC endpoint or the transaction itself may be the source of any issues.
4. **Network configuration**: Make sure your RPC endpoint matches the network configuration in your Para setup. Ensuring you set the correct RPC URL to match the transaction Chain ID is crucial for successful transaction signing and submission.
## Server-Side Signing
Para's signers can also be used on the server-side using pregen wallets or an active client side session. To learn more about using para on the server, check out these guides:
## Examples
If you'd like to learn more about how to use the ParaEthersSigner for different transaction types, check out this example in our Examples Hub:
## Troubleshooting
If your transaction fails with an RPC error, the issue is likely related to transaction construction, not Para's signing process. Common causes include:
* Insufficient funds for the transaction
* Incorrect nonce value
* Inadequate gas limit
* RPC endpoint connectivity issues
Try constructing a simpler transaction first to verify the signing process works correctly.
If you receive an error about no wallets being available:
1. Ensure the user has completed Para's authentication process
2. Verify the user has created or imported an Ethereum wallet
3. Check that you're using the correct Para instance that was used during authentication
If signatures are not being verified correctly:
1. Check that you're using the correct address from `getAddress()`
2. For EIP-712 typed data, ensure your domain and types match exactly between signing and verification
3. Verify that message formatting is consistent (some applications may prepend specific prefixes)
## Next Steps
Now that you've completed signing transactions with Ethers.js, explore more advanced features like Permissions Popups. If you're ready to kick off your project launch with Para, check out our Go Live Checklist.
# EVM Overview
Source: https://docs.getpara.com/web/guides/evm/overview
Seamlessly connect your dApp to EVM-compatible blockchains using Para's integration options with popular libraries like Ethers, Viem, and Wagmi.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para provides simple, standardized ways to integrate with EVM compatible blockchains using industry-standard libraries.
Choose the integration approach that best fits your existing tech stack and development preferences.
### Ready-Made Adapters
Already using a popular wallet connection library? Our adapters provide drop-in compatibility for using the `ParaModal`
with your existing setup.
### Signing Libraries
Use Para's signing libraries to use Para's MPC signing capabilities with popular EVM libraries.
# RainbowKit Integration
Source: https://docs.getpara.com/web/guides/evm/rainbowkit
Integrate Para with RainbowKit to provide seamless wallet connection and authentication for your dApp users.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide shows how to add Para to RainbowKit, allowing users to connect via social logins, email, and phone verification. Integration is simple—just add Para as a wallet connector to your wagmi configuration. Learn more about and in their documentation.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Install the required packages for the integration:
```bash npm
npm install @getpara/rainbowkit-wallet @getpara/rainbowkit wagmi viem @tanstack/react-query
```
```bash yarn
yarn add @getpara/rainbowkit-wallet @getpara/rainbowkit wagmi viem @tanstack/react-query
```
```bash pnpm
pnpm add @getpara/rainbowkit-wallet @getpara/rainbowkit wagmi viem @tanstack/react-query
```
## Setup
Create a configuration file to initialize your Para wallet configuration:
```typescript [expandable]
import { QueryClient } from "@tanstack/react-query";
import { connectorsForWallets } from "@getpara/rainbowkit";
import { getParaWallet, OAuthMethod, AuthLayout } from "@getpara/rainbowkit-wallet";
import { Environment } from "@getpara/web-sdk";
import { createConfig, http } from "wagmi";
import { sepolia } from "wagmi/chains";
const API_KEY = process.env.NEXT_PUBLIC_PARA_API_KEY || "";
const paraWalletOpts = {
para: {
environment: Environment.BETA,
apiKey: API_KEY,
},
appName: "My dApp",
};
const paraWallet = getParaWallet(paraWalletOpts);
const connectors = connectorsForWallets([
{
groupName: "Social Login",
wallets: [paraWallet],
},
]
// The rest of your connectors
);
export const wagmiConfig = createConfig({
connectors,
chains: [sepolia],
transports: {
[sepolia.id]: http(),
},
});
export const queryClient = new QueryClient();
```
Wrap your application with the necessary providers:
```tsx [expandable]
import React from "react";
import { QueryClientProvider } from "@tanstack/react-query";
import { RainbowKitProvider, lightTheme } from "@getpara/rainbowkit";
import { WagmiProvider } from "wagmi";
import { queryClient, wagmiConfig } from "./wagmi"; // Path to your config file
import { ConnectButton } from "@getpara/rainbowkit";
function App() {
return (
{/* Rest of your application */}
);
}
export default App;
```
Add the `ConnectButton` component to your application to allow users to connect their wallets:
```tsx
import { ConnectButton } from "@getpara/rainbowkit";
function App() {
return (
// The rest of your application
);
}
```
If using Next.js, its recommended to setup providers in a dedicated `Providers` component and using either the 'use client' directive or the `next/dynamic` import to ensure the providers are rendered on the client side.
### Advanced Configuration
Para's RainbowKit integration supports all of the `ParaModal` props for advanced configuration. Here's an example of how to set up the `GetParaOpts` object with various options:
```typescript [expandable]
const paraWalletOpts: GetParaOpts = {
para: {
environment: Environment.DEVELOPMENT,
apiKey: API_KEY,
},
appName: "My dApp",
logo: "/path/to/logo.svg", // Your app logo
oAuthMethods: [
OAuthMethod.APPLE,
OAuthMethod.DISCORD,
// Add other OAuth methods as needed
],
theme: {
backgroundColor: "#FFFFFF",
font: "Inter", // Font family to use
},
onRampTestMode: true,
disableEmailLogin: false,
disablePhoneLogin: false,
authLayout: [AuthLayout.AUTH_FULL], // Layout options for the auth modal
recoverySecretStepEnabled: true, // Enable recovery secrets
};
```
You can learn more about customizing the `ParaModal` in the customization guide:
### Interacting with the Connected Wallet
Rainbowkit is powered by wagmi, so you can use hooks like `useAccount` to interact with the connected wallet. To learn more about using wagmi hooks or the Rainbowkit hooks check out their docs and guides.
```tsx [expandable]
import { useAccount, useBalance } from "wagmi";
function WalletInfo() {
const { address, isConnected } = useAccount();
const { data: balance } = useBalance({ address });
if (!isConnected){
return Connect your wallet to view balance
;
}
return (
Connected address: {address}
Balance: {balance?.formatted} {balance?.symbol}
);
}
```
## Examples
If you'd like to learn more about how to use Rainbowkit with Para, check out our examples on GitHub:
## Troubleshooting
If you run into any issues, check out the following common problems and solutions:
If you encounter connection issues, check the following:
* Verify your Para API key is correct
* Ensure you've configured the correct environment (DEVELOPMENT vs PRODUCTION)
* Check browser console for error messages
* Verify you've selected supported networks in your configuration
If the Para modal doesn't appear when clicking the connect button:
* Ensure you're using the correct imports from `@getpara/rainbowkit`
* Check for any CSS conflicts that might hide the modal
* Verify your RainbowKit providers are properly configured
## Next Steps
You can also use Para directly without RainbowKit while still taking advantage of wagmi's hooks. Check out our wagmi guide for more information on how to do that.
# Viem Integration
Source: https://docs.getpara.com/web/guides/evm/viem
Integrate Para with Viem's lightweight interface for seamless Ethereum interactions using either v1 or v2.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
is a TypeScript interface for Ethereum. This guide shows how to integrate Para's secure signing with both (recommended) and versions.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Choose your package manager to install the Para Viem integration along with Viem:
```bash npm
npm install @getpara/viem-v2-integration viem@^2
```
```bash yarn
yarn add @getpara/viem-v2-integration viem@^2
```
```bash pnpm
pnpm add @getpara/viem-v2-integration viem@^2
```
```bash bun
bun add @getpara/viem-v2-integration viem@^2
```
```bash npm
npm install @getpara/viem-v1-integration viem@^1
```
```bash yarn
yarn add @getpara/viem-v1-integration viem@^1
```
```bash pnpm
pnpm add @getpara/viem-v1-integration viem@^1
```
```bash bun
bun add @getpara/viem-v1-integration viem@^1
```
## Basic Setup
```typescript
import { createParaViemClient, createParaAccount } from "@getpara/viem-v2-integration";
import { http, parseEther } from "viem";
import { sepolia } from "viem/chains";
import Para, { Environment } from "@getpara/web-sdk";
// Initialize Para (ensure authentication is completed before signing)
const para = new Para(Environment.BETA, YOUR_API_KEY);
// Create a Para Account
const account = await createParaAccount(para);
// Create the Para Viem WalletClient
const walletClient = createParaViemClient(para, {
account: account,
chain: sepolia,
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
});
// Now you can use the client with Viem's API
```
If you're currently using Viem v1, we recommend migrating to v2 for improved features and ongoing support.
```typescript
import { createParaViemClient, createParaAccount } from "@getpara/viem-v1-integration";
import { http, parseEther } from "viem";
import { sepolia } from "viem/chains";
import Para, { Environment } from "@getpara/web-sdk";
// Initialize Para (ensure authentication is completed before signing)
const para = new Para(Environment.BETA, YOUR_API_KEY);
// Create a Para Account
const viemParaAccount = await createParaAccount(para);
// Create the Para Viem Client
const paraClient = createParaViemClient(para, {
account: viemParaAccount,
chain: sepolia,
transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
});
// Now you can use the client with Viem's API
```
## Usage Examples
Once you've set up the Para Viem integration, you can leverage Viem's API with Para's secure signing. Para handles the secure signing process without changing how you construct transactions.
### Sending ETH
```typescript
import { parseEther, parseGwei } from "viem";
// Prepare transaction parameters
const transaction = {
account: viemParaAccount,
to: "0x1234567890123456789012345678901234567890" as `0x${string}`,
value: parseEther("0.01"),
gas: 21000n, // Or use parseGwei("21000")
};
// Send the transaction
try {
const hash = await paraClient.sendTransaction(transaction);
console.log("Transaction hash:", hash);
// Wait for confirmation
const receipt = await paraClient.waitForTransactionReceipt({ hash });
console.log("Transaction confirmed in block:", receipt.blockNumber);
} catch (error) {
console.error("Error sending transaction:", error);
}
```
```typescript
import { parseEther, parseGwei } from "viem";
// Prepare transaction parameters
const transaction = {
account: viemParaAccount,
to: "0x1234567890123456789012345678901234567890",
value: parseEther("0.01"),
gasLimit: parseGwei("21000"),
};
// Send the transaction
try {
const hash = await paraClient.sendTransaction(transaction);
console.log("Transaction hash:", hash);
// Wait for confirmation
const receipt = await paraClient.waitForTransactionReceipt({ hash });
console.log("Transaction confirmed in block:", receipt.blockNumber);
} catch (error) {
console.error("Error sending transaction:", error);
}
```
### Interacting with Smart Contracts
```typescript [expandable]
import { parseUnits } from "viem";
// ERC-20 contract ABI (simplified for transfer)
const abi = [{
name: "transfer",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "recipient", type: "address" },
{ name: "amount", type: "uint256" }
],
outputs: [{ type: "bool" }]
}];
// Create contract instance
const contract = {
address: "0xTokenContractAddress" as `0x${string}`,
abi
};
// Prepare the transaction
const { request } = await paraClient.simulateContract({
...contract,
functionName: "transfer",
args: [
"0xRecipientAddress" as `0x${string}`,
parseUnits("10", 18) // Adjust decimals based on your token
],
account: viemParaAccount
});
// Send the transaction
const hash = await paraClient.writeContract(request);
console.log("Transaction hash:", hash);
```
```typescript [expandable]
import { parseUnits } from "viem";
// ERC-20 contract ABI (simplified for transfer)
const abi = [{
name: "transfer",
type: "function",
stateMutability: "nonpayable",
inputs: [
{ name: "recipient", type: "address" },
{ name: "amount", type: "uint256" }
],
outputs: [{ type: "bool" }]
}];
// Create contract instance
const contract = {
address: "0xTokenContractAddress",
abi
};
// Prepare the transaction
const { request } = await paraClient.simulateContract({
...contract,
functionName: "transfer",
args: [
"0xRecipientAddress",
parseUnits("10", 18) // Adjust decimals based on your token
],
account: viemParaAccount
});
// Send the transaction
const hash = await paraClient.writeContract(request);
console.log("Transaction hash:", hash);
```
### Raw Transaction Signing
In some cases, you might want to sign a transaction without sending it immediately:
```typescript
// Prepare transaction parameters
const transaction = {
account: viemParaAccount,
to: "0x1234567890123456789012345678901234567890" as `0x${string}`,
value: parseEther("0.01"),
gas: 21000n,
};
// Sign the transaction (returns the signed transaction bytes)
try {
const signedTx = await paraClient.signTransaction(transaction);
console.log("Signed transaction:", signedTx);
// You can later broadcast this signed transaction
const hash = await paraClient.broadcastTransaction({
serializedTransaction: signedTx
});
console.log("Transaction hash:", hash);
} catch (error) {
console.error("Error signing transaction:", error);
}
```
```typescript
// Prepare transaction parameters
const transaction = {
account: viemParaAccount,
to: "0x1234567890123456789012345678901234567890",
value: parseEther("0.01"),
gasLimit: parseGwei("21000"),
};
// Sign the transaction (returns the signed transaction bytes)
try {
const signedTx = await paraClient.signTransaction(transaction);
console.log("Signed transaction:", signedTx);
// You can later broadcast this signed transaction
const hash = await paraClient.sendRawTransaction({
serializedTransaction: signedTx
});
console.log("Transaction hash:", hash);
} catch (error) {
console.error("Error signing transaction:", error);
}
```
## Para Viem Client Methods
The Para Viem client provides methods that allow you to leverage Para's secure signing capabilities:
Creates a Viem account from Para. This account is used for signing operations.
```typescript
const account = await createParaAccount(para);
console.log("Account address:", account.address);
```
Signs an arbitrary message with Para.
```typescript
const signature = await paraClient.signMessage({
account: viemParaAccount,
message: "Hello, Para!"
});
console.log("Signature:", signature);
```
Signs a transaction without sending it, returning serialized transaction bytes.
```typescript
const signedTx = await paraClient.signTransaction(transaction);
console.log("Signed Transaction:", signedTx);
```
Signs EIP-712 typed data with Para.
```typescript
const signature = await paraClient.signTypedData({
account: viemParaAccount,
domain,
types,
primaryType: 'Mail',
message
});
console.log("Typed Data Signature:", signature);
```
Para handles only the signing process. It does not modify your transaction in any way. All transaction construction, including gas estimations and parameter settings, is your responsibility through the Viem API.
## Important Considerations
When working with the Para Viem integration, keep in mind:
1. **Authentication requirement**: The Para client must have an authenticated account before attempting any signing operations.
2. **Wallet availability**: Ensure Para has wallets available for the EVM wallet type. If you encounter errors about missing wallets, check your developer portal settings to confirm your API key is configured for EVM wallet types.
3. **Transaction construction**: Para only signs the raw bytes of the transaction you provide. Any issues related to transaction parameters (gas price, gas limit, etc.) or RPC interactions are not related to Para's signing functionality.
4. **Viem version compatibility**: Ensure you're using the correct Para integration package that matches your Viem version (v1 or v2).
## Server-Side Signing
Para's signers can also be used on the server-side using pregen wallets or an active client side session. To learn more about using para on the server, check out these guides:
## Examples
If you'd like to learn more about how to use the Para Viem WalletClient for different transaction types, check out this example in our Examples Hub:
## Troubleshooting
If your transaction fails with an RPC error, the issue is likely related to transaction construction, not Para's signing process. Common causes include:
* Insufficient funds for the transaction
* Incorrect nonce value
* Inadequate gas limit
* RPC endpoint connectivity issues
Try constructing a simpler transaction first to verify the signing process works correctly.
If you receive an error about no wallets being available:
1. Ensure the user has completed Para's authentication process
2. Verify the user has created or imported an Ethereum wallet
3. Check that you're using the correct Para instance that was used during authentication
Viem v2 has stricter type requirements than v1, particularly around address formatting. Ensure you're using the correct type annotations:
```typescript
// Convert string addresses to the correct Viem type
const address = "0x1234..." as `0x${string}`;
// For v2, use BigInt values for gas limits
const gas = 21000n; // or parseGwei("21000")
```
## Next Steps
Now that you've completed integrating Para with Viem, explore more advanced features and use cases.
# Wagmi Integration
Source: https://docs.getpara.com/web/guides/evm/wagmi
Integrate Para with Wagmi's React Hooks to build powerful Ethereum dApps with seamless wallet connection and transaction signing.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
is a collection of React Hooks for Ethereum. This guide shows how to integrate Para's secure signing with both (recommended) and versions.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Choose your package manager to install the Para Wagmi integration along with Wagmi:
```bash npm
npm install @getpara/wagmi-v2-integration @tanstack/react-query wagmi@^2 viem
```
```bash yarn
yarn add @getpara/wagmi-v2-integration @tanstack/react-query wagmi@^2 viem
```
```bash pnpm
pnpm add @getpara/wagmi-v2-integration @tanstack/react-query wagmi@^2 viem
```
```bash bun
bun add @getpara/wagmi-v2-integration @tanstack/react-query wagmi@^2 viem
```
```bash npm
npm install @getpara/wagmi-v1-integration wagmi@^1
```
```bash yarn
yarn add @getpara/wagmi-v1-integration wagmi@^1
```
```bash pnpm
pnpm add @getpara/wagmi-v1-integration wagmi@^1
```
```bash bun
bun add @getpara/wagmi-v1-integration wagmi@^1
```
## Setup
Using Para with Wagmi is similar to using any other wallet provider, create a connector and pass it to Wagmi's configuration.
Wagmi is best used when creating a custom wallet connector UI. If not using a custom UI, consider using the `ParaModal` instead.
```typescript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { paraConnector } from "@getpara/wagmi-v2-integration";
import { OAuthMethod } from "@getpara/web-sdk";
import { createConfig, http } from "wagmi";
import { sepolia } from "wagmi/chains";
import Para, { Environment } from "@getpara/web-sdk";
const para = new Para(Environment.BETA, YOUR_API_KEY);
const connector = paraConnector({
para,
chains: [sepolia],
appName: "Your dApp Name",
options: {},
});
// Configure Wagmi
export const config = createConfig({
chains: [sepolia],
connectors: [connector],
transports: {
[sepolia.id]: http(),
},
});
export const queryClient = new QueryClient();
```
Then set up your providers in your main app component by wrapping your app with `WagmiProvider` and `QueryClientProvider`:
```tsx
import { WagmiProvider } from "wagmi";
import { QueryClientProvider } from "@tanstack/react-query";
import { config, queryClient } from "./wagmi"; // Path to your config file
function App() {
return (
{/* Your app components */}
);
}
```
If you're currently using Wagmi v1, we recommend migrating to v2 for improved features and ongoing support.
```typescript
import { paraConnector } from "@getpara/wagmi-v1-integration";
import { OAuthMethod } from "@getpara/web-sdk";
import { configureChains, createClient } from "wagmi";
import { mainnet, sepolia } from "wagmi/chains";
import { publicProvider } from "wagmi/providers/public";
import Para, { Environment } from "@getpara/web-sdk";
const para = new Para(Environment.BETA, YOUR_API_KEY);
// Configure chains and providers
const { chains, provider } = configureChains(
[mainnet, sepolia],
[publicProvider()]
);
// Create Wagmi client
export const client = createClient({
autoConnect: true,
connectors: [
paraConnector({
para,
chains,
options: {},
appName: "Your dApp Name",
}),
],
provider,
});
export { chains };
```
Then set up your provider in your main app component:
```tsx
import { WagmiConfig } from "wagmi";
import { client } from "./wagmi-config";
function App() {
return (
{/* Your app components */}
);
}
```
### Advanced Configuration
Para's Wagmi connector supports additional configuration options to customize the user experience:
```typescript
const connector = paraConnector({
para,
chains: [sepolia],
appName: "Your dApp Name",
options: {
// Custom options for your connector
},
nameOverride: "Para", // Custom name displayed in UI
idOverride: "para", // Custom ID for the connector
oAuthMethods: [
OAuthMethod.GOOGLE,
OAuthMethod.TWITTER,
OAuthMethod.DISCORD,
// Add or remove methods as needed
],
disableEmailLogin: false, // Set to true to disable email login
disablePhoneLogin: false, // Set to true to disable phone login
onRampTestMode: true, // Enable test mode for on-ramps if applicable
customTheme: {
// Custom theme options
accentColor: "#5F4DFF",
backgroundColor: "#FFFFFF",
},
});
```
## Usage Examples
Once you've set up the Para Wagmi integration, you can use Wagmi's hooks to interact with EVM chains. Example for connecting to a wallet:
### Connecting a Wallet
```tsx
import { useAccount, useConnect, useDisconnect } from "wagmi";
function ConnectButton() {
const { connect, connectors } = useConnect();
const { address, isConnected } = useAccount();
const { disconnect } = useDisconnect();
if (isConnected) {
return (
Connected as {address}
disconnect()}>Disconnect
);
}
return (
{connectors
.filter((connector) => connector.id === "para")
.map((connector) => (
connect({ connector })}>
Connect with {connector.name}
))}
);
}
```
```tsx
import { useAccount, useConnect, useDisconnect } from "wagmi";
function ConnectButton() {
const { address, isConnected } = useAccount();
const { connect, connectors } = useConnect();
const { disconnect } = useDisconnect();
if (isConnected) {
return (
Connected as {address}
disconnect()}>Disconnect
);
}
return (
{connectors
.filter((connector) => connector.id === "para")
.map((connector) => (
connect({ connector })}>
Connect with {connector.name}
))}
);
}
```
## Examples
If you'd like to learn more about how to use Wagmi with Para, check out this example that creates a custom wallet connector UI with Para as a wallet option:
## Troubleshooting
If you encounter problems connecting with Para through Wagmi:
1. Verify that your Para client is properly initialized and authenticated
2. Check if you've properly configured the Para connector with the correct chains
3. Ensure the connector is correctly passed to the Wagmi configuration
4. Check browser console for specific errors
If Wagmi hooks aren't working as expected:
1. Verify that your component is wrapped with the proper Wagmi providers
2. For Next.js, ensure you've added the `'use client'` directive at the top of your component file
3. Confirm that you're using the correct version of hooks matching your Wagmi version
4. Check for any conditional rendering that might be causing hooks to be skipped
If transactions fail:
1. Check if the user has sufficient funds for the transaction
2. Verify that the transaction parameters (gas limits, etc.) are correctly set
3. Check if the connected chain matches the expected network for your dApp
4. Look for RPC connection issues in the browser console
## Next Steps
With your Para and Wagmi integration complete, check out these other guides to further enhance your dApp:
# Cosmos Wallets
Source: https://docs.getpara.com/web/guides/external-wallets/cosmos
Learn how to combine the Para Modal with Cosmos wallets.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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 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 including and
. Our integration leverages a modified fork of the
React library.
Install the required packages alongside your existing Para dependencies:
```bash npm
npm install @getpara/core-sdk @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @getpara/user-management-client @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet
```
```bash yarn
yarn add @getpara/core-sdk @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @getpara/user-management-client @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet
```
```bash pnpm
pnpm add @getpara/core-sdk @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @getpara/user-management-client @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet
```
```bash bun
bun add @getpara/core-sdk @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @getpara/user-management-client @cosmjs/cosmwasm-stargate @cosmjs/launchpad @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc @leapwallet/cosmos-social-login-capsule-provider long starknet
```
Import the necessary wallet connectors and chain configurations:
```typescript main.tsx
import { useState } from "react";
import { ParaCosmosProvider, keplrWallet, leapWallet } from "@getpara/cosmos-wallet-connectors";
import { cosmoshub, osmosis } from "@getpara/graz/chains";
import { type ChainInfo } from "keplr-wallet/types";
```
If you encounter type issues with `@getpara/graz/chains`, you'll need to generate the chain files. You can:
1. Add a postinstall script to your package.json:
```json
{
"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
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 `ParaCosmosProvider` component by wrapping your application content in the `ParaCosmosProvider` component. Pass in the required configuration props:
```typescript main.tsx
export const App = () => {
const [chainId, setChainId] = useState(cosmoshub.chainId);
return (
{
setChainId(chainId);
}}
multiChain
walletConnect={{ options: { projectId: "..." } }}>
{/* Your app content and existing ParaModal */}
);
};
```
For the ParaCosmosProvider wrapping you don't need to include a QueryClientProvider as the provider already includes
one.
#### Provider Configuration
Array of Cosmos chain configurations
Array of wallet connectors to enable
The currently selected chain ID
Callback function when user switches chains
Enable multi-chain support. Defaults to false
Your WalletConnect project ID for additional connectivity options
The `ParaCosmosProvider` is built on top of
and supports all of its configuration options.
## 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.
### Creating a Custom Provider
To create a custom provider, you can follow the example below. This provider will manage the modal state and handle
chain switching.
```tsx CosmosProvider.tsx
import { createContext, useContext, useState } from "react";
import { ParaCosmosProvider, keplrWallet, leapWallet } from "@getpara/cosmos-wallet-connectors";
import { cosmoshub, osmosis } from "@getpara/graz/chains";
import { ParaModal } from "@getpara/modal";
import { type ChainInfo } from "keplr-wallet/types";
const CosmosContext = createContext({
openModal: () => {},
closeModal: () => {},
isOpen: false,
chainId: "",
setChainId: (chainId: string) => {},
});
export const useWeb3Modal = () => useContext(CosmosContext);
export const CosmosProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const [chainId, setChainId] = useState(cosmoshub.chainId);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
const cosmosChains: ChainInfo[] = [
{
...cosmoshub,
rpc: "https://rpc.cosmos.directory/cosmoshub",
rest: "https://rest.cosmos.directory/cosmoshub",
},
{
...osmosis,
rpc: "https://rpc.cosmos.directory/osmosis",
rest: "https://rest.cosmos.directory/osmosis",
},
];
return (
{children}
);
};
```
### 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.
```typescript
```
#### Modal Props Config
Modal prop options for customizing the Para Modal are included below. For advanced customization options, refer to
.
Array of supported external wallets to display in the modal
Defines the authentication layout style for social login and external wallets. Options are `AUTH_FULL`, `EXTERNAL_FULL`, `AUTH_CONDENSED`, and `EXTERNAL_CONDENSED`. Test the layout options in our
Color mode for the modal
Primary text and content color (hex format)
Modal background color (hex format)
Highlight and button colors (hex format)
URL for your application logo
Your application name displayed in the modal
Your initialized Para client instance
Array of supported OAuth authentication methods
Disable email-based authentication
Disable phone-based authentication
Enable the recovery secret step in the flow
Enable test mode for on-ramp features
## 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 Para instance:
```
{
externalWalletConnectionOnly: true,
...restOfYourOptions
}
```
Since connection only wallets bypass Para, most Para functionality will be unavailable. This includes full Para auth with external wallets, 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/web/guides/external-wallets/evm
Learn how to combine the Para Modal with EVM wallets.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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.
## 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.
Install the required packages alongside your existing Para dependencies:
```bash npm
npm install @getpara/evm-wallet-connectors @tanstack/react-query wagmi
```
```bash yarn
yarn add @getpara/evm-wallet-connectors @tanstack/react-query wagmi
```
```bash pnpm
pnpm add @getpara/evm-wallet-connectors @tanstack/react-query wagmi
```
```bash bun
bun add @getpara/evm-wallet-connectors @tanstack/react-query wagmi
```
Import the wallet connectors and supporting components you need. Adjust the imports based on which wallets you want to support:
```typescript main.tsx
import {
ParaEvmProvider,
coinbaseWallet,
metaMaskWallet,
rabbyWallet,
rainbowWallet,
walletConnectWallet,
zerionWallet,
safeWallet,
} from "@getpara/evm-wallet-connectors";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { sepolia, celo, mainnet, polygon } from "wagmi/chains";
```
Configure the `ParaEvmProvider` component by wrapping your application content in the `QueryClientProvider` and `ParaEvmProvider` components. Pass in the required configuration props:
```typescript main.tsx
const queryClient = new QueryClient();
export const App = () => {
return (
{/* Your app content and existing ParaModal */}
);
};
```
### Provider Configuration
Your WalletConnect project ID for wallet connections. If not using WalletConnect, you can use an empty string.
The name of your application displayed in wallet connection prompts
Array of supported EVM chains from wagmi/chains (e.g., mainnet, polygon, sepolia)
Array of enabled wallet connectors from @getpara/evm-wallet-connectors
Your initialized Para client instance
**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 `ParaEvmProvider` 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.
## 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.
### Creating a Custom Provider
A custom provider component can be created to encapsulate the ParaEvmProvider and ParaModal, allowing for easier
management of the modal state and wallet connections. An example implementation is shown below:
```tsx Web3Provider.tsx
const Web3Context = createContext({
openModal: () => {},
closeModal: () => {},
isOpen: false
});
export const useWeb3Modal = () => useContext(Web3Context);
export const Web3Provider = ({ children, config }) => {
const [isOpen, setIsOpen] = useState(false);
const queryClient = new QueryClient();
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
return (
{children}
);
};
```
### 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.
Optionally, you can pass the `externalWalletsWithParaAuth` prop to include full Para authentication for specific wallets.
This will add a signature verification step for these wallets and will setup a full Para account for the user, including creating Para wallets according to your APi key settings.
```typescript
```
#### Modal Props Config
Modal prop options for customizing the Para Modal are included below. For advanced customization options, refer to
.
Array of supported external wallets to display in the modal
Defines the authentication layout style for social login and external wallets. Options are `AUTH_FULL`, `EXTERNAL_FULL`, `AUTH_CONDENSED`, and `EXTERNAL_CONDENSED`. Test the layout options in our
Which external wallets to include full Para authentication for
Color mode for the modal
Primary text and content color (hex format)
Modal background color (hex format)
Highlight and button colors (hex format)
URL for your application logo
Your application name displayed in the modal
Your initialized Para client instance
Array of supported OAuth authentication methods
Disable email-based authentication
Disable phone-based authentication
Enable the recovery secret step in the flow
Enable test mode for on-ramp features
## 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 Para instance:
```
{
externalWalletConnectionOnly: true,
...restOfYourOptions
}
```
Since connection only wallets bypass Para, most Para functionality will be unavailable. This includes full Para auth with external wallets, 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/web/guides/external-wallets/multichain
Learn how to combine EVM, Solana, and Cosmos wallets with the Para Modal.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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
To support multiple blockchain ecosystems, you'll need to install and configure providers for each chain type you want
to support. This setup allows users to connect wallets from different ecosystems and interact with various blockchain
networks.
Install the required packages for all supported chains:
```bash npm
npm install @getpara/core-sdk @getpara/evm-wallet-connectors @getpara/solana-wallet-connectors @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @tanstack/react-query wagmi @solana/web3.js @solana/wallet-adapter-base @solana/wallet-adapter-react @cosmjs/stargate @cosmjs/proto-signing
```
```bash yarn
yarn add @getpara/core-sdk @getpara/evm-wallet-connectors @getpara/solana-wallet-connectors @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @tanstack/react-query wagmi @solana/web3.js @solana/wallet-adapter-base @solana/wallet-adapter-react @cosmjs/stargate @cosmjs/proto-signing
```
```bash pnpm
pnpm add @getpara/core-sdk @getpara/evm-wallet-connectors @getpara/solana-wallet-connectors @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @tanstack/react-query wagmi @solana/web3.js @solana/wallet-adapter-base @solana/wallet-adapter-react @cosmjs/stargate @cosmjs/proto-signing
```
```bash bun
bun add @getpara/core-sdk @getpara/evm-wallet-connectors @getpara/solana-wallet-connectors @getpara/cosmos-wallet-connectors @getpara/graz @getpara/react-sdk @tanstack/react-query wagmi @solana/web3.js @solana/wallet-adapter-base @solana/wallet-adapter-react @cosmjs/stargate @cosmjs/proto-signing
```
Import the necessary components and wallet connectors for each chain:
```typescript
// Chain-specific providers
import { ParaEvmProvider, metaMaskWallet, coinbaseWallet } from "@getpara/evm-wallet-connectors";
import { ParaSolanaProvider, phantomWallet, backpackWallet } from "@getpara/solana-wallet-connectors";
import { ParaCosmosProvider, keplrWallet, leapWallet } from "@getpara/cosmos-wallet-connectors";
// Chain configurations
import { mainnet, polygon } from "wagmi/chains";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { clusterApiUrl } from "@solana/web3.js";
import { cosmoshub, osmosis } from "@getpara/graz/chains";
// Support packages
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
```
Set up the configuration for each blockchain network:
```typescript
// EVM Configuration
const evmConfig = {
projectId: "your_wallet_connect_project_id",
appName: "Your App Name",
chains: [mainnet, polygon],
wallets: [metaMaskWallet, coinbaseWallet],
};
// Solana Configuration
const solanaNetwork = WalletAdapterNetwork.Mainnet;
const solanaConfig = {
endpoint: clusterApiUrl(solanaNetwork),
wallets: [phantomWallet, backpackWallet],
chain: solanaNetwork,
appIdentity: {
name: "Your App Name",
uri: `${location.protocol}//${location.host}`,
},
};
// Cosmos Configuration
const cosmosConfig = {
chains: [
{
...cosmoshub,
rpc: "https://rpc.cosmos.directory/cosmoshub",
rest: "https://rest.cosmos.directory/cosmoshub",
},
{
...osmosis,
rpc: "https://rpc.cosmos.directory/osmosis",
rest: "https://rest.cosmos.directory/osmosis",
},
],
wallets: [keplrWallet, leapWallet],
selectedChainId: cosmoshub.chainId,
multiChain: true,
};
```
Combine all providers to create a unified multichain experience:
```typescript MultichainProvider.tsx
import { createContext, useContext, useState } from "react";
import { ParaModal } from "@getpara/modal";
const MultichainContext = createContext({
openModal: () => {},
closeModal: () => {},
isOpen: false,
});
export const useMultichainModal = () => useContext(MultichainContext);
export const MultichainProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
return (
{children}
);
};
```
Due to graz implementation requirements, the `ParaCosmosProvider` must be the outermost provider component. It will
handle the `QueryClientProvider` internally, so you don't need to add it separately when using Cosmos support.
For Next.js or other SSR frameworks, implement proper client-side initialization:
```typescript
// Using the 'use client' directive (Next.js 13+)
"use client";
// Or using dynamic imports
import dynamic from "next/dynamic";
const MultichainProvider = dynamic(() => import("./MultichainProvider"), { ssr: false });
```
## Usage Example
Here's how to use the multichain provider in your application:
```typescript
function App() {
const { openModal } = useMultichainModal();
return (
Connect Wallet
{/* Rest of your application */}
);
}
// Wrap your app with the provider
function Root() {
return (
);
}
```
## 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:
# Overview
Source: https://docs.getpara.com/web/guides/external-wallets/overview
Learn how to use Para with external wallets like MetaMask, Phantom, and Keplr.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para seamlessly integrates with popular external wallets across different blockchains. This enables your users to connect their existing wallets while benefiting from Para's security and user experience features.
If you want to support multiple wallet types, you can implement a unified wallet connection experience that allows users to switch between different wallets seamlessly.
# Solana Wallets
Source: https://docs.getpara.com/web/guides/external-wallets/solana
Learn how to combine the Para Modal with Solana wallets.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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
.
Install the required packages alongside your existing Para dependencies:
```bash npm
npm install @getpara/react-sdk @getpara/solana-wallet-connectors @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js @tanstack/react-query
```
```bash yarn
yarn add @getpara/react-sdk @getpara/solana-wallet-connectors @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js @tanstack/react-query
```
```bash pnpm
pnpm add @getpara/react-sdk @getpara/solana-wallet-connectors @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js @tanstack/react-query
```
```bash bun
bun add @getpara/react-sdk @getpara/solana-wallet-connectors @solana-mobile/wallet-adapter-mobile @solana/wallet-adapter-base @solana/wallet-adapter-react @solana/wallet-adapter-walletconnect @solana/web3.js @tanstack/react-query
```
Import the wallet connectors and supporting components you need. Adjust the imports based on which wallets you want to support:
```typescript main.tsx
import { ParaSolanaProvider, backpackWallet, glowWallet, phantomWallet } from "@getpara/solana-wallet-connectors";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { clusterApiUrl } from "@solana/web3.js";
```
Set up your Solana network configuration. Choose the appropriate network for your deployment environment:
```typescript main.tsx
const solanaNetwork = WalletAdapterNetwork.Devnet;
const endpoint = clusterApiUrl(solanaNetwork);
```
Configure the `ParaSolanaProvider` component by wrapping your application content in the `QueryClientProvider` and `ParaSolanaProvider` components. Pass in the required configuration props:
```typescript main.tsx
export const App = () => {
return (
{/* Your app content and existing ParaModal */}
);
};
```
### Provider Configuration
The RPC endpoint URL for your Solana network
Array of Solana wallet connectors to enable
The Solana network to connect to (Mainnet, Devnet, or Testnet)
Your application name displayed in wallet prompts
Your application's URI (e.g., [https://yourdomain.com](https://yourdomain.com))
## 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.
### Creating a Custom Provider
A custom provider component can be created to encapsulate the ParaSolanaProvider and ParaModal, allowing for easier
management of the modal state and wallet connections. An example implementation is shown below:
```tsx Web3Provider.tsx
import { createContext, useContext, useState } from "react";
import { ParaSolanaProvider, phantomWallet, backpackWallet } from "@getpara/solana-wallet-connectors";
import { ParaModal } from "@getpara/react-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";
import { clusterApiUrl } from "@solana/web3.js";
const SolanaContext = createContext({
openModal: () => {},
closeModal: () => {},
isOpen: false,
});
export const useSolanaModal = () => useContext(SolanaContext);
export const SolanaProvider = ({ children }) => {
const [isOpen, setIsOpen] = useState(false);
const queryClient = new QueryClient();
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
const solanaNetwork = WalletAdapterNetwork.Devnet;
const endpoint = clusterApiUrl(solanaNetwork);
return (
{children}
);
};
```
### 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.
Optionally, you can pass the `externalWalletsWithParaAuth` prop to include full Para authentication for specific wallets.
This will add a signature verification step for these wallets and will setup a full Para account for the user, including creating Para wallets according to your APi key settings.
```typescript
```
#### Modal Props Config
Modal prop options for customizing the Para Modal are included below. For advanced customization options, refer to
.
Array of supported external wallets to display in the modal
Defines the authentication layout style for social login and external wallets. Options are `AUTH_FULL`, `EXTERNAL_FULL`, `AUTH_CONDENSED`, and `EXTERNAL_CONDENSED`. Test the layout options in our
Which external wallets to include full Para authentication for
Color mode for the modal
Primary text and content color (hex format)
Modal background color (hex format)
Highlight and button colors (hex format)
URL for your application logo
Your application name displayed in the modal
Your initialized Para client instance
Array of supported OAuth authentication methods
Disable email-based authentication
Disable phone-based authentication
Enable the recovery secret step in the flow
Enable test mode for on-ramp features
## 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 Para instance:
```
{
externalWalletConnectionOnly: true,
...restOfYourOptions
}
```
Since connection only wallets bypass Para, most Para functionality will be unavailable. This includes full Para auth with external wallets, 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 `CosmJS`.
# React Hooks
Source: https://docs.getpara.com/web/guides/hooks
Add state management and SDK interactions using Para's React hooks
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
## Overview
Para's React hooks provide an intuitive way to manage wallet state, handle transactions, and interact with the Para SDK.
This guide assumes you've already set up the Para Modal in your application following one of our framework integration
guides.
## Setting Up the Provider
To use Para's React hooks, wrap your application with `ParaProvider`. This provider gives your components access to
Para's hooks and state management.
```tsx
import { Environment, ParaProvider, ParaModal } from "@getpara/react-sdk";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
function App() {
return (
console.log("Logout:", event.detail),
onLogin: (event) => console.log("Login:", event.detail),
onSignMessage: (event) => console.log("Message Signed:", event.detail),
}}>
// The provider wraps the modal
);
}
```
### Provider Configuration
Para environment to use (DEV, BETA, or PRODUCTION)
Your Para API key
Optional constructor options
All of the following callback event types are exported from the `@getpara/react-sdk` and follow the following signature:
```typescript
{
detail: {
data: T;
error?: Error;
}
}
```
Called with the `LogoutEvent` when a user logs out
Called with the `LoginEvent` when a user logs in
Called with the `AccountSetupEvent` when account setup completes
Called with the `AccountCreationEvent` when a new account is created
Called with the `SignMessageEvent` after message signing
Called with the `SignTransactionEvent` after transaction signing
Called with the `ExternalWalletChangeEvent` when external wallet changes
Called with the `WalletsChangeEvent` when wallets list changes
Called with the `WalletCreatedEvent` when new wallet is created
Called with the `PregenWalletClaimedEvent` when pregen wallet is claimed
Disable automatic session refresh
Optional Para instance to use. This will override any values passed to the `paraClientConfig` prop
## Using the Hooks
Here's an example component using Para's React hooks to handle wallet connection and message signing:
```tsx
import { useAccount, useClient, useSignMessage, useWallet, useModal } from "@getpara/react-sdk";
import { useState } from "react";
export function WalletComponent() {
const { data: account } = useAccount();
const { data: wallet } = useWallet();
const { signMessageAsync } = useSignMessage();
const para = useClient();
const { openModal } = useModal();
const [message, setMessage] = useState("");
const [messageSignature, setMessageSignature] = useState();
const handleSign = async () => {
if (!wallet || !message) return;
const signatureRes = await signMessageAsync({
messageBase64: Buffer.from(message).toString("base64"),
});
if (!("pendingTransactionId" in signatureRes) && signatureRes.signature) {
setMessageSignature(signatureRes.signature);
}
};
return (
Connected Wallet:
{account?.isConnected
? wallet
? para.getDisplayAddress(wallet.id, {
truncate: true,
addressType: wallet.type,
})
: "No Wallet Selected"
: "Not Connected"}
{account?.isConnected && (
<>
setMessage(e.target.value ?? "")}
/>
{messageSignature && Signature: {messageSignature} }
Sign Message
>
)}
{account?.isConnected ? "Open Modal" : "Login"}
);
}
```
## Available Hooks
### Query Hooks
Hooks to access current the current Para instance account and wallet state. These hooks use the
[React Query](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery) `useQuery` hook and return the
same structure as the React Query hooks.
Access current account state and connection status
```tsx
const { data: account } = useAccount();
// account.isConnected, account.userId, etc.
```
Get current wallet information for the selected wallet
```tsx
const { data: wallet } = useWallet();
// wallet.id, wallet.type, etc.
```
### Mutation Hooks
Hooks for performing actions and state changes in the Para instance. These hooks use the
[React Query](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation) `useMutation` hook and
return the same structure as the React Query hooks (see the note below). **NOTE** Our hooks return mutation functions
named specifically for their mutation action. For example,`useCreateUser` returns both `createUserAsync` and
`createUser` rather than the `mutateAsync` and `mutate` functions that React Query returns, these functions are
identical to their React Query counterparts.
Create new Para account
```tsx
const { createUserAsync } = useCreateUser();
await createUserAsync({ email: 'user@example.com' });
```
Start login process
```tsx
const { initiateLoginAsync } = useInitiateLogin();
await initiateLoginAsync({ email: 'user@example.com' });
```
Handle logout
```tsx
const { logoutAsync } = useLogout();
logoutAsync({ clearPregenWallets: false });
```
Sign messages. If a `walletId` isn't passed in, the current `selectedWallet` will be used (see the `useWalletState` hook below).
```tsx
const { signMessageAsync } = useSignMessage();
const sig = await signMessageAsync({
messageBase64: 'base64-message',
walletId: 'wallet-id'
});
```
Sign transactions. If a `walletId` isn't passed in, the current `selectedWallet` will be used (see the `useWalletState` hook below).
```tsx
const { signTransactionAsync } = useSignTransaction();
const sig = await signTransactionAsync({
rlpEncodedTxBase64: 'base64-tx',
walletId: 'wallet-id'
});
```
### Utility Hooks
Hooks for retrieving and modifying application state. These hooks DO NOT incorporate or follow and React Query patterns.
Access Para client instance
```tsx
const para = useClient();
```
Control Para modal
```tsx
const { isOpen, openModal, closeModal } = useModal();
```
Manage selected wallet state. This is the wallet shown in the modal display & is the default wallet used in the wallet action hooks.
```tsx
const { selectedWallet, setSelectedWallet } = useWalletState();
```
## Advanced Usage
### Using Para with EVM Providers
The `ParaProvider` can work with our External Wallet Connectors to provide wallet connection. An example using our Para
EVM Provider is shown below:
```tsx
import { Environment, ParaProvider, ParaEvmProvider } from "@getpara/react-sdk";
import { metaMaskWallet } from "@getpara/evm-wallet-connectors";
import { sepolia } from "wagmi/chains";
function App() {
return (
);
}
```
For more details on using external wallet connectors visit any of the external wallet integration guides:
# Permissions
Source: https://docs.getpara.com/web/guides/permissions
Configure user-facing transaction confirmation dialogs
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
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
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
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/web/guides/pregen
Learn how to create and manage pregenerated wallets for users with Para's SDK
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## 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.
```typescript
const hasWallet = await para.hasPregenWallet({
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
```
```typescript
if (!hasWallet) {
const pregenWallet = await para.createPregenWallet({
type: WalletType.EVM,
pregenIdentifier: "user@example.com",
pregenIdentifierType: "EMAIL",
});
console.log("Pregenerated Wallet ID:", pregenWallet.id);
}
```
The type of wallet to create, defaulting to the sole allowed type for your application (if applicable)
The string identifier for the new wallet.
The identifier type. Allowed types are `'EMAIL'`, `'PHONE'`, `'DISCORD'`, `'TWITTER'`, or `'CUSTOM_ID'`.
The identifier doesn't have to be an email. It can be any string specific to your application, such as a user ID. See
the `Method parameters` above for more details.
## 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
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
// 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
// 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
Ensure the user has completed Para's authentication flow and has an active session.
```typescript
// User must be authenticated through Para's systems
if (!(await para.isFullyLoggedIn())) {
// Redirect to authentication flow or show login modal
}
```
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
// Load the previously stored user share into the Para client
await para.setUserShare(userShare);
```
Ensure the pregenerated wallet's identifier matches the authenticated user's identifier:
```typescript
// 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,
newPregenIdentifier: user.email,
newPregenIdentifierType: "EMAIL",
});
```
### Claiming the Wallet
Once prerequisites are met, you can claim the wallet:
```typescript
// Claim all pregenerated wallets associated with this user
const recoverySecret = await para.claimPregenWallets();
// Or claim specific wallets by identifier
const recoverySecret = await para.claimPregenWallets({
pregenIdentifier: user.email,
pregenIdentifierType: "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
Retrieve pregenerated wallets for a given identifier. This is read-only and does not include any signer information. Signing ability rests solely on the `UserShare`.
{" "}
Check if a pregenerated wallet exists for an identifier.
{" "}
Claim pregenerated wallets associated with an identifier. See the above claiming process for more details.
{" "}
Create a new pregenerated wallet for an identifier.
Update the identifier of a pregenerated wallet. See the above claiming process for more details.
## 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/web/guides/sessions
Guide to managing authentication sessions in Para for web applications
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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 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.
```typescript
async isSessionActive(): Promise
```
This method returns a boolean indicating if the session is currently valid and
active. For external wallet connections, this will always return true.
Example usage:
```typescript
const para = new Para(env, 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.
```typescript
async keepSessionAlive(): Promise
```
This is a lightweight method that attempts to maintain the current session and
returns a boolean indicating success or failure.
Example usage:
```typescript
const para = new Para(env, 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);
}
```
If you're using the React SDK and the `ParaProvider` component, you can leverage automatic session management:
```typescript
// 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.
```typescript
async refreshSession({ shouldOpenPopup } = {}): Promise
```
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
const para = new Para(env, apiKey);
// When session expires, initiate a full authentication
if (!(await para.isSessionActive())) {
//route to authentication page
}
```
## Client-Server Session Transfer
### 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.
```typescript
exportSession({ excludeSigners?: boolean }): string
```
Returns a Base64 encoded string containing the session state, including user
details, wallet information, and authentication data.
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 client-side export:
```typescript
const para = new Para(env, 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
```
To learn more about handling session on the server, check out the following guide:
## Sessions with Pre-Generated Wallets
When using pre-generated wallets, session management works differently as these wallets don't require traditional authentication.
For pre-generated 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
const para = new Para(env, apiKey);
// Load a pre-generated wallet
await para.loadUserShare(userShare);
// Session checks will return true as long as userShare is loaded
const isActive = await para.isSessionActive(); // Always true for pre-gen wallets
```
## Best Practices
1. **Proactive Session Management**: Check session status before operations that require authentication.
2. **Regular Session Extension**: For long user sessions, periodically call `keepSessionAlive()` or use the `ParaProvider` automatic session management.
3. **Security-First Approach**: When exporting sessions to servers, use `excludeSigners: true` unless server-side signing is explicitly needed.
4. **Graceful Expiration Handling**: Provide a smooth re-authentication flow when sessions expire.
5. **Session Verification**: For security-critical operations, verify sessions on both client and server sides.
# Para with Anchor Framework
Source: https://docs.getpara.com/web/guides/solana/anchor
Learn how to integrate Para with Solana Anchor Framework for interacting with on-chain programs.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
The is a popular development framework for building Solana programs that simplifies the development process with features like IDL integration and type safety. This guide will show you how to integrate Para's wallet and signing capabilities with Anchor.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Choose your preferred package manager to install the required dependencies:
```bash npm
npm install @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor
```
```bash yarn
yarn add @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor
```
```bash pnpm
pnpm add @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor
```
```bash bun
bun add @getpara/solana-web3.js-v1-integration @solana/web3.js @project-serum/anchor
```
## Setting Up Anchor with Para
To use Para with Anchor, you need to create a custom signer that integrates with Anchor's wallet system. Below is an example of how to set up the Para signer with Anchor using the `ParaSolanaWeb3Signer` class. You can reference the guide for more details on the signer setup.
```typescript
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";
import { para } from "./para"; // Your Para client initialization
const solanaConnection = new Connection(clusterApiUrl("testnet"));
const solanaSigner = new ParaSolanaWeb3Signer(para, solanaConnection);
const anchorWallet = {
publicKey: solanaSigner.sender,
signTransaction: async (tx: T): Promise => {
return await solanaSigner.signTransaction(tx);
},
signAllTransactions: async (txs: T[]): Promise => {
return await Promise.all(txs.map((tx) => solanaSigner.signTransaction(tx)));
},
};
const provider = new anchor.AnchorProvider(
solanaConnection,
anchorWallet,
{ commitment: "confirmed" }
);
// Set the provider globally
anchor.setProvider(provider);
```
## Creating a Read-Only Provider
For cases where you need to read from a program before the user has authenticated, you can create a read-only provider:
```typescript
const readOnlyWallet = {
publicKey: SystemProgram.programId, // Fallback to SystemProgram's public key
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.");
},
};
const readOnlyProvider = new anchor.AnchorProvider(
solanaConnection,
readOnlyWallet,
{ commitment: "confirmed" }
);
```
## Working with Anchor Programs
Once you've set up the provider, you can initialize and interact with your Anchor program:
```typescript
// Initialize the program
const program = new anchor.Program(idl as YourProgramImport, provider);
// Now you can call your program's methods
try {
// Example: Create Token
const tx = await program.methods
.createToken(tokenName, tokenSymbol)
.accounts({
payer: solanSigner.sender,
mintAccount: mintKeypair.publicKey,
tokenProgram: TOKEN_2022_PROGRAM_ID,
systemProgram: SystemProgram.programId,
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
})
.signers([mintKeypair])
.rpc();
console.log("Transaction signature:", tx);
} catch (error) {
console.error("Error calling program method:", error);
}
```
We encourage you check out the for more details on how to define your IDL, accounts, and methods.
Para only acts as the signer for the transaction and does not handle the program logic or state. Constructing transactions and interacting with the program is entirely up to you. Ensure you have the correct program IDL and account structures defined in your Anchor program.
## Server-Side Integration
Para's signers can also be used on the server-side with Anchor using pregen wallets or an active client-side session. To learn more about using Para on the server, check out these guides:
## Examples
If you'd like to learn more about how to use Para with Anchor for different program interactions, check out this example in our Examples Hub:
# Overview
Source: https://docs.getpara.com/web/guides/solana/overview
An overview of Solana integration supported by Para using solana-web3.js, with information on current platform support.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para supports solana-web3.js for easy and standardized Solana integrations. To learn more, check out the integration
guide below:
# Para with Solana Web3.js
Source: https://docs.getpara.com/web/guides/solana/web3js
Learn how to integrate Para with Solana using the Solana Web3.js library to sign transactions and interact with the Solana blockchain.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Solana Web3.js is the official Solana JavaScript API for interacting with the Solana network. This guide will show you
how to integrate Para's wallet and signing capabilities with Solana using our integration package.
## Prerequisites
To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration. Before integrating Para with your application, ensure you have:
* Completed Para authentication setup in your application (see one of our Setup Guides)
* A valid Para API key
* An RPC endpoint for your desired network
Need an API key? Visit the to create API keys, manage billing, teams, and more.
## Installation
Choose your preferred package manager to install the required dependencies:
```bash npm
npm install @getpara/solana-web3.js-v1-integration @solana/web3.js
```
```bash yarn
yarn add @getpara/solana-web3.js-v1-integration @solana/web3.js
```
```bash pnpm
pnpm add @getpara/solana-web3.js-v1-integration @solana/web3.js
```
```bash bun
bun add @getpara/solana-web3.js-v1-integration @solana/web3.js
```
## Setting Up the Signer
Initialize the Para Solana signer:
```typescript
import { ParaSolanaWeb3Signer, createTestTransaction } from "@getpara/solana-web3.js-v1-integration";
import { Connection, clusterApiUrl, Transaction } from "@solana/web3.js";
import { para } from "./para"; // Your Para client initialization
// Set up Solana connection
const solanaConnection = new Connection(clusterApiUrl("testnet"));
// Create the Para Solana Signer
const solanaSigner = new ParaSolanaWeb3Signer(para, solanaConnection);
// The test transaction sends 0.01 testnet SOL to your own address
const transaction: Transaction = await createTestTransaction(para);
const signedTx = await solanaSigner.signTransaction(transaction);
```
## Transaction Signing
### Signing Transactions
```typescript
import { Transaction, SystemProgram, LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
// Create transaction
const transaction = new Transaction().add(
SystemProgram.transfer({
fromPubkey: solanaSigner.sender,
toPubkey: new PublicKey("RECIPIENT_ADDRESS"),
lamports: LAMPORTS_PER_SOL * 0.1, // 0.1 SOL
})
);
// Sign the transaction
try {
const signedTx = await solanaSigner.signTransaction(transaction);
console.log("Signed transaction:", signedTx);
} catch (error) {
console.error("Error signing transaction:", error);
}
```
### Sending Transactions
```typescript
try {
const signature = await solanaSigner.sendTransaction(transaction, {
skipPreflight: false,
preflightCommitment: "confirmed",
});
console.log("Transaction signature:", signature);
} catch (error) {
console.error("Error sending transaction:", error);
}
```
## Additional Methods
The `ParaSolanaWeb3Signer` provides several additional methods and properties:
* `signBytes(bytes: Buffer)`: Sign arbitrary bytes directly
* `signVersionedTransaction(transaction: VersionedTransaction)`: Sign a versioned transaction
* `address`: Get the wallet address as a string
* `sender`: Get the wallet public key as a `solana.PublicKey` object
## Server-Side Signing
Para's signers can also be used on the server-side using pregen wallets or an active client side session. To learn more about using para on the server, check out these guides:
## Examples
If you'd like to learn more about how to use the `ParaSolanaWeb3Signer` for different transaction types, check out this example in our Examples Hub:
# Overview
Source: https://docs.getpara.com/web/overview
An introduction to web integrations with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para supports a variety of modern web frameworks, giving you the flexibility to implement our sdks using your preferred technology stack. Each integration guide provides step-by-step instructions tailored to the specific framework.
### React
### Vue
### Svelte
## Advanced Integrations
Take your para integration beyond basic Authentication enabling powerful capabilities for specific use cases and enhanced user experiences.
## Ecosystem Integrations
Para works seamlessly with major blockchain ecosystems, allowing you to leverage Para's authentication alongside chain-specific libraries and development tools.
# Para with Next.js
Source: https://docs.getpara.com/web/setup/react/nextjs
A guide to integrate Para into your Next.js application.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide will walk you through the process of integrating the `ParaProvider` with the `ParaModal` into your **Next.js** application,providing a seamless way to manage user authentication and wallets.
If you haven't already set up your Next.js app, you can get started by creating a new project via the
.
## 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
```bash npm
npm install @getpara/react-sdk @tanstack/react-query
```
```bash yarn
yarn add @getpara/react-sdk @tanstack/react-query
```
```bash pnpm
pnpm add @getpara/react-sdk @tanstack/react-query
```
```bash bun
bun add @getpara/react-sdk @tanstack/react-query
```
Depending on your Next.js version or your build/TypeScript settings, you may want or need to transpile external modules
(like `@getpara/*`).
In your `next.config.js`, enable the transpilation:
```js
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ["@getpara/react-sdk", "@getpara/*"],
};
module.exports = nextConfig;
```
## Setting Up the Para SDK
Now that you've installed the necessary dependencies, let's set up the Para SDK in your Next.js project using the
recommended ParaProvider approach.
#### Create a Providers Component">
First, create a Providers component that will wrap your application with both the QueryClientProvider (required for Para) and the ParaProvider:
```jsx
"use client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Environment, 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}
);
}
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
#### Wrap Your App with Providers"
Next, wrap your root layout with the Providers component:
```jsx
import { Providers } from './providers';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
#### Add the Para Modal
Now you can include the ParaModal component in any client component within your application and use the ParaProvider hooks to control it:
```jsx
"use client";
import { ParaModal, useModal } from "@getpara/react-sdk";
export function ParaButton() {
const { openModal } = useModal();
return (
<>
openModal()}>
Sign in with Para
>
);
}
```
The ParaModal will automatically connect to the ParaProvider context, so you don't need to pass any additional props for functionality. You only need to customize its appearance.
**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
To provide the best experience for your users, you can customize the appearance of the Para Modal to match your
application's branding.
When rendering the ParaModal component, you can pass props to customize its appearance:
```jsx
```
For a full list of available `ParaModalProps`, refer to the customization guide:
## Alternative Approach: Standalone Para Modal
While the ParaProvider approach is recommended, you can also use the ParaModal as a standalone component. This approach requires you to create and manage a Para client instance manually.
### Creating a Para Client Instance
You can create the Para client instance in a dedicated file:
```jsx
import Para, { Environment } from "@getpara/react-sdk";
const para = new Para(Environment.BETA, process.env.NEXT_PUBLIC_PARA_API_KEY);
export default para;
```
### Integrating the Standalone Para Modal
Because Next.js supports server-side rendering (SSR), you need to ensure the modal runs on the client side:
```jsx
"use client";
import React, { useState } from "react";
import { ParaModal } from "@getpara/react-sdk";
import para from "../clients/para";
import "@getpara/react-sdk/styles.css";
export default function HomePage() {
const [isOpen, setIsOpen] = useState(false);
return (
setIsOpen(true)}>Sign in with Para
setIsOpen(false)}
/>
);
}
```
## Examples
For practical implementation of the Para SDK in **Next.js** applications, explore our Examples Hub repository. It contains a variety of examples showcasing how to integrate the Para SDK with Next.js:
## Troubleshooting
If you encounter issues during the integration or usage of the Para Modal in Next.js, here are some common problems and
their solutions:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Para with React
Source: https://docs.getpara.com/web/setup/react/overview
Discover options for using Para with React.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para integrates seamlessly with React applications, providing powerful features for building interactive interfaces.
Choose the React framework that best suits your project needs:
# Para with TanStack Start
Source: https://docs.getpara.com/web/setup/react/tanstack-start
A guide to integrate Para SDK with TanStack Start while preserving server-side rendering capabilities.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide will walk you through the process of integrating the Para SDK with **TanStack Start** while preserving server-side rendering (SSR) capabilities.
## What is TanStack Start?
TanStack Start is a full-stack React framework powered by TanStack Router. It provides full-document SSR, streaming, server functions, bundling, and more using tools like Nitro and Vite. It's ready to deploy to your favorite hosting provider!
TanStack Start uses Nitro and Vite to bundle and deploy your application, providing:
* A unified API for SSR, streaming, and hydration
* Extraction of server-only code from your client-side code (e.g., server functions)
* Bundling your application for deployment to any hosting provider
## 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
First, install the Para React SDK and React Query using your preferred package manager:
```bash npm
npm install @getpara/react-sdk @tanstack/react-query
```
```bash yarn
yarn add @getpara/react-sdk @tanstack/react-query
```
```bash pnpm
pnpm add @getpara/react-sdk @tanstack/react-query
```
```bash bun
bun add @getpara/react-sdk @tanstack/react-query
```
## 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. However, we only want to run these polyfills on the client, not on the server.
1. Install the Vite Node Polyfills plugin:
```bash npm
npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm
pnpm add vite-plugin-node-polyfills -D
```
```bash bun
bun add -d vite-plugin-node-polyfills
```
2. Configure the polyfills in your `app.config.ts`:
```ts
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()],
},
},
},
});
```
This approach:
* Avoids loading node polyfills on the server where they're unnecessary and can cause conflicts
* Sets `process.browser` to true which helps modules determine the execution environment
* Ensures proper resolution of browser-specific code paths
## Setting Up the Para SDK
In TanStack Start, Para SDK components must be loaded client-side only to avoid SSR conflicts. This is accomplished using a combination of `React.lazy` for dynamic loading and `ClientOnly` from TanStack Router to prevent server rendering.
First, set up your API key and environment constants in a file like `src/constants.ts`:
```ts
export const API_KEY = import.meta.env.VITE_PARA_API_KEY || "";
export const ENVIRONMENT = import.meta.env.VITE_PARA_ENVIRONMENT || "BETA";
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
Create a providers component that will wrap your application with both the QueryClientProvider and lazy-loaded ParaProvider:
```tsx
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ClientOnly } from "@tanstack/react-router";
import { API_KEY, ENVIRONMENT } from "~/constants";
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}
);
}
```
The `ClientOnly` component ensures that the ParaProvider is only rendered on the client side, preventing hydration mismatches. The `fallback` prop determines what to show during server-side rendering.
Now, wrap your root component with the Providers component:
```tsx
import Providers from "~/components/Providers";
function RootComponent() {
return (
);
}
```
For any component that uses Para SDK components, use the same approach with `React.lazy` and `ClientOnly`:
```tsx
import React from "react";
import { createFileRoute, ClientOnly } from "@tanstack/react-router";
import "@getpara/react-sdk/styles.css";
// Lazy load Para components
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,
});
```
And in your `ParaContainer.tsx`:
```tsx
import { AuthLayout, OAuthMethod, ParaModal, useModal, useWallet, useAccount } from "@getpara/react-sdk";
export function ParaContainer() {
const { openModal } = useModal();
const { data: wallet } = useWallet();
const { data: account, isLoading, error } = useAccount();
return (
<>
{account?.isConnected ? (
Wallet address: {wallet?.address}
) : (
You are not logged in.
)}
Open Para Modal
{error && {error.message}
}
>
);
}
```
**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.
### Why Client-side Only Loading?
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.
### Customizing the Para Modal
To provide the best experience for your users, you can customize the appearance of the Para Modal to match your application's branding:
```tsx
```
For a full list of available `ParaModalProps`, refer to the customization guide:
## Examples
For practical implementation of the Para SDK in **TanStack Start** applications, explore our example repository:
## Troubleshooting
If you encounter hydration mismatches or errors related to styled-components, ensure you're using both:
1. `React.lazy` to dynamically import Para components
2. `ClientOnly` from TanStack router to prevent server rendering
```tsx
const LazyParaComponent = React.lazy(() =>
import("@getpara/react-sdk").then((mod) => ({
default: mod.ParaComponent
}))
);
// Then in your JSX:
Loading...}>
```
This dual approach ensures Para components are only evaluated and rendered on the client-side.
If you encounter errors related to missing polyfills (like `crypto`, `buffer`, etc.), verify:
1. You've installed `vite-plugin-node-polyfills`
2. The polyfill plugin is correctly configured in your `app.config.ts` under the `routers.client.vite.plugins` section
3. `process.browser` is set to `true` in the base `vite` section
```ts
export default defineConfig({
vite: {
define: {
"process.browser": true,
},
},
routers: {
client: {
vite: {
plugins: [nodePolyfills()],
},
},
},
});
```
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Para with React + Vite
Source: https://docs.getpara.com/web/setup/react/vite
A guide to quickly integrate the Para Modal into your Vite-powered React application.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide will walk you through the process of integrating the Para Modal into a **Vite**-powered React application,
providing a seamless way to manage user authentication and wallets.
If you haven't already created a new Vite project, you can follow to get started.
## 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
First, install the Para React SDK and React Query using your preferred package manager:
```bash npm
npm install @getpara/react-sdk @tanstack/react-query
```
```bash yarn
yarn add @getpara/react-sdk @tanstack/react-query
```
```bash pnpm
pnpm add @getpara/react-sdk @tanstack/react-query
```
```bash bun
bun add @getpara/react-sdk @tanstack/react-query
```
## Setting Up Polyfills with Vite
Some Node.js modules that Para relies on (e.g., `crypto`, `buffer`, `stream`) may need to be polyfilled in a Vite
environment. One easy option is to use `vite-plugin-node-polyfills`:
```bash npm
npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm
pnpm add vite-plugin-node-polyfills -D
```
```bash bun
bun add -d vite-plugin-node-polyfills
```
```js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [react(), nodePolyfills()],
// Additional Vite configurations if needed
});
```
This ensures Node-specific modules are properly polyfilled in the browser environment.
## Setting Up the Para SDK
Create a providers component that will wrap your application with both the QueryClientProvider (required for Para) and the ParaProvider:
```jsx
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Environment, ParaProvider } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
const queryClient = new QueryClient();
export function Providers({ children }) {
return (
{children}
);
}
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
Now, wrap your main app component with the Providers component:
```jsx
import { Providers } from './providers';
import YourApp from './YourApp';
function App() {
return (
);
}
export default App;
```
Now you can include the ParaModal component in any component within your application and use the ParaProvider hooks to control it:
```jsx
import { ParaModal, useModal } from "@getpara/react-sdk";
function LoginButton() {
const { openModal } = useModal();
return (
<>
openModal()}>
Sign in with Para
>
);
}
export default LoginButton;
```
The ParaModal will automatically connect to the ParaProvider context, so you don't need to pass any additional props for functionality. You only need to customize its appearance.
**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
To provide the best experience for your users, you can customize the appearance of the Para Modal to match your
application's branding.
When rendering the ParaModal component, you can pass props to customize its appearance:
```jsx
```
For a full list of available `ParaModalProps`, refer to the customization guide:
## Alternative Approach: Standalone Para Modal
While the ParaProvider approach is recommended, you can also use the ParaModal as a standalone component. This approach requires you to create and manage a Para client instance manually.
### Creating a Para Client Instance
You can create the Para client instance in a dedicated file:
```jsx
import Para, { Environment } from "@getpara/react-sdk";
const para = new Para(Environment.BETA, import.meta.env.VITE_PARA_API_KEY);
export default para;
```
### Integrating the Standalone Para Modal
Once you have created your Para client instance, you can integrate the Para Modal into your React component:
```jsx
import React, { useState } from "react";
import { ParaModal } from "@getpara/react-sdk";
import para from "./clients/para"; // or the path to where you created your para instance
import "@getpara/react-sdk/styles.css";
function App() {
const [isOpen, setIsOpen] = useState(false);
return (
setIsOpen(true)}>Sign in with Para
setIsOpen(false)}
/>
);
}
export default App;
```
This creates a button that, when clicked, opens the Para Modal for user authentication. The Para Modal handles all
aspects of user authentication and wallet management.
## Examples
For practical implementation of the Para SDK in **Vite**-based React applications, explore our example repository. This
repository contains real-world examples to help you get started.
## Troubleshooting
If you encounter issues during the integration or usage of the Para Modal in a Vite-based app, here are some common
problems and their solutions:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Para + Svelte
Source: https://docs.getpara.com/web/setup/svelte/overview
Discover options for using Para with Svelte.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para integrates seamlessly with Svelte applications, providing powerful features for building interactive interfaces. Choose the Svelte framework that best suits your project needs:
Para does not offer a dedicated Svelte SDK at this time. However, we provide solutions for utilizing our React based `ParaModal` component in Svelte applications.
If you prefer to build your own custom UI, you can also use `@getpara/web-sdk` directly. Reach out to us for help with custom UI integration.
# Para with SvelteKit
Source: https://docs.getpara.com/web/setup/svelte/svelte-kit
A user-friendly guide to integrate the React-based Para Modal into your SvelteKit application.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
If you haven't already you can create a new SvelteKit project 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
Install the Para React SDK, plus React dependencies:
```bash npm
npm install @getpara/react-sdk react react-dom
```
```bash yarn
yarn add @getpara/react-sdk react react-dom
```
```bash pnpm
pnpm add @getpara/react-sdk react react-dom
```
```bash bun
bun add @getpara/react-sdk react react-dom
```
## Configuring SvelteKit (Preprocessor & Polyfills)
### Svelte Preprocess React
In your **svelte.config.js**, import and apply `svelte-preprocess-react`:
```js svelte.config.js
import adapter from "@sveltejs/adapter-auto";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
import preprocessReact from "svelte-preprocess-react/preprocessReact";
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [vitePreprocess(), preprocessReact()],
kit: {
adapter: adapter(),
},
};
export default config;
```
### Vite Plugins (Node Polyfills)
SvelteKit uses Vite under the hood. Install and configure polyfills for Node modules:
```bash npm
npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm
pnpm add vite-plugin-node-polyfills -D
```
```bash bun
bun add vite-plugin-node-polyfills
```
Then enable it in `vite.config.ts`:
```ts vite.config.ts
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [sveltekit(), nodePolyfills()],
});
```
If you prefer SSR, ensure you dynamically handle the React-based modal on the client. If you want pure client-side
usage, set `export const ssr = false;` in your page's `.ts` or `.js` files for SvelteKit to skip SSR.
## Setting Up the Para SDK
Now that you've installed the necessary dependencies, let's set up the Para SDK in your SvelteKit application. This
involves creating a client instance and integrating the Para Modal.
### Creating a Para Client Instance
As with other frameworks, you'll need a dedicated file for your client instance (e.g., `client/para.ts`):
```ts client/para.ts
import { Environment, ParaWeb } from "@getpara/react-sdk";
const PARA_API_KEY = import.meta.env.VITE_PARA_API_KEY;
export const para = new ParaWeb(Environment.BETA, PARA_API_KEY);
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
### Using the Para Modal in a SvelteKit Route
Let's say you have a **`+page.svelte`** in `src/routes/`. You would use the Para Modal with the preprocessed React
component:
```svelte
isModalOpen = true}>
Open Para Modal
isModalOpen = false} onRampTestMode={true} twoFactorAuthEnabled={false}/>
```
With SSR disabled, the page will be served client-side only, ensuring the React-based modal can load without conflicts.
**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
Just like in React, you can provide any additional props to the `` component. For example, customizing
theming:
```svelte
```
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:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Para with Svelte + Vite
Source: https://docs.getpara.com/web/setup/svelte/vite
A user-friendly guide to integrate the Para Modal (React-based) into your Svelte application powered by Vite.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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
First, install **both** the Para React SDK **and** React dependencies:
```bash npm
npm install @getpara/react-sdk react react-dom
```
```bash yarn
yarn add @getpara/react-sdk react react-dom
```
```bash pnpm
pnpm add @getpara/react-sdk react react-dom
```
```bash bun
bun add @getpara/react-sdk react react-dom
```
## 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
npm install svelte-preprocess-react
```
```bash yarn
yarn add svelte-preprocess-react
```
```bash pnpm
pnpm add svelte-preprocess-react
```
```bash bun
bun add svelte-preprocess-react
```
Then add it to your `svelte.config.js`:
```js svelte.config.js
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
npm install vite-plugin-node-polyfills --save-dev
```
```bash yarn
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm
pnpm add vite-plugin-node-polyfills -D
```
```bash bun
bun add vite-plugin-node-polyfills
```
Then configure it in `vite.config.js` or `vite.config.ts`:
```ts vite.config.ts
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [svelte(), nodePolyfills()],
});
```
## 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
import { Environment, ParaWeb } from "@getpara/react-sdk";
const PARA_API_KEY = import.meta.env.VITE_PARA_API_KEY;
export const para = new ParaWeb(Environment.BETA, PARA_API_KEY);
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
### 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
isModalOpen = true}>
Open Para Modal
isModalOpen = false}/>
```
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
theming:
```svelte
```
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:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Para with Nuxt 3
Source: https://docs.getpara.com/web/setup/vue/nuxt
A guide to integrate the Para Modal (React-based) into your Nuxt 3 application.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide shows you how to integrate the **Para Modal**—which is React-based—within a **Nuxt 3** application.
If you haven't already created your Nuxt 3 app, follow
to set up a new project.
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.
## 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
First, install the Para React SDK along with React dependencies:
```bash npm
npm install @getpara/react-sdk react react-dom
```
```bash yarn
yarn add @getpara/react-sdk react react-dom
```
```bash pnpm
pnpm add @getpara/react-sdk react react-dom
```
```bash bun
bun add @getpara/react-sdk react react-dom
```
## Configuring Nuxt (vite plugins & polyfills)
Nuxt 3 uses Vite under the hood, so we can add the **React plugin** and polyfills in `nuxt.config.ts`:
```ts nuxt.config.ts
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineNuxtConfig({
ssr: false, // If you plan to show the modal on the client only
vite: {
plugins: [react(), nodePolyfills()],
},
});
```
Depending on your app's architecture, you may or may not disable SSR entirely. The key requirement is ensuring the
React-based modal is client-rendered.
## 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
import { Environment, ParaWeb } from "@getpara/react-sdk";
const PARA_API_KEY = import.meta.env.VITE_PARA_API_KEY;
export const para = new ParaWeb(Environment.BETA, PARA_API_KEY);
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
### Building a Connector for the React Modal
Just as with Vue + Vite, we need a **connector** that mounts the React para modal into a DOM element. For instance:
```tsx para-modal-connector.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { ParaModal, ParaModalProps } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
export const createParaModalConnector = (targetEl: HTMLElement, props: Omit) => {
const root = createRoot(targetEl);
const state = {
isOpen: false,
render: (isOpen: boolean) => {
state.isOpen = isOpen;
root.render(
{
state.isOpen = false;
state.render(false);
props.onClose?.();
}}
/>
);
},
};
return {
open: () => state.render(true),
close: () => state.render(false),
isOpen: () => state.isOpen,
unmount: () => root.unmount(),
};
};
```
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.
### Using the Connector in a Nuxt Component
Nuxt 3 uses a file-based routing system. Let's assume you have a top-level `app.vue` or a page component where you want
to show the modal:
```vue app.vue
Para Modal in Nuxt 3
Open Para Modal
```
When you click **Open Para Modal**, the React-based Para Modal appears in your Nuxt app.
**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
As with any React-based usage, you can pass different props to `ParaModal`. For example:
```tsx
```
Use your connector function to provide these props from Vue.
### If You Don't Want to Use the React Modal
You can also build a fully custom UI using [@getpara/web-sdk](https://www.npmjs.com/package/@getpara/web-sdk). This
approach avoids mixing React into your Nuxt application by letting you directly call Para methods and build your own Vue
components.
## Examples
For a simple example of Para in a Nuxt app, check out our Examples Hub repository:
## 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:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Para + Vue
Source: https://docs.getpara.com/web/setup/vue/overview
Discover options for using Para with Vue.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para integrates seamlessly with Vue applications, providing powerful features for building interactive interfaces.
Choose the Vue framework that best suits your project needs:
Para does not offer a dedicated Vue SDK at this time. However, we provide solutions for utilizing our React based `ParaModal` component in Vue applications.
If you prefer to build your own custom UI, you can also use `@getpara/web-sdk` directly. Reach out to us for help with custom UI integration.
# Para with Vue + Vite
Source: https://docs.getpara.com/web/setup/vue/vite
A guide to integrate the Para Modal (React-based) into your Vue application powered by Vite.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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
First, install **both** the Para React SDK **and** the React dependencies, since the modal relies on React under the
hood:
```bash npm
npm install @getpara/react-sdk react react-dom @vitejs/plugin-react
```
```bash yarn
yarn add @getpara/react-sdk react react-dom @vitejs/plugin-react
```
```bash pnpm
pnpm add @getpara/react-sdk react react-dom @vitejs/plugin-react
```
```bash bun
bun add @getpara/react-sdk react react-dom @vitejs/plugin-react
```
## Setting Up Polyfills
Like any React + Vite project that may rely on Node modules (`crypto`, `buffer`, `stream`), you'll likely need
polyfills:
```bash npm
npm install vite-plugin-node-polyfills
```
```bash yarn
yarn add vite-plugin-node-polyfills -D
```
```bash pnpm
pnpm add vite-plugin-node-polyfills -D
```
```bash bun
bun add vite-plugin-node-polyfills
```
Then, update your `vite.config.js` or `vite.config.ts`:
```js vite.config.js
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
import { Environment, ParaWeb } from "@getpara/react-sdk";
const PARA_API_KEY = import.meta.env.VITE_PARA_API_KEY;
export const para = new ParaWeb(Environment.BETA, PARA_API_KEY);
```
Para offers two hosted environments: `Environment.BETA` (alias `Environment.DEVELOPMENT`) for testing, and
`Environment.PROD` (alias `Environment.PRODUCTION`) for live use. Select the environment that matches your current
development phase.
### Building a Connector for the React Modal
To display the React-based Para Modal from within Vue, we'll create a "connector" that mounts the React modal into a DOM
element. You can store this in a file such as `para-modal-connector.tsx`:
```tsx para-modal-connector.tsx
import React from "react";
import { createRoot } from "react-dom/client";
import { ParaModal, ParaModalProps } from "@getpara/react-sdk";
import "@getpara/react-sdk/styles.css";
export const createParaModalConnector = (targetEl: HTMLElement, props: Omit) => {
const root = createRoot(targetEl);
const state = {
isOpen: false,
render: (isOpen: boolean) => {
state.isOpen = isOpen;
root.render(
{
state.isOpen = false;
state.render(false);
props.onClose?.();
}}
/>
);
},
};
return {
open: () => state.render(true),
close: () => state.render(false),
isOpen: () => state.isOpen,
unmount: () => root.unmount(),
};
};
```
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
Para Modal Starter (Vue + Vite)
Open Para Modal
```
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
{}}
appName="My Vue App"
oAuthMethods={["GOOGLE", "DISCORD"]}
/>
```
Pass them through your connector's `props` object 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:
## Next Steps
After integrating Para, you can explore other features and integrations to enhance your Para experience.
# Next.js
Source: https://docs.getpara.com/web/troubleshooting/nextjs
Troubleshooting Para integration with Next.js applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Integrating Para with Next.js can present unique challenges. This guide outlines common issues you might encounter and
provides effective solutions and best practices to ensure a seamless integration.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before diving into specific issues, try these general troubleshooting steps:
```bash
rm -rf .next
rm -rf node_modules
npm cache clean --force
```
`bash npm install `
```bash
npm update @getpara/web-sdk @getpara/react-sdk
```
## Common Issues and Solutions
**Problem**: Para is a client-side library and may cause issues with Server-Side Rendering (SSR) in Next.js.
**Solution**: Use dynamic imports to load Para components only on the client side.
```jsx
import dynamic from "next/dynamic";
const ParaComponent = dynamic(() => import("@getpara/react-sdk").then((mod) => mod.ParaComponent), {
ssr: false,
});
```
**Problem**: Next.js may not transpile Para packages by default, leading to build errors.
**Solution**: Add Para packages to the `transpilePackages` configuration in `next.config.js`:
```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: [
"@getpara/web-sdk",
"@getpara/react-sdk",
// Add any other Para-related packages here
],
// ... other configurations
};
module.exports = nextConfig;
```
**Problem**: Para's CSS files may not load correctly in Next.js.
**Solution**: Import Para's CSS file in your `_app.js` or `_app.tsx` file:
```jsx
import "@getpara/react-sdk/dist/index.css";
function MyApp({ Component, pageProps }) {
return ;
}
export default MyApp;
```
**Problem**: Para API key not being recognized in the application.
**Solution**: Ensure you're setting the environment variable correctly in your `.env.local` file and prefixing it with `NEXT_PUBLIC_` for client-side access:
```
NEXT_PUBLIC_PARA_API_KEY=your_api_key_here
```
Then, use it in your code like this:
```javascript
const para = new Para(Environment.BETA, process.env.NEXT_PUBLIC_PARA_API_KEY);
```
**Problem**: Errors related to missing Node.js modules like `crypto` or `buffer`.
**Solution**: While not always necessary for Next.js, you may need to add polyfills. Update your `next.config.js`:
```javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
// ... other configs
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
...config.resolve.fallback,
crypto: require.resolve("crypto-browserify"),
stream: require.resolve("stream-browserify"),
buffer: require.resolve("buffer"),
};
}
return config;
},
};
module.exports = nextConfig;
```
### Best Practices
1. **Use the Latest Versions**: Always use the latest versions of Next.js and Para SDK to ensure compatibility and
access to the latest features.
2. **Client-Side Rendering for Para Components**: Whenever possible, render Para components on the client-side to avoid
SSR-related issues.
3. **Error Boundary**: Implement error boundaries to gracefully handle any runtime errors related to Para integration.
4. **Environment-Specific Configurations**: Use Next.js environment configurations to manage different settings for
development and production environments.
By following these troubleshooting steps and best practices, you should be able to resolve most common issues when
integrating Para with your Next.js application.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please contact our team 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.
# React with Vite
Source: https://docs.getpara.com/web/troubleshooting/react-vite
Overcoming Para integration challenges in Vite-powered React applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Vite's approach to building React applications can introduce unique considerations when integrating Para. This guide
addresses frequent issues and offers tailored solutions to ensure a successful implementation.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before diving into specific issues, try these general troubleshooting steps:
`bash rm -rf node_modules npm cache clean --force `
`bash npm install `
`bash npm update @getpara/react-sdk `
`bash npm run build `
## Common Issues and Solutions
**Problem**: Vite doesn't include Node.js polyfills by default, which can cause issues with packages that depend on Node.js built-ins like `buffer` or `crypto`.
**Solution**: Add the necessary polyfills using the `vite-plugin-node-polyfills` plugin. Adjust the configuration as needed for your specific requirements:
1. Install the plugin:
```bash
npm install --save-dev vite-plugin-node-polyfills
```
2. Update your `vite.config.js`:
```javascript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { nodePolyfills } from "vite-plugin-node-polyfills";
export default defineConfig({
plugins: [
react(),
nodePolyfills({
include: ["buffer", "crypto", "stream", "util"],
}),
],
// ... other configurations
});
```
**Problem**: Environment variables not being recognized in your application.
**Solution**: Ensure you're prefixing your environment variables with `VITE_` and accessing them correctly:
1. In your `.env` file:
```
VITE_PARA_API_KEY=your_api_key_here
```
2. In your code:
```javascript
const para = new Para(Environment.BETA, import.meta.env.VITE_PARA_API_KEY);
```
**Problem**: Para's CSS files not loading correctly.
**Solution**: Import Para's CSS file in your main `App.jsx` or `index.jsx`:
```jsx
import "@getpara/react-sdk/dist/index.css";
function App() {
// Your app code
}
export default App;
```
### Best Practices
1. **Use the Latest Versions**: Always use the latest versions of Vite, React, and Para SDK to ensure compatibility and
access to the latest features.
2. **Error Handling**: Implement error boundaries to gracefully handle any runtime errors related to Para integration.
3. **Development vs Production**: Use environment-specific configurations to manage different settings for development
and production builds. Para provides environment-specific API keys.
By following these troubleshooting steps and best practices, you should be able to resolve most common issues when
integrating Para with your React application using Vite.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please contact our team 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.
# Svelte
Source: https://docs.getpara.com/web/troubleshooting/svelte
Overcoming Para integration challenges in Svelte applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before diving into specific issues, try these general troubleshooting steps:
`bash rm -rf node_modules npm cache clean --force `
`bash npm install `
`bash npm update @getpara/react-sdk `
`bash npm run build `
Svelte applications require configuring some additional polyfills for svelte and react preprocessing. The following
example should cover these requirements.
## Adding Svelte and React + Vite Preprocessing
First, ensure `svelteKit` is installed and configured correctly within the project's `vite.config.ts` file.
```bash
npm i @sveltejs/kit
```
Then you'll need to add the appropriate preprocessing and adapter dependencies to the project's `svelte.config.ts` file.
```bash
npm i @sveltejs/vite-plugin-svelte @sveltejs/adapter-auto @svelte-preprocess @svelte-preprocess-react
```
Last, configure the `vite.config.js` and `svelte.config.js` project files to add preprocessing.
See the below code files for reference examples.
```javascript vite.config.js
import { sveltekit } from "@sveltejs/kit/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [sveltekit()],
});
```
```javascript svelte.config.js
import adapter from "@sveltejs/adapter-auto";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
import preprocessReact from "svelte-preprocess-react/preprocessReact";
/**
* This will add autocompletion if you're working with SvelteKit
*
* @type {import('@sveltejs/kit').Config}
*/
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess(), preprocessReact()],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
},
};
export default config;
```
For more info, including CommonJS style configs, please see the
## Example
Explore our example implementation of SvelteKit and Svelte with Vite:
### Best Practices
1. **Use the Latest Versions**: Always use the latest versions of Svelte, React, and Para SDK to ensure compatibility
and access to the latest features.
2. **Error Handling**: Implement error boundaries to gracefully handle any runtime errors related to Para integration.
3. **Development vs Production**: Use environment-specific configurations to manage different settings for development
and production builds. Para provides environment-specific API keys.
By following these troubleshooting steps and best practices, you should be able to resolve most common issues when
integrating Para with your React application using Vite.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please contact our team 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.
# TanStack Start Troubleshooting
Source: https://docs.getpara.com/web/troubleshooting/tanstack-start
Overcoming Para integration challenges in TanStack Start applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
TanStack Start's server-side rendering (SSR) capabilities can introduce unique challenges when integrating Para SDK. This guide addresses frequent issues and offers tailored solutions to ensure a successful implementation.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before diving into specific issues, try these general troubleshooting steps:
```bash
rm -rf node_modules
npm cache clean --force
```
```bash
npm install
```
```bash
npm update @getpara/react-sdk
```
```bash
npm run build
```
## Common Issues and Solutions
**Problem**: Para API key environment variables not being recognized in your application.
**Solution**: Ensure you're using the correct prefix for environment variables in TanStack Start and accessing them properly:
1. In your `.env` file:
```
VITE_PARA_API_KEY=your_api_key_here
VITE_PARA_ENVIRONMENT=BETA
```
2. In your constants file:
```javascript
// src/constants.ts
export const API_KEY = import.meta.env.VITE_PARA_API_KEY || "";
export const ENVIRONMENT = import.meta.env.VITE_PARA_ENVIRONMENT || "BETA";
```
3. When using these in your ParaProvider:
```jsx
{children}
```
**Problem**: Missing or improperly configured Node.js polyfills causing errors like `crypto is not defined` or similar issues.
**Solution**: Configure the polyfills specifically for the client bundle only:
1. Install the plugin:
```bash
npm install --save-dev vite-plugin-node-polyfills
```
2. Update your `app.config.ts` to apply polyfills only to the client bundle:
```typescript
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()],
},
},
},
});
```
**Important**: Do NOT configure nodePolyfills in the top-level vite plugins array, as this will apply the polyfills to the server bundle and can cause conflicts.
**Problem**: Browser-specific modules not being resolved correctly, leading to missing APIs or incorrect environment detection.
**Solution**: Add the `process.browser` definition to help modules determine the execution environment:
```typescript
// app.config.ts
export default defineConfig({
vite: {
define: {
"process.browser": true,
},
// other configurations...
},
// other configurations...
});
```
This setting is critical for ensuring that browser-specific code paths are correctly resolved during bundling.
**Problem**: Errors like `styled.div is not a function` or `Cannot read properties of undefined (reading 'div')` during server rendering.
**Solution**: Ensure Para components are only rendered on the client side by using both `React.lazy` for dynamic imports and `ClientOnly` from TanStack Router:
```jsx
import React from "react";
import { ClientOnly } from "@tanstack/react-router";
// Lazy load Para component to prevent server-side evaluation
const LazyParaProvider = React.lazy(() =>
import("@getpara/react-sdk").then((mod) => ({
default: mod.ParaProvider
}))
);
export function Providers({ children }) {
return (
{children}
);
}
```
The combination of `React.lazy` and `ClientOnly` ensures that Para components are not only rendered on the client side but also that the modules are not evaluated on the server.
**Problem**: Even with `ClientOnly`, you're still seeing styled-components errors during server rendering.
**Solution**: Module-level evaluation can still happen on the server even if the component isn't rendered. Make sure all Para imports are lazy loaded:
1. Don't import directly from Para SDK at the module level:
```jsx
// AVOID this at the top level:
import { ParaModal, useModal } from "@getpara/react-sdk";
```
2. Instead, create wrapper components for all Para components:
```jsx
// Create a component file like ParaContainer.tsx
export function ParaContainer() {
// Import and use Para components here
const { openModal } = useModal();
return (
<>
Open Para Modal
>
);
}
```
3. Then lazy load these wrapper components:
```jsx
const LazyParaContainer = React.lazy(() =>
import("~/components/ParaContainer").then((mod) => ({
default: mod.ParaContainer,
}))
);
function HomePage() {
return (
Loading...}>
);
}
```
**Problem**: The Para modal appears transparent or without proper styling.
**Solution**: Import Para's CSS file in your component:
```jsx
// In your main component or route file:
import "@getpara/react-sdk/styles.css";
// Then use Para components as usual
```
Ensure this import is included in the component that uses Para components or in a parent component that wraps them.
## Best Practices for TanStack Start Integration
1. **Client-Side Only Rendering**: Always use both `React.lazy` and `ClientOnly` for Para components to avoid SSR issues.
2. **Polyfill Strategy**: Configure node polyfills only for the client bundle using the `routers.client.vite.plugins` config.
3. **Environment Awareness**: Set `process.browser` to true to help modules determine the execution environment.
4. **Component Boundaries**: Clearly define boundaries between server and client components to prevent hydration mismatches.
5. **Error Handling**: Implement error boundaries to gracefully handle any runtime errors related to Para integration.
6. **Development vs Production**: Use environment-specific configurations to manage different settings for development and production builds.
By following these troubleshooting steps and best practices, you should be able to resolve most common issues when integrating Para with your TanStack Start application.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please contact our team 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.
# Flutter SDK API
Source: https://docs.getpara.com/flutter/api/sdk
Documentation for the Para SDK in Flutter, including methods for wallet management, authentication, and transactions.
## Para Class
The `Para` class provides a Flutter interface for the Para SDK, managing wallet operations, authentication, and
transactions.
### Properties
The environment configuration for the Para SDK.
The API key used for authentication with Para services.
Optional URI to override the default JavaScript bridge.
The relying party ID for WebAuthn operations.
### Methods
Constructs a new Para instance.
The environment to use for the Para SDK.
The API key for authentication.
Optional URI to override the default JavaScript bridge.
The relying party ID for WebAuthn operations.
Initializes the Para SDK. Call this method immediately after constructing an instance.
Clears storage associated with the Para SDK.
Whether to retain the Paillier secret key.
Sets the email of the currently logged in user.
The email to set.
Gets the email of the currently logged in user, if one exists.
Logs in a user using passkeys.
Generates a passkey for the user.
The user's email.
The biometrics ID obtained from verification.
Determines if a user exists with the given email address.
The email to check.
Creates a new user with the given email address.
The email for the new user.
Supplies email verification code obtained from prompting the user.
The verification code to verify.
Sets up two-factor authentication for the user.
The URI for setting up 2FA in an authenticator app.
Turns on two-factor authentication for the user.
The verification code for enabling 2FA.
Checks if 2FA is enabled for the user.
Creates a wallet for the user.
Whether to skip distributable wallet creation.
Signs a message using the specified wallet.
The ID of the wallet to use for signing.
The base64-encoded message to sign.
Signs a transaction using the specified wallet.
The ID of the wallet to use for signing.
The RLP encoded transaction in base64.
The chain ID of the network.
Sends a transaction using the specified wallet.
The ID of the wallet to use for sending.
The RLP encoded transaction in base64.
The chain ID of the network.
Logs the user out.
Instructs the SDK to dispose of any resources that require explicit disposal.
# External Wallets
Source: https://docs.getpara.com/flutter/guides/external-wallets
Instructions for using external wallets in the flutter sdk.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Overview
This guide outlines how to integrate and use external cryptocurrency wallets with our Flutter SDK. It covers the setup,
deep-linking protocols, authentication, and transaction signing processes for wallets such as MetaMask and Phantom.
## Deeplinking
Our Flutter SDK communicates with other apps via deeplinking. To enable deeplinking for your application, you will need
to modify the AndroidManifest.xml for android and Info.plist for iOS.
For Android, add an intent filter, which should look like this.
```xml
```
For iOS go to TARGETS > App Name > Info > URL Types and add a new URL type. In the URL Schemes field, add your scheme.
This can be any string you want, but it must be unique to your app.
## Phantom Connector
### Setup
In the same place where you instantiate the Para Client, you will need to also instantiate the Phantom Connector.
```dart
final para = Para(
environment: Environment.beta,
apiKey: apiKey,
);
final phantomConnector = ParaPhantomConnector(
para: para, appUrl: , deepLink: "");
```
### Connecting
After setup, all you need to do is call
```dart
phantomConnector.connect();
```
This will open the Phantom wallet and connect it to your app.
### Sign Message
To sign a message, you can call
```dart
phantomConnector
.signMessage("Message to sign! Hello World")
.then((onValue) => {
print(onValue); // Do whatever you want with the signed message
});
```
### Sign Transaction
This assumes usage of the package.
To sign a transaction, you can call
```dart
final cluster = Cluster.mainnet;
final connection = Connection(cluster);
final BlockhashWithExpiryBlockHeight blockhash =
await connection.getLatestBlockhash();
final transaction = Transaction.v0(
payer:
Pubkey.fromBase58("Ez9MDm59vRftZS63KSbTdM4ujUEwftRC3wkRmMxu5XWz"),
recentBlockhash: blockhash.blockhash,
instructions: [
SystemProgram.transfer(
fromPubkey: Pubkey.fromBase58(
"Ez9MDm59vRftZS63KSbTdM4ujUEwftRC3wkRmMxu5XWz"),
toPubkey: Pubkey.fromBase58(
"HVMgc1okoZ1fzkpSAABoirViU83rqNRnVcEisjtgdNZC"),
lamports: solToLamports(0.5),
),
]);
final signedTransaction =
await phantomConnector.signTransaction(transaction);
```
### Sign and Send Transaction
This assumes usage of the package.
To sign a transaction, you can call
```dart
final cluster = Cluster.mainnet;
final connection = Connection(cluster);
final BlockhashWithExpiryBlockHeight blockhash =
await connection.getLatestBlockhash();
final transaction = Transaction.v0(
payer:
Pubkey.fromBase58("Ez9MDm59vRftZS63KSbTdM4ujUEwftRC3wkRmMxu5XWz"),
recentBlockhash: blockhash.blockhash,
instructions: [
SystemProgram.transfer(
fromPubkey: Pubkey.fromBase58(
"Ez9MDm59vRftZS63KSbTdM4ujUEwftRC3wkRmMxu5XWz"),
toPubkey: Pubkey.fromBase58(
"HVMgc1okoZ1fzkpSAABoirViU83rqNRnVcEisjtgdNZC"),
lamports: solToLamports(0.5),
),
]);
final signature =
await phantomConnector.signAndSendTransaction(transaction);
```
## MetaMask Connector
### Setup
In the same place where you instantiate the Para Client, you will need to also instantiate the Phantom Connector.
```dart
final para = Para(
environment: Environment.beta,
apiKey: apiKey,
);
final metamaskConnector = ParaMetaMaskConnector(
para: para, appUrl: , deepLink: "");
```
### Connecting
After setup, all you need to do is call
```dart
metamaskConnector.connect();
```
This will open the MetaMask wallet and connect it to your app.
### Sign Message
To sign a message, you can call
```dart
metamaskConnector
.signMessage(
"Message to sign! Hello World", metamaskConnector.accounts.first) // you can select whichever account if multiple
.then((onValue) => {
print(onValue); // Do whatever you want with the signed message
});
```
### Send Transaction
This assumes usage of the package.
To send a transaction, you can call
```dart
final transaction = Transaction(
from: EthereumAddress.fromHex(metamaskConnector.accounts.first),
to: EthereumAddress.fromHex('0x13158486860B81Dee9e43Dd0391e61c2F82B577F'),
value: EtherAmount.inWei(BigInt.from(10000000000000000)),
maxGas: 100000,
gasPrice: EtherAmount.inWei(BigInt.from(1000000000)),
);
metamaskConnector
.sendTransaction(transaction, metamaskConnector.accounts.first)
.then((onValue) => {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Transaction signed: $onValue'),
),
)
});
```
# Wallet Pregeneration
Source: https://docs.getpara.com/flutter/guides/pregen
Create and manage pregenerated wallets for users in Flutter applications
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
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
import 'package:para/para.dart';
import 'package:para_flutter/client/para.dart';
Future> createPregenWallets() async {
final pregenWallets = await para.createPregenWalletPerType(
pregenIdentifier: "user@example.com",
pregenIdentifierType: PregenIdentifierType.email,
types: [WalletType.evm], // Optionally specify wallet types
);
// Get the user share
final userShare = await para.getUserShare();
// 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
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
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
import 'dart:convert';
import 'package:para_flutter/client/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 signature = await para.signMessage(
walletId: walletId,
messageBase64: messageBase64,
);
return signature;
}
```
### 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
import 'package:device_info_plus/device_info_plus.dart';
import 'package:para/para.dart';
import 'package:para_flutter/client/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 pregenWallets = await para.createPregenWalletPerType(
pregenIdentifier: "device-$deviceId",
pregenIdentifierType: PregenIdentifierType.customId,
);
// Store the user share in device-specific secure storage
final userShare = await para.getUserShare();
await storeUserShare(userShare);
return pregenWallets;
}
```
Seamlessly introduce blockchain functionality to your existing app users without requiring them to understand wallets or crypto.
```dart
import 'package:para/para.dart';
import 'package:para_flutter/client/para.dart';
Future> createWalletForExistingUser(String userId) async {
final pregenIdentifier = "user-$userId";
try {
final pregenWallets = await para.createPregenWalletPerType(
pregenIdentifier: pregenIdentifier,
pregenIdentifierType: PregenIdentifierType.customId,
);
final userShare = await para.getUserShare();
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
import 'package:para_flutter/client/para.dart';
Future claimWallet() async {
// Ensure user is authenticated
if (!(await para.isFullyLoggedIn())) {
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
final recoverySecret = await para.claimPregenWallets();
// 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 Troubleshooting Guide
Source: https://docs.getpara.com/flutter/troubleshooting
Common issues and solutions when integrating Para with Flutter applications
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
This guide addresses common issues you might encounter when integrating Para with your Flutter application. It provides
solutions and best practices to ensure a smooth integration.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before diving into specific issues, try these general troubleshooting steps:
1. **Clean the project and get dependencies**:
```bash
flutter clean
flutter pub get
```
2. **Update Flutter and dependencies**:
```bash
flutter upgrade
flutter pub upgrade
```
3. **Ensure Para package is up to date**: Check your `pubspec.yaml` file and update the Para package version if
necessary.
4. **Rebuild the project**:
```bash
flutter run
```
## Common Issues and Solutions
### 1. Package Not Found or Version Conflicts
**Problem**: Dart can't find the Para package or there are version conflicts with other dependencies.
**Solution**: Ensure your `pubspec.yaml` file is correctly configured:
```yaml
dependencies:
flutter:
sdk: flutter
para: ^latest_version
dependency_overrides:
# Add any necessary overrides here
```
After updating `pubspec.yaml`, run:
```bash
flutter pub get
```
### 2. Platform-Specific Setup Issues
**Problem**: Para features not working on specific platforms (iOS/Android).
**Solution**: Ensure platform-specific configurations are correct:
For iOS (`ios/Runner/Info.plist`):
```xml
CFBundleURLTypes
CFBundleURLSchemes
para
```
For Android (`android/app/build.gradle`):
```gradle
android {
defaultConfig {
...
minSdkVersion 21
}
}
```
### 3. Initialization Errors
**Problem**: Para fails to initialize or throws errors on startup.
**Solution**: Ensure proper initialization in your `main.dart`:
```dart
import 'package:para/para.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final para = Para(
environment: Environment.beta,
apiKey: 'YOUR_API_KEY',
);
runApp(MyApp(para: para));
}
```
### 4. Asynchronous Operation Errors
**Problem**: Errors when performing asynchronous operations with Para.
**Solution**: Ensure proper async/await usage and error handling:
```dart
try {
final wallet = await para.createWallet({skipDistribute: false});
// Handle successful wallet creation
} catch (e) {
print('Error creating wallet: $e');
// Handle error appropriately
}
```
### 5. UI Thread Blocking
**Problem**: Para operations blocking the UI thread.
**Solution**: Use `compute` function for heavy computations:
```dart
import 'package:flutter/foundation.dart';
Future createWalletAsync(Para para) async {
return compute(_createWallet, para);
}
Wallet _createWallet(Para para) {
return para.createWallet({skipDistribute: false});
}
// Usage
final wallet = await createWalletAsync(para);
```
### 6. Platform Channel Errors
**Problem**: Errors related to platform channel communication.
**Solution**: Ensure the latest version of Para Flutter plugin is used and platform-specific code is correctly
implemented. If issues persist, check the plugin's GitHub repository for any known issues or updates.
### Best Practices
1. **State Management**: Use a state management solution like Provider or Riverpod to manage Para's state across your
app.
2. **Error Handling**: Implement robust error handling and user feedback mechanisms for Para operations.
3. **Secure Storage**: Use Flutter's secure storage solutions for storing sensitive data related to Para.
4. **Offline Support**: Implement proper offline handling and synchronization strategies for Para operations.
5. **Testing**: Write unit and integration tests for your Para integration to catch issues early.
6. **Performance Monitoring**: Use Flutter's DevTools to monitor the performance impact of Para integration.
7. **Keep Updated**: Regularly check for updates to the Para Flutter plugin and update your integration accordingly.
## Debugging Tips
1. **Enable Verbose Logging**: Enable verbose logging for Para operations to get more detailed information:
```dart
Para(
environment: Environment.beta,
apiKey: 'YOUR_API_KEY',
logLevel: ParaLogLevel.verbose,
);
```
2. **Use Flutter DevTools**: Utilize Flutter DevTools for performance profiling and debugging.
3. **Platform-Specific Debugging**: For platform-specific issues, use Xcode for iOS and Android Studio for Android
debugging.
By following these troubleshooting steps and best practices, you should be able to resolve most common issues when
integrating Para with your Flutter application.
### Integration Support
If you're experiencing issues that aren't resolved by our troubleshooting resources, please contact our team 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.
# Swift SDK API
Source: https://docs.getpara.com/swift/api/sdk
Swift API for Para SDK
## ParaManager Class
The `ParaManager` class provides a Swift interface for the Para SDK, managing wallet operations, authentication, and
transactions.
### Properties
The current wallet associated with the ParaManager.
The current session state of the Para SDK.
The environment configuration for the Para SDK.
The API key used for authentication with Para services.
The WebKit view used for handling web-based operations.
### Methods
Initializes a new ParaManager instance.
The environment to use for the Para SDK.
The API key for authentication.
Initializes the Para SDK within the WebView.
Checks if a user exists with the given email.
The email to check.
Whether the user exists.
Creates a new user with the given email.
The email for the new user.
Logs in a user using passkeys.
The authorization controller for handling passkey operations.
Verifies an email using a verification code.
The verification code sent to the user's email.
The biometrics ID associated with the verification.
Generates a passkey for the user.
The user's email.
The biometrics ID obtained from verification.
The authorization controller for handling passkey operations.
Sets up two-factor authentication for the user.
The URI for setting up 2FA in an authenticator app.
Enables two-factor authentication for the user.
Checks if 2FA is set up for the user.
Whether 2FA is set up.
Creates a new wallet for the user.
Whether to skip distributable wallet creation.
Fetches all wallets associated with the user.
An array of user wallets.
Signs a message using the specified wallet.
The ID of the wallet to use for signing.
The message to sign.
The signed message.
Signs a transaction using the specified wallet.
The ID of the wallet to use for signing.
The RLP encoded transaction.
The chain ID of the network.
The signed transaction.
Sends a transaction using the specified wallet.
The ID of the wallet to use for sending.
The RLP encoded transaction.
The chain ID of the network.
The transaction signature.
# Mobile Examples
Source: https://docs.getpara.com/swift/examples
Explore Para's mobile integration examples for Swift
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
Para offers a comprehensive Swift example to help you integrate our technology into your iOS applications. Our example repository demonstrates minimal implementation requirements with clean, focused code that you can easily adapt to your specific application needs.
## Para Swift Example
Explore our Swift example showcasing Para integration with various features:
The Swift example demonstrates:
* Native iOS passkey authentication
* Face ID and Touch ID integration
* Wallet creation and management
* Transaction signing across multiple chains
* Session management
* Integration with Apple's Secure Enclave
* iCloud Keychain support
## 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.
# Cosmos Integration
Source: https://docs.getpara.com/swift/guides/cosmos
Integrate Para with Cosmos ecosystem in your Swift applications (Coming Soon)
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
**Coming Soon**: Cosmos integration for Swift applications is currently in development and will be available soon. Reach out to us at if you'd like to get early access.
Para will soon provide support for Cosmos ecosystem integration in Swift applications, allowing you to leverage Para's secure wallet infrastructure with Cosmos-based blockchains.
# EVM Integration
Source: https://docs.getpara.com/swift/guides/evm
Guide to working with EVM chains using the Para Swift SDK
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
The Para Swift SDK provides robust support for Ethereum Virtual Machine (EVM) compatible blockchains through the `ParaEvmSigner` class. This guide demonstrates how to perform common operations such as signing messages, signing transactions, and sending transactions on EVM chains.
## Getting Started
To work with EVM chains, you'll need:
1. An initialized `ParaManager` instance
2. A wallet with EVM capabilities
3. An RPC URL for your target EVM network
## Initializing the EVM Signer
First, create an instance of `ParaEvmSigner` by providing your Para manager, an RPC URL, and optionally a wallet ID:
```swift
import ParaSwift
// Create the EVM signer
let paraEvmSigner = try ParaEvmSigner(
paraManager: paraManager,
rpcUrl: "https://sepolia.infura.io/v3/YOUR_API_KEY",
walletId: wallet.id // Optional, can be selected later
)
```
Replace `YOUR_API_KEY` with your actual API key from providers like Infura, Alchemy, or your own RPC node.
## Selecting a Wallet
If you didn't specify a wallet ID when creating the signer, or if you want to switch to a different wallet, use the `selectWallet` method:
```swift
// Select a wallet for signing
try await paraEvmSigner.selectWallet(walletId: wallet.id)
```
## Signing Messages
Para makes it easy to sign arbitrary messages with your EVM wallet:
```swift
// Create a message and convert to base64
let message = "Hello, Ethereum!"
let messageBytes = message.data(using: .utf8)!
let base64Message = messageBytes.base64EncodedString()
// Sign the message
let signature = try await paraManager.signMessage(
walletId: wallet.id,
message: base64Message
)
```
## Working with Transactions
### Creating an EVM Transaction
Para uses the `EVMTransaction` model to represent transactions:
```swift
import BigInt
// Create transaction parameters
let transaction = EVMTransaction(
to: "0x301d75d850c878b160ad9e1e3f6300202de9e97f",
value: BigUInt("1000000000000000")!, // 0.001 ETH in wei
gasLimit: BigUInt("21000")!,
gasPrice: nil, // For Type 2 transactions (EIP-1559)
maxPriorityFeePerGas: BigUInt("1000000000")!, // 1 Gwei
maxFeePerGas: BigUInt("3000000000")!, // 3 Gwei
nonce: BigUInt("0")!, // Transaction count for the address
chainId: BigUInt("11155111")!, // Sepolia testnet
smartContractAbi: nil, // Optional: ABI for contract interactions
smartContractFunctionName: nil, // Optional: Function to call
smartContractFunctionArgs: [], // Optional: Function arguments
smartContractByteCode: nil, // Optional: For contract deployment
type: 2 // Transaction type (2 for EIP-1559)
)
```
### Signing a Transaction
To sign a transaction without broadcasting it to the network:
```swift
// Sign the transaction
let signedTransaction = try await paraEvmSigner.signTransaction(
transactionB64: transaction.b64Encoded()
)
```
### Sending a Transaction
To sign and broadcast a transaction in a single step:
```swift
// Sign and send the transaction
let transactionHash = try await paraEvmSigner.sendTransaction(
transactionB64: transaction.b64Encoded()
)
```
## Interacting with Smart Contracts
Para's `EVMTransaction` model supports smart contract interactions:
### Calling a Contract Function (Read-Only)
```swift
// Example of creating a transaction for calling a read function
let contractTransaction = EVMTransaction(
to: "0x123abc...", // Contract address
value: BigUInt(0), // No value sent
gasLimit: BigUInt("100000")!,
gasPrice: nil,
maxPriorityFeePerGas: BigUInt("1000000000")!,
maxFeePerGas: BigUInt("3000000000")!,
nonce: BigUInt("5")!,
chainId: BigUInt("11155111")!,
smartContractAbi: """
[
{
"inputs": [],
"name": "retrieve",
"outputs": [{"internalType":"uint256","name":"","type":"uint256"}],
"stateMutability":"view",
"type":"function"
}
]
""",
smartContractFunctionName: "retrieve",
smartContractFunctionArgs: [],
smartContractByteCode: nil,
type: 2
)
```
### Sending a Transaction to a Contract (Write)
```swift
// Example of creating a transaction for a state-changing function
let contractWriteTransaction = EVMTransaction(
to: "0x123abc...", // Contract address
value: BigUInt(0), // No value sent
gasLimit: BigUInt("150000")!,
gasPrice: nil,
maxPriorityFeePerGas: BigUInt("1000000000")!,
maxFeePerGas: BigUInt("3000000000")!,
nonce: BigUInt("6")!,
chainId: BigUInt("11155111")!,
smartContractAbi: """
[
{
"inputs": [{"internalType":"uint256","name":"num","type":"uint256"}],
"name": "store",
"outputs": [],
"stateMutability":"nonpayable",
"type":"function"
}
]
""",
smartContractFunctionName: "store",
smartContractFunctionArgs: ["42"], // Arguments as strings
smartContractByteCode: nil,
type: 2
)
// Send the transaction
try await paraEvmSigner.sendTransaction(
transactionB64: contractWriteTransaction.b64Encoded()
)
```
### Deploying a Smart Contract
```swift
// Example of creating a transaction for contract deployment
let deployTransaction = EVMTransaction(
to: nil, // nil for contract deployment
value: BigUInt(0),
gasLimit: BigUInt("4000000")!,
gasPrice: nil,
maxPriorityFeePerGas: BigUInt("1500000000")!,
maxFeePerGas: BigUInt("3500000000")!,
nonce: BigUInt("7")!,
chainId: BigUInt("11155111")!,
smartContractAbi: nil,
smartContractFunctionName: nil,
smartContractFunctionArgs: [],
smartContractByteCode: "0x608060405234801561001057600080fd5b50...", // Contract bytecode
type: 2
)
// Send the transaction to deploy the contract
try await paraEvmSigner.sendTransaction(
transactionB64: deployTransaction.b64Encoded()
)
```
## Working with Different Networks
Para supports any EVM-compatible network. Simply update the RPC URL and chain ID to target different networks:
```swift
// Ethereum Mainnet
let mainnetSigner = try ParaEvmSigner(
paraManager: paraManager,
rpcUrl: "https://mainnet.infura.io/v3/YOUR_API_KEY",
walletId: wallet.id
)
// Create transaction with appropriate chain ID
let mainnetTransaction = EVMTransaction(
// ... other parameters
chainId: BigUInt("1")!, // Ethereum Mainnet
// ... other parameters
)
```
## Example Integration
# External Wallets
Source: https://docs.getpara.com/swift/guides/external-wallets
Instructions for using external wallets with the ParaSwift SDK.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Overview
This guide outlines how to integrate and use external cryptocurrency wallets with the ParaSwift SDK. It covers setup, deep-linking protocols, authentication, and transaction signing processes for wallets such as MetaMask.
## Deep Linking
The ParaSwift SDK communicates with other apps via deep linking. To enable deep linking for your application, you'll need to configure your app appropriately.
### Configure URL Schemes
First, you need to configure your app to handle custom URL schemes:
1. Open your app's Info.plist file
2. Add a new entry for `LSApplicationQueriesSchemes` as an array
3. Add the URL schemes of supported wallets as strings to this array
```xml
LSApplicationQueriesSchemes
metamask
```
### Configure URL Types
Next, you need to set up URL Types to handle callbacks from external wallets:
1. In Xcode, select your app target
2. Go to "Info" tab
3. Expand "URL Types"
4. Click the "+" button to add a new URL Type
5. Set the "Identifier" to your app's bundle identifier
6. Set the "URL Schemes" to a unique scheme for your app
## MetaMask Connector
### Setup
Create and initialize the MetaMask connector with your app's configuration:
```swift
import ParaSwift
// Create MetaMask configuration
let bundleId = Bundle.main.bundleIdentifier ?? ""
let metaMaskConfig = MetaMaskConfig(
appName: "Your App Name",
appId: bundleId,
apiVersion: "1.0"
)
// Initialize the connector
let metaMaskConnector = MetaMaskConnector(
para: paraManager,
appUrl: "https://\(bundleId)", // Your app's URL
config: metaMaskConfig
)
```
### Handle Deep Links
Handle incoming deep links from MetaMask by adding a URL handler in your SwiftUI app:
```swift
@main
struct YourApp: App {
@StateObject private var metaMaskConnector = MetaMaskConnector(
para: paraManager,
appUrl: "https://your-app-url",
config: metaMaskConfig
)
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(metaMaskConnector)
.onOpenURL { url in
metaMaskConnector.handleURL(url)
}
}
}
}
```
### Connecting
To connect to MetaMask and retrieve the user's accounts:
```swift
do {
try await metaMaskConnector.connect()
// Access connected accounts via metaMaskConnector.accounts
} catch {
// Handle connection error
}
```
### Sign Message
To request a signature for a message from MetaMask:
```swift
guard let account = metaMaskConnector.accounts.first else { return }
do {
let signature = try await metaMaskConnector.signMessage(
"Message to sign! Hello World",
account: account
)
// Use the signature
} catch {
// Handle signing error
}
```
### Send Transaction
To send a transaction through MetaMask using the standard transaction parameters:
```swift
import Web3Core
guard let account = metaMaskConnector.accounts.first else { return }
do {
let valueInWei = Web3Core.Utilities.parseToBigUInt("0.001", units: .ether)!
let gasLimit = Web3Core.Utilities.parseToBigUInt("100000", units: .wei)!
let transaction = EVMTransaction(
to: "0x13158486860B81Dee9e43Dd0391e61c2F82B577F",
value: valueInWei,
gasLimit: gasLimit
)
let txHash = try await metaMaskConnector.sendTransaction(transaction, account: account)
// Transaction sent successfully, use txHash
} catch {
// Handle transaction error
}
```
Alternatively, you can use a raw transaction dictionary format:
```swift
guard let account = metaMaskConnector.accounts.first else { return }
let transaction: [String: String] = [
"from": account,
"to": "0x13158486860B81Dee9e43Dd0391e61c2F82B577F",
"value": "0x38D7EA4C68000", // 0.001 ETH in wei (hex)
"gasLimit": "0x186A0" // 100000 in hex
]
do {
let txHash = try await metaMaskConnector.sendTransaction(
transaction,
account: account
)
// Transaction sent successfully
} catch {
// Handle transaction error
}
```
### Properties
The MetaMask connector provides several useful properties:
```swift
// Check if connected to MetaMask
let isConnected = metaMaskConnector.isConnected
// Get list of connected accounts
let accounts = metaMaskConnector.accounts
// Get current chain ID (e.g., "0x1" for Ethereum mainnet)
let chainId = metaMaskConnector.chainId
```
## Advanced Configuration
### Customizing MetaMask Connection
You can customize various aspects of the MetaMask connection by modifying the MetaMaskConfig:
```swift
let metaMaskConfig = MetaMaskConfig(
appName: "Your App Name",
appId: bundleId,
apiVersion: "1.0",
appIcon: "https://yourapp.com/icon.png" // Optional custom icon URL
)
```
### Working with Different Networks
MetaMask supports multiple networks. You can check the current network and adjust your app's behavior accordingly:
```swift
switch metaMaskConnector.chainId {
case "0x1":
// Ethereum Mainnet
case "0x5":
// Goerli Testnet
case "0x89":
// Polygon
default:
// Other network
}
```
# Wallet Pregeneration & Claiming
Source: https://docs.getpara.com/swift/guides/pregen
Pregenerate and claim wallets in your Swift applications (Coming Soon)
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
**Coming Soon**: Wallet pregeneration and claiming functionality for Swift applications is currently in development and will be available soon. Reach out to us at if you'd like to get early access.
Para will soon provide support for wallet pregeneration and claiming in Swift applications, allowing you to create blockchain wallets for users before they complete authentication.
# Swift Session Management
Source: https://docs.getpara.com/swift/guides/sessions
Guide to managing authentication sessions in Para for Swift applications
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
Para Swift SDK provides robust session management, automatically tracking authentication states and ensuring secure, seamless interactions. Effective session management is critical for security, usability, and reliability of your application.
## Session States
Para manages sessions using the `ParaSessionState` enum:
* `unknown`: Initial state before the status is determined.
* `inactive`: SDK initialized but no active session.
* `active`: Session is active but user not fully logged in.
* `activeLoggedIn`: User is fully logged in with an active session.
### Observing Session States
Utilize SwiftUI's Combine or other state management tools to observe changes:
```swift
@StateObject private var paraManager: ParaManager
.onChange(of: paraManager.sessionState) { newState in
switch newState {
case .activeLoggedIn:
print("User authenticated")
case .inactive:
print("Session inactive")
default:
print("Session state: \(newState)")
}
}
```
## 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.
## Key Session Methods
* `isSessionActive() async throws -> Bool`: Checks if the session is currently valid before performing authenticated operations.
* `isFullyLoggedIn() async throws -> Bool`: Checks if the user is fully logged in with an active session.
* `exportSession() async throws -> String`: Exports session state as a string that can be used for advanced integration scenarios.
* `logout() async throws`: Clears the current session, removes all website data from the WebView, and resets the session state to inactive.
## Maintaining Active Sessions
For long-running applications, check session status before performing sensitive operations:
```swift
func performSensitiveOperation() {
Task {
do {
if try await paraManager.isSessionActive() {
// Proceed with sensitive operation
try await signTransaction(...)
} else {
// Handle session expiration - redirect to login
navigateToLogin()
}
} catch {
await MainActor.run {
isLoggedIn = false
}
}
}
}
```
## Refreshing Expired Sessions
When a session has expired, Para recommends initiating a full authentication flow rather than trying to refresh the session.
For Swift applications, always call `logout()` before reinitiating authentication when a session has expired to ensure all stored data is properly cleared.
```swift
import ParaSwift
func handleSessionExpiration() async {
do {
// When session expires, first clear storage
try await paraManager.logout()
// Then redirect to authentication screen
await MainActor.run {
// Navigate to authentication screen
}
} catch {
// Handle error
}
}
```
## Background Security
Clear sensitive data when app goes to background by logging out:
```swift
class AppDelegate: NSObject, UIApplicationDelegate {
func applicationDidEnterBackground(_ application: UIApplication) {
Task {
try? await paraManager.logout()
}
}
}
```
## Exporting Sessions to Your Server
In some advanced scenarios, you may need to export the session state:
```swift
func sendSessionToServer() async throws {
do {
// Export session without signing capabilities
let sessionString = try await paraManager.exportSession()
// Create URL request
var request = URLRequest(url: URL(string: "https://your-api.com/sessions")!)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Create request body
let body: [String: Any] = ["session": sessionString]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
// Send request
let (_, response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
// Handle success
} catch {
// Handle error
throw error
}
}
```
## Best Practices
### Check Sessions on App Launch
Verify session status when your app starts to determine if users need to reauthenticate:
```swift
import SwiftUI
import ParaSwift
class AppStartupManager: ObservableObject {
@Published var isLoggedIn = false
let paraManager: ParaManager
init(paraManager: ParaManager) {
self.paraManager = paraManager
checkSessionOnLaunch()
}
func checkSessionOnLaunch() {
Task {
do {
let isActive = try await paraManager.isSessionActive()
await MainActor.run {
if isActive {
// User is logged in
isLoggedIn = true
} else {
// Session not active, clear any lingering data
Task {
try? await paraManager.logout()
}
isLoggedIn = false
}
}
} catch {
await MainActor.run {
isLoggedIn = false
}
}
}
}
}
```
### Handle App Lifecycle Changes
Swift apps can be backgrounded and foregrounded, which may affect session status:
```swift
import SwiftUI
import ParaSwift
class LifecycleManager: ObservableObject {
let paraManager: ParaManager
init(paraManager: ParaManager) {
self.paraManager = paraManager
// Register for foreground notifications
NotificationCenter.default.addObserver(
self,
selector: #selector(appMovedToForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil
)
}
@objc func appMovedToForeground() {
// App came to foreground, check session
checkSession()
}
func checkSession() {
Task {
let isActive = try? await paraManager.isSessionActive()
if isActive != true {
try? await paraManager.logout()
await MainActor.run {
// Navigate to login screen
}
}
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
```
## Next Steps
Explore more advanced features and integrations with Para in Swift:
# Social Login
Source: https://docs.getpara.com/swift/guides/social-login
Instructions for implementing social login with the ParaSwift SDK.
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
## Overview
This guide outlines how to implement social login (OAuth) with the ParaSwift SDK. It covers setup, authentication flow, and handling both new and existing user scenarios with providers like Google, Apple, and Discord.
## Prerequisites
## 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.
You must have the ParaSwift SDK installed and configured in your project. If you haven't done this yet, please refer to our .
## Environment Setup
Before implementing social login, ensure your environment is properly configured:
1. Add the `webAuthenticationSession` environment value in your view
2. Add the `authorizationController` environment value for handling passkey operations after social login
```swift
@Environment(\.webAuthenticationSession) private var webAuthenticationSession
@Environment(\.authorizationController) private var authorizationController
```
## Implementing Social Login
### Setup
Create a view that will handle the social login process:
```swift
import SwiftUI
import ParaSwift
import AuthenticationServices
struct SocialLoginView: View {
@EnvironmentObject var paraManager: ParaManager
@Environment(\.webAuthenticationSession) private var webAuthenticationSession
@Environment(\.authorizationController) private var authorizationController
@State private var email = ""
@State private var error: Error?
}
```
### Initiating OAuth Authentication
To begin the OAuth flow with a specific provider, call the `oAuthConnect` method:
```swift
private func login(provider: OAuthProvider) {
Task {
do {
let email = try await paraManager.oAuthConnect(provider: provider, webAuthenticationSession: webAuthenticationSession)
handleLogin(email: email)
} catch {
self.error = error
}
}
}
```
### Handling Authentication Result
After a successful OAuth connection, you'll need to handle both new and existing user scenarios:
```swift
private func handleLogin(email: String) {
Task {
self.email = email
let userExists = try await paraManager.checkIfUserExists(email: email)
if userExists {
// User exists - complete login with passkey
try await paraManager.login(authorizationController: authorizationController, authInfo: EmailAuthInfo(email: email))
// Navigate to authenticated area of your app
} else {
// New user - create account
try await paraManager.createUser(email: email)
// Navigate to verification or onboarding view
}
}
}
```
### Creating Social Login Buttons
Here's an example of how to create buttons for different OAuth providers:
```swift
var body: some View {
VStack {
// Google Login Button
Button {
login(provider: .google)
} label: {
HStack(spacing: 15) {
Image("google")
.resizable()
.frame(width: 24, height: 24)
Text("Login with Google")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.tint(.primary)
.foregroundStyle(.background)
// Discord Login Button
Button {
login(provider: .discord)
} label: {
HStack(spacing: 15) {
Image("discord")
.resizable()
.frame(width: 24, height: 20)
Text("Login with Discord")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.tint(Color(uiColor: UIColor(rgb: 0x5865F2)))
// Apple Login Button
Button {
login(provider: .apple)
} label: {
HStack(spacing: 15) {
Image("apple")
.resizable()
.frame(width: 24, height: 24)
Text("Login with Apple")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
}
.padding()
}
```
## Available OAuth Providers
The ParaSwift SDK supports the following OAuth providers:
* Google (`.google`)
* Apple (`.apple`)
* Discord (`.discord`)
## Complete Example
Here's a complete example of a social login view:
```swift
import SwiftUI
import ParaSwift
import AuthenticationServices
struct SocialLoginView: View {
@EnvironmentObject var paraManager: ParaManager
@State private var navigationState: NavigationState = .idle
@Environment(\.webAuthenticationSession) private var webAuthenticationSession
@Environment(\.authorizationController) private var authorizationController
@State private var email = ""
@State private var error: Error?
@State private var showError = false
enum NavigationState {
case idle
case verification
case home
}
private func login(provider: OAuthProvider) {
Task {
do {
let email = try await paraManager.oAuthConnect(provider: provider, webAuthenticationSession: webAuthenticationSession)
await handleLogin(email: email)
} catch {
self.error = error
self.showError = true
}
}
}
private func handleLogin(email: String) async {
do {
self.email = email
let userExists = try await paraManager.checkIfUserExists(email: email)
if userExists {
try await paraManager.login(authorizationController: authorizationController, authInfo: EmailAuthInfo(email: email))
navigationState = .home
} else {
try await paraManager.createUser(email: email)
navigationState = .verification
}
} catch {
self.error = error
self.showError = true
}
}
var body: some View {
VStack {
Button {
login(provider: .google)
} label: {
HStack(spacing: 15) {
Image("google")
.resizable()
.frame(width: 24, height: 24)
Text("Login with Google")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.tint(.primary)
.foregroundStyle(.background)
Button {
login(provider: .discord)
} label: {
HStack(spacing: 15) {
Image("discord")
.resizable()
.frame(width: 24, height: 20)
Text("Login with Discord")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
.tint(Color(uiColor: UIColor(rgb: 0x5865F2)))
Button {
login(provider: .apple)
} label: {
HStack(spacing: 15) {
Image("apple")
.resizable()
.frame(width: 24, height: 24)
Text("Login with Apple")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
}
.alert("Connection Error", isPresented: $showError) {
Button("OK", role: .cancel) { }
} message: {
Text(error?.localizedDescription ?? "Unknown error occurred")
}
.navigationDestination(isPresented: Binding(
get: { navigationState == .verification },
set: { if !$0 { navigationState = .idle } }
)) {
VerifyEmailView(email: email)
}
.navigationDestination(isPresented: Binding(
get: { navigationState == .home },
set: { if !$0 { navigationState = .idle } }
)) {
HomeView()
}
.padding()
.navigationTitle("Social Login")
}
}
// Helper extension for UIColor from hex
extension UIColor {
convenience init(rgb: UInt) {
self.init(
red: CGFloat((rgb & 0xFF0000) >> 16) / 255.0,
green: CGFloat((rgb & 0x00FF00) >> 8) / 255.0,
blue: CGFloat(rgb & 0x0000FF) / 255.0,
alpha: CGFloat(1.0)
)
}
}
```
## Next Steps
After implementing social login, you might want to:
1. for new users
2. for session management
3. as an additional security layer
# Solana Integration
Source: https://docs.getpara.com/swift/guides/solana
Integrate Para with Solana in your Swift applications (Coming Soon)
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
**Coming Soon**: Solana integration for Swift applications is currently in development and will be available soon. Reach out to us at if you'd like to get early access.
Para will soon provide support for Solana integration in Swift applications, allowing you to leverage Para's secure wallet infrastructure with the Solana blockchain.
# Overview
Source: https://docs.getpara.com/swift/overview
An introduction to Swift integrations with Para
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
The Para Swift SDK enables developers to effortlessly integrate secure, non-custodial wallets directly into iOS apps, providing users with complete control over their digital assets. Designed for a native iOS experience, Para leverages Apple's passkeys and biometric authentication to ensure security and convenience, significantly simplifying blockchain integration.
## Getting Started with Swift
## Blockchain Ecosystem Integrations
Para works seamlessly with major blockchain ecosystems in your Swift apps, allowing you to leverage Para's authentication alongside chain-specific libraries and development tools.
## Advanced Features
Customize Para's behavior and extend its functionality with these advanced configuration guides.
## Authentication Options
Enhance your application's security and user experience with additional authentication methods.
## Implementation Examples
Explore complete implementation examples to accelerate your Swift development with Para.
# Setup
Source: https://docs.getpara.com/swift/setup
Step-by-step guide for integrating the Para Swift SDK into your iOS application
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
The Para Swift SDK enables you to integrate secure wallet features including creation, passkey-based authentication, and transaction signing into your iOS applications. This guide covers all necessary steps from installation to implementing authentication flows.
## 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.
## Installation
1. In your Xcode project, go to **File > Add Packages** or (in your target) **Frameworks, Libraries, and Embedded Content** and click **+**.
2. Enter `https://github.com/getpara/swift-sdk`
3. Select **Up to Next Major Version** ensuring `0.0.1 < 1.0.0`
4. Add the package to your app target and click **Add Package**.
To enable passkeys on iOS, you need to configure Associated Domains:
1. In Xcode, go to **Signing & Capabilities** for your app target
2. Click **+ Capability** and add **Associated Domains**
3. Add the following entries:
```
webcredentials:app.usecapsule.com
webcredentials:app.beta.usecapsule.com
```
4. Provide your Team ID + Bundle ID to Para, e.g. `A1B2C3D4E5.com.example.yourapp`
Without properly registering your domain with Para, passkey authentication flows will fail. Contact Para support if you encounter issues with passkey registration.
**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.
## Initialization
First, you need to initialize the Para manager in your app. Below is an example of initializing the SDK in a SwiftUI application:
```swift
import SwiftUI
import ParaSwift
@main
struct ExampleApp: App {
@StateObject private var paraManager: ParaManager
@StateObject private var appRootManager = AppRootManager()
init() {
let environmentString = Bundle.main.object(forInfoDictionaryKey: "PARA_ENVIRONMENT") as? String ?? "beta"
let environment: ParaEnvironment = environmentString == "beta" ? .beta : .prod
let apiKey = Bundle.main.object(forInfoDictionaryKey: "PARA_API_KEY") as! String
_paraManager = StateObject(wrappedValue: ParaManager(environment: environment, apiKey: apiKey))
}
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(paraManager)
}
}
}
```
## Authentication
Para supports email and phone authentication methods. Each method uses a secure, passkey-first approach for seamless biometric logins via Face ID or Touch ID.
Email authentication verifies users via a one-time password (OTP) sent to their email address and then sets up a biometric-enabled passkey for future logins.
First, check if the user already exists in the Para system:
```swift
let userExists = try await paraManager.checkIfUserExists(email: userEmail)
```
If the user exists, trigger the passkey login flow:
```swift
try await paraManager.login(
authorizationController: authorizationController,
authInfo: EmailAuthInfo(email: userEmail)
)
```
If the user does not exist, initiate the account creation process:
```swift
// Create user - this sends a verification code to the email
try await paraManager.createUser(email: userEmail)
// User enters verification code from their email
let verificationCode = "123456" // Replace with actual code from UI input
// Verify the code to get a biometrics ID
let biometricsId = try await paraManager.verify(verificationCode: verificationCode)
```
After verification, generate a passkey and create a wallet:
```swift
// Create passkey linked to the email
try await paraManager.generatePasskey(
identifier: userEmail,
biometricsId: biometricsId,
authorizationController: authorizationController
)
// Create wallet for the new user
try await paraManager.createWallet(type: .evm, skipDistributable: false)
```
Phone authentication verifies users via SMS OTP and sets up a biometric-enabled passkey for future logins.
First, check if the user already exists by phone number:
```swift
let userExists = try await paraManager.checkIfUserExistsByPhone(
phoneNumber: phoneNumber,
countryCode: "+1" // Replace with appropriate country code
)
```
If the user exists, trigger the passkey login flow:
```swift
try await paraManager.login(
authorizationController: authorizationController,
authInfo: PhoneAuthInfo(phone: phoneNumber, countryCode: "+1")
)
```
If the user does not exist, initiate the account creation process:
```swift
// Create user - this sends a verification code via SMS
try await paraManager.createUserByPhone(
phoneNumber: phoneNumber,
countryCode: "+1" // Replace with appropriate country code
)
// User enters verification code from SMS
let verificationCode = "123456" // Replace with actual code from UI input
// Verify the code to get a biometrics ID
let biometricsId = try await paraManager.verifyByPhone(verificationCode: verificationCode)
```
After verification, generate a passkey and create a wallet:
```swift
// Create passkey linked to the phone number
try await paraManager.generatePasskey(
identifier: "\(countryCode)\(phoneNumber)",
biometricsId: biometricsId,
authorizationController: authorizationController
)
// Create wallet for the new user
try await paraManager.createWallet(type: .evm, skipDistributable: false)
```
## Subsequent Logins
After initial setup, users can log in using their passkey, which triggers biometric authentication:
```swift
// For users who have already set up a passkey
try await paraManager.login(authorizationController: authorizationController)
```
This will prompt the user for Face ID or Touch ID verification before granting access to their wallets.
## Checking Authentication Status
You can check if a user is already authenticated:
```swift
let isLoggedIn = await paraManager.isFullyLoggedIn()
if isLoggedIn {
// User is authenticated, proceed to main app flow
} else {
// Show login/signup UI
}
```
## Sign out
To sign out a user and clear their session:
```swift
try await paraManager.logout()
```
## Basic Wallet Operations
After successful authentication, you can perform wallet operations:
```swift
// Get all user wallets
let wallets = try await paraManager.getWallets()
// Sign a simple message
let messageData = "Hello, Para!".data(using: .utf8)!
let base64Message = messageData.base64EncodedString()
let signature = try await paraManager.signMessage(
walletId: wallets.first!.id,
message: base64Message
)
```
For detailed transaction signing with specific blockchains (EVM, Solana, Cosmos), please refer to the respective blockchain integration guides.
## Example
For a complete implementation example, check out our Swift SDK example app:
## Next Steps
After integrating Para into your Flutter app, you can explore other features and integrations to enhance your Para experience.
# Troubleshooting
Source: https://docs.getpara.com/swift/troubleshooting
Solutions for common issues with the Para Swift SDK integration
export const Link = ({href, label}) => {
e.currentTarget.querySelector("#underline").style.height = "2px";
}} onMouseLeave={e => {
e.currentTarget.querySelector("#underline").style.height = "1px";
}}>
{label}
;
export const Card = ({imgUrl, iconUrl, fontAwesomeIcon, title, description, href, horizontal = false}) => {
return ;
};
This guide helps you identify and resolve common issues encountered while integrating the Para Swift SDK into your iOS application.
Using an LLM (ChatGPT, Claude) or Coding Assistant (Cursor, Github Copilot)? Here are a few tips:
1. Include the to ensure you're getting the most up-to-date help!
2. Check out the for an interactive LLM that leverages the Para Examples Hub!
## General Troubleshooting Steps
Before diving into specific issues, try these basic troubleshooting steps:
In Xcode, go to **Product → Clean Build Folder** (Option + Shift + Command + K).
For Swift Package Manager: **File → Packages → Update to Latest Package Versions**
For CocoaPods: Run `pod update` in your terminal.
Ensure you are using the latest Para SDK compatible with your minimum deployment target (iOS 13.0+).
Confirm your API key, Associated Domains, custom URL scheme, Team ID, and Bundle ID are correctly configured.
## Common Issues and Solutions
### Authentication Issues
**Error:** `ParaError.passkeyGenerationFailed`
**Solution:** Verify Associated Domains, Team ID, Bundle ID, and domain setup.
```swift
do {
try await paraManager.generatePasskey(
identifier: email,
biometricsId: biometricsId,
authorizationController: authorizationController
)
} catch ParaError.passkeyGenerationFailed {
print("Passkey generation failed. Check configurations.")
}
```
**Solution:** Confirm biometric setup on the device and check permissions in app settings.
Make sure your app includes the necessary privacy descriptions in Info.plist:
* `NSFaceIDUsageDescription` for Face ID
* Proper permission handling for biometric authentication
**Error:** `ParaError.userRejected`
**Solution:** Catch this error and offer a retry option to the user. This error occurs when the user cancels a biometric prompt or declines to allow the action.
**Solution:** Verify:
* Correct environment settings (BETA/prod)
* Proper user input (valid email/phone format)
* Active network connection
* You haven't hit rate limits for verification attempts
### Transaction Signing Issues
**Error:** `ParaError.invalidTransaction`
**Solution:** Verify transaction parameters, proper encoding (Base64), and integration with web3 libraries.
```swift
do {
try await paraEvmSigner.signTransaction(transactionB64: transaction.b64Encoded())
} catch ParaError.invalidTransaction {
print("Transaction format is invalid. Check parameters and encoding.")
}
```
**Error:** `ParaError.userRejected`
**Solution:** Inform the user clearly about what they're signing and provide a retry option if they accidentally rejected the prompt.
**Solution:**
* Ensure wallet balance covers gas fees
* Verify correct gas parameters
* Use reliable web3 libraries for estimates
* Consider implementing fallback gas values
### Network Issues
**Error:** `ParaError.networkError`
**Solution:** Check network connectivity, implement retry logic, and use network monitoring tools.
```swift
do {
try await paraManager.createWallet(type: .evm, skipDistributable: false)
} catch ParaError.networkError {
print("Network connection issue. Check connectivity and try again.")
}
```
**Solution:**
* Implement timeout handling using Swift concurrency features
* Provide retry options for users
* Display loading indicators during network operations
* Consider implementing exponential backoff for retries
### External Wallet Issues
**Error:** `ParaError.walletNotInstalled`
**Solution:** Prompt users to install MetaMask from the App Store.
```swift
do {
try await metaMaskConnector.connect()
} catch ParaError.walletNotInstalled {
print("MetaMask is not installed. Please install it from the App Store.")
}
```
**Solution:** Confirm URL scheme setup and correct handling of deep-link callbacks.
```swift
// In your AppDelegate or Scene implementation
func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
guard let url = URLContexts.first?.url else { return }
metaMaskConnector.handleURL(url)
}
```
**Solution:**
* Verify URL schemes in `Info.plist`
* Confirm deep-link handling in AppDelegate or SceneDelegate
* Test with simple deep links to isolate the issue
* Check if the external wallet app is properly installed
## Best Practices
## Development Tools
Enable debug mode in the Para SDK (if available) to get more detailed logging information.
Utilize network debugging tools like Charles Proxy or Xcode's network debugger to inspect API calls.
Leverage Xcode's built-in debugging features:
* Set breakpoints at critical points
* Inspect variables and state
* Use the console for logging
## Getting Help
If you're still experiencing issues after trying the solutions above, you can get additional help:
*
* Contact Para Support via email or Discord
* When reporting issues, include:
* Detailed error messages
* Steps to reproduce the issue
* Device and iOS version details
* Para SDK version