> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getpara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Wallet Pregeneration

> Learn how to create and manage pregenerated wallets for users with Para's SDK

export const MethodDocs = ({name, description, parameters = [], returns, deprecated = false, since = null, async = false, static: isStatic = false, tag = null, defaultExpanded = false, preventCollapse = false, id = 'method'}) => {
  const [isExpanded, setIsExpanded] = useState(defaultExpanded || preventCollapse);
  const [isHovered, setIsHovered] = useState(false);
  const [isCopied, setIsCopied] = useState(false);
  const [hoveredParam, setHoveredParam] = useState(null);
  const [hoveredReturn, setHoveredReturn] = useState(false);
  const parseMethodName = fullName => {
    const match = fullName.match(/^([^(]+)(\()([^)]*)(\))$/);
    if (match) {
      return {
        name: match[1],
        openParen: match[2],
        params: match[3],
        closeParen: match[4]
      };
    }
    return {
      name: fullName,
      openParen: '',
      params: '',
      closeParen: ''
    };
  };
  const methodParts = parseMethodName(name);
  const handleCopy = e => {
    e.stopPropagation();
    navigator.clipboard.writeText(name);
    setIsCopied(true);
    setTimeout(() => setIsCopied(false), 2000);
  };
  return <div className={`not-prose rounded-2xl border border-gray-200 overflow-hidden transition-colors duration-200 mb-6 ${isHovered && !preventCollapse ? 'bg-gray-50' : 'bg-white'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <button onClick={() => !preventCollapse && setIsExpanded(!isExpanded)} className={`w-full bg-transparent p-6 border-none text-left ${preventCollapse ? 'cursor-default' : 'cursor-pointer'}`}>
        <div className="flex items-start justify-between gap-4">
          <div className="flex-1 flex flex-col gap-2">
            <div className="flex items-center gap-3 flex-wrap">
              <div className="flex items-center gap-2">
                {async && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-purple-200 text-purple-800 rounded-lg">
                    async
                  </span>}
                {isStatic && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-violet-200 text-violet-900 rounded-lg">
                    static
                  </span>}
                {tag && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-teal-200 text-teal-800 rounded-lg">
                    {tag}
                  </span>}
              </div>
              
              <code className="text-lg font-mono font-semibold text-gray-900">
                <span>{methodParts.name}</span>
                <span className="text-gray-500 font-normal">{methodParts.openParen}</span>
                <span className="text-blue-600 font-normal">{methodParts.params}</span>
                <span className="text-gray-500 font-normal">{methodParts.closeParen}</span>
              </code>
              
              {deprecated && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-red-100 text-red-800 rounded-lg flex items-center gap-0.5">
                  ⚠ Deprecated
                </span>}
              {since && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-blue-100 text-blue-800 rounded-lg">
                  Since v{since}
                </span>}
            </div>
            
            <p className="text-sm text-gray-600 leading-6 m-0">
              {description}
            </p>
          </div>
          
          <div className="flex items-center gap-2 flex-shrink-0">
            <button onClick={handleCopy} className="p-2 bg-transparent border-none rounded-md cursor-pointer transition-colors duration-200 text-gray-500 hover:bg-gray-100" title="Copy method signature">
              {isCopied ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#16a34a" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <polyline points="20 6 9 17 4 12"></polyline>
                </svg> : <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                  <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
                  <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
                </svg>}
            </button>
            
            {!preventCollapse && <span className="text-gray-400">
                {isExpanded ? <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="18 15 12 9 6 15"></polyline>
                  </svg> : <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <polyline points="6 9 12 15 18 9"></polyline>
                  </svg>}
              </span>}
          </div>
        </div>
      </button>

      <div className={`overflow-hidden transition-all duration-300 ease-in-out px-6 border-t border-gray-200 ${isExpanded ? 'max-h-[2000px] opacity-100 pb-6' : 'max-h-0 opacity-0 pb-0'}`}>
        {parameters.length > 0 && <div className="pt-6">
            <div className="flex items-center gap-2 mb-3">
              <h3 className="text-sm font-semibold text-gray-700 uppercase tracking-wider m-0">
                Parameters
              </h3>
              <span className="text-xs text-gray-500">({parameters.length})</span>
            </div>
            <div>
              {parameters.map((param, index) => <div key={index} className={`pl-4 border-l-2 transition-colors duration-200 ${hoveredParam === index ? 'border-gray-300' : 'border-gray-200'} ${index < parameters.length - 1 ? 'mb-3' : ''}`} onMouseEnter={() => setHoveredParam(index)} onMouseLeave={() => setHoveredParam(null)}>
                  <div className="flex items-baseline gap-2 mb-1 flex-wrap">
                    <code className="font-mono text-sm font-medium text-gray-900">
                      {param.name}
                    </code>
                    <span className="text-sm text-gray-500">:</span>
                    {param.typeLink ? <a href={param.typeLink} className="no-underline">
                        <code className="font-mono text-sm text-blue-600 bg-transparent px-1 py-0.5 rounded-md cursor-pointer transition-all duration-200 hover:bg-gray-100 hover:text-blue-700">
                          {param.type}
                        </code>
                      </a> : <code className="font-mono text-sm text-blue-600 bg-transparent px-1 py-0.5 rounded-md">
                        {param.type}
                      </code>}
                    {param.required && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-yellow-100 text-yellow-800 rounded-lg">
                        Required
                      </span>}
                    {param.optional && <span className="px-1.5 py-0.5 text-[0.625rem] font-medium bg-gray-100 text-gray-600 rounded-lg">
                        Optional
                      </span>}
                  </div>
                  {param.description && <p className="text-sm text-gray-600 mt-1 mb-0">
                      {param.description}
                    </p>}
                  {param.defaultValue !== undefined && <p className="text-sm text-gray-500 mt-1">
                      Default: <code className="font-mono text-[0.625rem] bg-gray-100 px-1.5 py-0.5 rounded-lg">{param.defaultValue}</code>
                    </p>}
                </div>)}
            </div>
          </div>}

        {returns && <div className={`${parameters.length > 0 ? 'mt-6' : 'pt-6'}`}>
            <h3 className="text-sm font-semibold text-gray-700 uppercase tracking-wider m-0 mb-3">
              Returns
            </h3>
            <div className={`pl-4 border-l-2 transition-colors duration-200 ${hoveredReturn ? 'border-gray-300' : 'border-gray-200'}`} onMouseEnter={() => setHoveredReturn(true)} onMouseLeave={() => setHoveredReturn(false)}>
              <div className="flex items-baseline gap-2 mb-1">
                {returns.typeLink ? <a href={returns.typeLink} className="no-underline">
                    <code className="font-mono text-sm text-blue-600 bg-transparent px-1 py-0.5 rounded cursor-pointer transition-all duration-200 hover:bg-gray-100 hover:text-blue-700">
                      {returns.type}
                    </code>
                  </a> : <code className="font-mono text-sm text-blue-600 bg-transparent px-1 py-0.5 rounded">
                    {returns.type}
                  </code>}
              </div>
              {returns.description && <p className="text-sm text-gray-600 mt-1 mb-0">
                  {returns.description}
                </p>}
            </div>
          </div>}
      </div>
    </div>;
};

export const Card = ({imgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={handleClick} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full cursor-pointer rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

export const Link = ({href, label, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  return <a href={href} target={newTab ? '_blank' : '_self'} rel={newTab ? 'noopener noreferrer' : undefined} className="not-prose inline-block relative text-black font-semibold cursor-pointer border-b-0 no-underline" onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      {label}
      <span className={`absolute left-0 bottom-0 w-full rounded-sm bg-gradient-to-r from-orange-600 to-purple-600 transition-all duration-300 ${isHovered ? 'h-0.5' : 'h-px'}`} />
    </a>;
};

## Overview

Wallet Pregeneration allows you to create wallets before a user authenticates with Para. Your application controls the
wallet's user share until the wallet is claimed. After a successful claim, Para rotates the key material and protects
the user share with the user's authentication method.

Pregenerated wallets can be associated with an email address, a phone number, a third-party account identifier, or a
custom ID of your choosing. If you create a wallet with a custom ID and later want a user to claim it with email or
phone auth, update the pregenerated wallet identifier before returning the user share to the client.

* 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, your application is responsible for mapping that ID to the user who should be allowed to claim it.

## Creating a Pregenerated Wallet

Before creating a wallet for a user, it's a good practice to check if one already exists.

### Check if a pregenerated wallet exists

```typescript theme={null}
const hasWallet = await para.hasPregenWallet({
  pregenId: { email: "user@example.com" },
});
```

### Create a pregenerated wallet if needed

```typescript theme={null}
if (!hasWallet) {
  await para.createPregenWallet({
    type: "EVM",
    pregenId: { email: "user@example.com" },
  });
}
```

### Method Parameters

<MethodDocs
  defaultExpanded={true}
  preventCollapse={true}
  name="createPregenWallet()"
  description="Creates a pregenerated wallet of a specific type."
  async={true}
  parameters={[
{
  name: "type",
  type: "TWalletType",
  typeLink: "/v2/references/types/twallettype",
  required: true,
  description: "The type of wallet to create, 'EVM' | 'SOLANA' | 'COSMOS' | 'STELLAR'."
},
{
  name: "pregenId",
  type: "PregenAuth",
  typeLink: "/v2/references/types/pregenauth",
  required: true,
  description: "The pregen identifier for the wallet, in the form: `{ email: string } | { phone: string } | { telegramUserId: string } | { farcasterUsername: string } | { xUsername: string } | { discordUsername: string } | { customId: string }`."
}
]}
  returns={{
type: "Wallet",
typeLink: "/v2/references/types/wallet",
description: "The created pregen wallet."
}}
/>

### PregenAuth Type Definition

<MethodDocs
  defaultExpanded={true}
  preventCollapse={true}
  name="PregenAuth"
  tag="Type"
  description="Authentication type for pregen identifiers, in the form of various auth objects."
  returns={{
type: "Auth<PregenAuthType>",
typeLink: "/v2/references/types/auth"
}}
/>

<Note>
  The identifier can be an email or phone number, a third-party user ID (for Farcaster, Telegram, Discord, or X), or a custom ID relevant to your application. Choose an identifier that works best for your application architecture.
</Note>

## Storing and Managing User Share

After creating a pregenerated wallet, it's crucial to securely store the user share. This share is part of Para's 2/2
MPC protocol and remains the application's responsibility until the wallet is claimed.

To retrieve the user share for a pregenerated wallet, use the `getUserShare` method:

```typescript theme={null}
const userShare: string = await para.getUserShare();
```

<Warning>
  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.
</Warning>

### 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

<Check>
  Para is happy to offer pre-launch security reviews for teams in the Growth tier or above. Let us know if you need
  help!
</Check>

This share management is temporary - once the user claims their wallet, Para will handle the share security through the
user's authentication methods.

## Using a Pregenerated Wallet

Before using a pregenerated wallet for signing operations, you must first load the user share into your Para client
instance. Retrieve the `UserShare` from your secure storage and load it into Para using the `setUserShare` method:

```typescript theme={null}
await para.setUserShare(userShare);
```

Once the share is loaded, the wallet becomes available for signing operations, just like any other Para wallet:

```typescript theme={null}
const messageBase64 = btoa("Hello, World!");
const signature = await para.signMessage({
  walletId,
  messageBase64,
});
```

<Note>
  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.
</Note>

### 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:

* <Link label="EVM Ecosystem Guide" href="/v2/react/guides/web3-operations/evm/setup-libraries" />
* <Link label="Solana Ecosystem Guide" href="/v2/react/guides/web3-operations/solana/setup-libraries" />
* <Link label="Cosmos Ecosystem Guide" href="/v2/react/guides/web3-operations/cosmos/setup-libraries" />

## Claiming a Pregenerated Wallet

<Warning>
  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.
</Warning>

Claiming transfers ownership of a pregenerated wallet to a user's Para account. This process requires:

1. The wallet's user share is loaded into the Para client before the claim runs
2. The wallet's identifier matches the authenticating user's identifier
3. The claim runs from a client-side Para SDK instance

### Claiming with the modal

Configure `fetchPregenWalletsOverride` before opening the Para modal. During authentication, Para calls this function
with the identifier the user is authenticating with. Your backend uses that identifier to find the stored user share and
returns it to the SDK.

<Info>
  If you created the wallet with a custom ID, update the pregenerated wallet identifier to the authenticating identifier
  before returning the user share. The returned share must describe the wallet with the same identifier Para is claiming.
</Info>

```typescript theme={null}
async function fetchPregenWalletsOverride(opts: { pregenId: PregenAuth }) => Promise<{ userShare?: string }> {
  const email = "email" in opts.pregenId ? opts.pregenId.email : undefined;

  if (!email) {
    return { userShare: undefined };
  }

  const response = await fetch("/api/pregen/share", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email }),
  });
  const data = await response.json();

  return { userShare: data.userShare };
}
```

Pass the callback when you create the Para client used by your provider.

```typescript theme={null}
import ParaWeb, { Environment, ParaProvider } from "@getpara/react-sdk";

const para = new ParaWeb(Environment.BETA, process.env.NEXT_PUBLIC_PARA_API_KEY!, {
  fetchPregenWalletsOverride,
});

<ParaProvider paraClientConfig={para}>
  {children}
</ParaProvider>
```

Your backend should return the stored share only after it has prepared the wallet for the authenticating identifier.

```typescript theme={null}
import { Para as ParaServer } from "@getpara/server-sdk";

export async function POST(request: Request) {
  const { email } = await request.json();
  const wallet = await getPregenWalletByEmail(email);

  if (!wallet) {
    return Response.json({ userShare: undefined });
  }

  const para = new ParaServer(process.env.PARA_API_KEY!);
  const userShare = await decryptUserShare(wallet.encryptedUserShare);

  await para.setUserShare(userShare);
  await para.updatePregenWalletIdentifier({
    walletId: wallet.walletId,
    newPregenId: { email },
  });

  return Response.json({ userShare: await para.getUserShare() });
}
```

<Warning>
  Use a fresh server-side Para client for request handlers that call `setUserShare`. Reusing one client across requests
  can mix wallet shares from different users.
</Warning>

### Claiming manually

If you are not using the modal flow, authenticate the user first. Then retrieve the prepared user share from your backend,
load it into the client, and claim the wallet.

```typescript theme={null}
const response = await fetch("/api/pregen/share", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email: user.email }),
});
const { userShare } = await response.json();

await para.setUserShare(userShare);

const recoverySecret = await para.claimPregenWallets({
  pregenId: { email: user.email },
});
```

You can also claim every pregenerated wallet whose loaded share and identifier match the authenticated user.

```typescript theme={null}
const recoverySecret = await para.claimPregenWallets();
```

<Note>
  In the modal flow, you usually do not call `claimPregenWallets` directly. When the callback returns a matching
  user share during authentication, Para loads the share and claims the wallet as part of the auth flow.
</Note>

### 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

<Expandable title="Core API Methods">
  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="getPregenWallets()"
    description="Retrieves pregenerated wallets."
    async={true}
    parameters={[
{
  name: "pregenId",
  type: "PregenAuth",
  typeLink: "/v2/references/types/pregenauth",
  optional: true,
  description: "The pregen ID for the wallets to fetch. If not provided, all available wallets will be retrieved."
}
]}
    returns={{
type: "WalletEntity[]",
typeLink: "/v2/references/types/walletentity",
description: "Array of pregenerated wallet entities."
}}
  />

  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="hasPregenWallet()"
    description="Checks if a pregenerated wallet exists for the given ID."
    async={true}
    parameters={[
{
  name: "pregenId",
  type: "PregenAuth",
  typeLink: "/v2/references/types/pregenauth",
  required: true,
  description: "The pregen ID for the wallet to check."
}
]}
    returns={{
type: "boolean",
description: "True if a pregen wallet exists."
}}
  />

  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="claimPregenWallets()"
    description="Claims pregenerated wallets for the user."
    async={true}
    parameters={[
{
  name: "pregenId",
  type: "PregenAuth",
  typeLink: "/v2/references/types/pregenauth",
  optional: true,
  description: "The pregen identifier for the wallet to claim. If not provided, will attempt to claim all wallets in storage."
}
]}
    returns={{
type: "string | undefined",
description: "Optional recovery secret."
}}
  />

  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="updatePregenWalletIdentifier()"
    description="Updates the identifier for a pregenerated wallet."
    async={true}
    parameters={[
{
  name: "walletId",
  type: "string",
  required: true,
  description: "The ID of the pregen wallet to update."
},
{
  name: "newPregenId",
  type: "PregenAuth",
  typeLink: "/v2/references/types/pregenauth",
  required: true,
  description: "The new identifier for the wallet."
}
]}
  />

  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="createPregenWalletPerType()"
    description="Creates pregenerated wallets for specified types."
    async={true}
    parameters={[
{
  name: "types",
  type: "TWalletType[]",
  typeLink: "/v2/references/types/twallettype",
  optional: true,
  description: "The wallet types to create. If not provided, defaults to your application's `supportedWalletTypes` setting."
},
{
  name: "pregenId",
  type: "PregenAuth",
  typeLink: "/v2/references/types/pregenauth",
  required: true,
  description: "The pregen identifier for the wallets, in the form: `{ email: string } | { phone: string } | { telegramUserId: string } | { farcasterUsername: string } | { xUsername: string } | { discordUsername: string } | { customId: string }`."
}
]}
    returns={{
type: "Wallet[]",
typeLink: "/v2/references/types/wallet",
description: "Array of created pregen wallets."
}}
  />

  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="getUserShare()"
    description="Retrieves the user's share."
    async={false}
    returns={{
type: "string | null",
description: "The user share or null."
}}
  />

  <MethodDocs
    defaultExpanded={true}
    preventCollapse={true}
    name="setUserShare()"
    description="Sets the user's share."
    async={true}
    parameters={[
{
  name: "share",
  type: "string | null",
  required: true
}
]}
  />
</Expandable>

## Best Practices and Considerations

<AccordionGroup>
  <Accordion title="App-specific vs. Cross-app Usage">
    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.
  </Accordion>

  <Accordion title="Identifier Management">
    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.
  </Accordion>

  <Accordion title="Secure Storage Practices">
    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.
  </Accordion>

  <Accordion title="User Experience Design">
    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.
  </Accordion>

  <Accordion title="Wallet Type Considerations">
    Be deliberate about the wallet types you create:

    * **Match Blockchain Needs**: Select wallet types (EVM, Solana, Cosmos, Stellar) 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
  </Accordion>

  <Accordion title="Server vs. Client Operations">
    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.
  </Accordion>
</AccordionGroup>

## Reference Example

For complete examples demonstrating the usage of pregeneration methods, refer to our examples repository:

<CardGroup cols={2}>
  <Card title="Server-Side Pregeneration" imgUrl="/images/v2/general-server.png" href="https://github.com/getpara/examples-hub/blob/2.0.0/server/with-node/src/routes/createWallet.ts" description="Creating pregenerated wallets from your backend service." />

  <Card title="Client-Side Claiming" imgUrl="/images/v2/feature-pregeneration.png" href="https://github.com/getpara/examples-hub/tree/2.0.0/web/with-react-nextjs/para-pregen-claim" description="Example flow for claiming pregenerated wallets after user authentication in NextJS." />
</CardGroup>
