# Create and recover wallets in Unity

## Understanding embedded wallets

To create wallets for your users during the login flow, you need to configure the private key generation. The configuration depends on your chosen recovery method, so it's important to decide on your recovery strategy first.

**Make sure to wait for the embedded state ready before using the embedded wallet. Learn more about [how to check the embedded state](/docs/products/embedded-wallet/unity/signer/state).**

## Decide a recovery method

Recovering the embedded wallet is needed when a user logs into a new device.

Openfort embedded wallets have two core recovery modes: automatic recovery and password recovery. At a high-level, this setting modulates how the embedded wallet's recovery share is encrypted and stored.

* **Automatic recovery**: The recovery share is encrypted with a combination of project entropy and Openfort's entropy. When logging into a new device, users can immediately access their embedded wallet.

:::tip
**Shield configuration**: Before configuring the automatic recovery,
generate your project's **publishable and secret shield keys** and store the
**encryption share**. Learn about the different API keys
[here](/docs/configuration/api-keys#shield-secret-and-publishable-keys).
:::

* **Password recovery:** The recovery share is encrypted by *user-provided password*. When logging into a new device, users must enter their password to recover the embedded wallet on the new device. Once the private key has been recovered on a device, users don't need to enter their password on that device again.

## Methods

### ConfigureEmbeddedWallet

Configure the embedded wallet with either automatic or password recovery.

**Method Signature:**

```csharp
public async UniTask ConfigureEmbeddedWallet(ConfigureEmbeddedWalletRequest request)
```

**Parameters:**

* `ConfigureEmbeddedWalletRequest request` - Wallet configuration request

**Returns:**

* `UniTask` - Completes when wallet is configured

**ConfigureEmbeddedWalletRequest Structure:**

```csharp
public class ConfigureEmbeddedWalletRequest
{
    public RecoveryParams recoveryParams;
    public int? chainId;
    public ChainType? chainType;
    public AccountType? accountType;

    public ConfigureEmbeddedWalletRequest(
        RecoveryParams recoveryParams = null,
        int? chainId = null,
        ChainType? chainType = null,
        AccountType? accountType = null)
    {
        this.recoveryParams = recoveryParams;
        this.chainId = chainId;
        this.chainType = chainType;
        this.accountType = accountType;
    }
}

public abstract class RecoveryParams
{
    public RecoveryMethod recoveryMethod;
}

public class AutomaticRecoveryParams : RecoveryParams
{
    public string encryptionSession;

    public AutomaticRecoveryParams(string encryptionSession = null)
    {
        this.encryptionSession = encryptionSession;
        this.recoveryMethod = RecoveryMethod.AUTOMATIC;
    }
}

public class PasswordRecoveryParams : RecoveryParams
{
    public string password;

    public PasswordRecoveryParams(string password)
    {
        this.password = password;
        this.recoveryMethod = RecoveryMethod.PASSWORD;
    }
}
```

## Recovery methods

### Automatic recovery

**From your backend**, you should have an endpoint that generates an encryption session for the user. This endpoint should be protected and only accessible by the user who is requesting the encryption session (that is, the user who is logging in).
Learn how to set up this endpoint and request an encryption session in our [automatic recovery session guide](/docs/products/embedded-wallet/server/automatic-recovery-session).

**Example:**

:::code-group

```csharp [openfortManager.cs]
using System;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using Cysharp.Threading.Tasks;
using Openfort.OpenfortSDK;
using Openfort.OpenfortSDK.Model;
using Newtonsoft.Json;

public class EmbeddedSignerManager : MonoBehaviour
{
    private OpenfortSDK openfort;
    private const string BACKEND_URL = "https://your-api-endpoint.com";
    private string authToken; // Your auth token from login

    private async void Start()
    {
        try
        {
            // Initialize Openfort SDK
            openfort = await OpenfortSDK.Init(
                "YOUR_OPENFORT_PUBLISHABLE_KEY",
                "YOUR_SHIELD_PUBLISHABLE_KEY"
            );
        }
        catch (Exception e)
        {
            Debug.LogError($"Failed to initialize Openfort: {e.Message}");
        }
    }

    // Setup automatic recovery with Openfort authentication
    public async UniTask SetupAutomaticRecoveryWithOpenfort(string email, string password)
    {
        try
        {
            // Step 1: Sign up the user
            var authResponse = await openfort.SignUpWithEmailPassword(email, password);
            authToken = authResponse.Token; // Store the auth token
            Debug.Log($"User signed up: {authResponse.User.Id}");

            // Step 2: Get encryption session from your backend
            string encryptionSession = await GetEncryptionSession();

            // Step 3: Configure the embedded wallet with automatic recovery
            int chainId = 80002; // Polygon Amoy testnet
            var recoveryParams = new AutomaticRecoveryParams(encryptionSession);

            var request = new ConfigureEmbeddedWalletRequest(
                recoveryParams: recoveryParams,
                chainId: chainId
            );
            
            await openfort.ConfigureEmbeddedWallet(request);
            Debug.Log("Automatic recovery wallet configured successfully");
        }
        catch (OpenfortException e)
        {
            Debug.LogError($"Openfort error: {e.Message}, Type: {e.Type}");
        }
        catch (Exception e)
        {
            Debug.LogError($"Error setting up automatic recovery: {e.Message}");
        }
    }

    private async UniTask<string> GetEncryptionSession()
    {
        string url = $"{BACKEND_URL}/api/protected-create-encryption-session";
        
        using (UnityWebRequest webRequest = UnityWebRequest.Post(url, "{}"))
        {
            webRequest.SetRequestHeader("Content-Type", "application/json");
            webRequest.SetRequestHeader("Authorization", $"Bearer {authToken}");

            await webRequest.SendWebRequest();

            if (webRequest.result != UnityWebRequest.Result.Success)
            {
                throw new Exception($"Failed to create encryption session: {webRequest.error}");
            }

            string jsonResponse = webRequest.downloadHandler.text;
            var response = JsonConvert.DeserializeObject<EncryptionSessionResponse>(jsonResponse);
            return response.session;
        }
    }
}

// Response model for the encryption session
[Serializable]
public class EncryptionSessionResponse
{
    public string session { get; set; }
}
```

```ts [Express.js]
import express, { Request, Response, NextFunction } from 'express';

// Initialize the Openfort client
import Openfort from "@openfort/openfort-node";
const openfort = new Openfort(process.env.OPENFORT_SECRET_KEY);

// Use your own middleware (JWT, sessions, API key, etc.) to authenticate the user
export const authenticateUser = (req: Request, res: Response, next: NextFunction) => {
  // Example: req.user = { id: 'user_123', email: 'user@example.com' };
  return next();
};

const router = express.Router();

router.post('/api/protected-create-encryption-session', authenticateUser, async (req: Request, res: Response) => {
  try {
    const session = await openfort.createEncryptionSession(
      process.env.OPENFORT_SHIELD_PUBLISHABLE_KEY as string,
      process.env.OPENFORT_SHIELD_SECRET_KEY as string,
      process.env.OPENFORT_SHIELD_ENCRYPTION_SHARE as string
    );
    res.status(200).json({ session });
  } catch (e) {
    console.error(e);
    res.status(500).json({ error: 'Internal server error' });
  }
});

export default router;
```

:::

### Password recovery

Require that users set a password when the wallet is created, enforcing password-based recovery from the start.

```csharp
using System;
using UnityEngine;
using Cysharp.Threading.Tasks;
using Openfort.OpenfortSDK;
using Openfort.OpenfortSDK.Model;

public class PasswordRecoveryManager : MonoBehaviour
{
    private OpenfortSDK openfort;
    
    private async void Start()
    {
        try
        {
            // Initialize SDK (Shield key optional for password recovery)
            openfort = await OpenfortSDK.Init(
                "YOUR_OPENFORT_PUBLISHABLE_KEY"
            );
        }
        catch (Exception e)
        {
            Debug.LogError($"Failed to initialize Openfort: {e.Message}");
        }
    }
    
    public async UniTask SetupPasswordRecovery(string email, string password, string recoveryPassword)
    {
        try
        {
            // Step 1: Sign up or log in the user
            var authResponse = await openfort.SignUpWithEmailPassword(email, password);
            Debug.Log($"User authenticated: {authResponse.User.Id}");
            
            // Step 2: Configure embedded wallet with password recovery
            int chainId = 80002; // Polygon Amoy testnet
            var recoveryParams = new PasswordRecoveryParams(recoveryPassword);

            var request = new ConfigureEmbeddedWalletRequest(
                recoveryParams: recoveryParams,
                chainId: chainId
            );
            
            await openfort.ConfigureEmbeddedWallet(request);
            Debug.Log("Password recovery wallet configured successfully");
            
            // The user will need to enter this recovery password when
            // accessing their wallet from a new device
        }
        catch (OpenfortException e)
        {
            Debug.LogError($"Openfort error: {e.Message}, Type: {e.Type}");
        }
        catch (Exception e)
        {
            Debug.LogError($"Error setting up password recovery: {e.Message}");
        }
    }
    
    // Method to recover wallet on a new device
    public async UniTask RecoverWalletWithPassword(string email, string loginPassword, string recoveryPassword)
    {
        try
        {
            // Step 1: Log in the user
            var authResponse = await openfort.LogInWithEmailPassword(email, loginPassword);
            Debug.Log($"User logged in: {authResponse.User.Id}");
            
            // Step 2: Recover the wallet with the recovery password
            var recoveryParams = new PasswordRecoveryParams(recoveryPassword);
            
            var request = new ConfigureEmbeddedWalletRequest(
                recoveryParams: recoveryParams,
                chainId: 80002
            );
            
            await openfort.ConfigureEmbeddedWallet(request);
            Debug.Log("Wallet recovered successfully with password");
        }
        catch (Exception e)
        {
            Debug.LogError($"Error recovering wallet: {e.Message}");
        }
    }
}
```

## Additional wallet methods

### CreateEmbeddedWallet

Create a new embedded wallet for the authenticated user.

**Method Signature:**

```csharp
public async UniTask<EmbeddedAccount> CreateEmbeddedWallet(CreateEmbeddedWalletRequest request)
```

**Parameters:**

* `CreateEmbeddedWalletRequest request` - Wallet creation request

**Returns:**

* `UniTask<EmbeddedAccount>` - The created embedded wallet account

**CreateEmbeddedWalletRequest Structure:**

```csharp
public class CreateEmbeddedWalletRequest
{
    public AccountType accountType;       // EOA or SMART_ACCOUNT
    public ChainType chainType;           // EVM or SVM
    public RecoveryParams recoveryParams;
    public int? chainId;

    public CreateEmbeddedWalletRequest(
        AccountType accountType,
        ChainType chainType,
        RecoveryParams recoveryParams,
        int? chainId = null)
    {
        this.accountType = accountType;
        this.chainType = chainType;
        this.recoveryParams = recoveryParams;
        this.chainId = chainId;
    }
}
```

**EmbeddedAccount Structure:**

```csharp
public class EmbeddedAccount
{
    public string Id { get; set; }                         // Account identifier
    public ChainType ChainType { get; set; }               // EVM or SVM
    public string Address { get; set; }                    // Wallet address
    public long? CreatedAt { get; set; }                   // Creation timestamp
    public string ImplementationType { get; set; }         // Account implementation
    public string FactoryAddress { get; set; }             // Smart account factory
    public string ImplementationAddress { get; set; }      // Implementation address
    public string Salt { get; set; }                       // Account salt
    public AccountType AccountType { get; set; }           // EOA, SMART_ACCOUNT, or DELEGATED_ACCOUNT
    public RecoveryMethod? RecoveryMethod { get; set; }    // PASSWORD or AUTOMATIC
    public RecoveryMethodDetails RecoveryMethodDetails { get; set; } // Details about recovery
    public int? ChainId { get; set; }                      // Blockchain chain ID
}
```

**Example:**

```csharp
public async UniTask<EmbeddedAccount> CreateNewWallet(string encryptionSession)
{
    try
    {
        var recoveryParams = new AutomaticRecoveryParams(encryptionSession);
        var request = new CreateEmbeddedWalletRequest(
            AccountType.SMART_ACCOUNT,
            ChainType.EVM,
            recoveryParams,
            80002 // Polygon Amoy
        );

        var account = await openfort.CreateEmbeddedWallet(request);

        Debug.Log($"Wallet created: {account.Address}");
        Debug.Log($"Account Type: {account.AccountType}");
        Debug.Log($"Chain ID: {account.ChainId}");

        return account;
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to create wallet: {e.Message}");
        throw;
    }
}
```

### RecoverEmbeddedWallet

Recover an existing embedded wallet on a new device.

**Method Signature:**

```csharp
public async UniTask<EmbeddedAccount> RecoverEmbeddedWallet(RecoverEmbeddedWalletRequest request)
```

**Parameters:**

* `RecoverEmbeddedWalletRequest request` - Wallet recovery request

**Returns:**

* `UniTask<EmbeddedAccount>` - The recovered embedded wallet account

**RecoverEmbeddedWalletRequest Structure:**

```csharp
public class RecoverEmbeddedWalletRequest
{
    public string account;              // Account ID to recover
    public RecoveryParams recoveryParams;

    public RecoverEmbeddedWalletRequest(string account, RecoveryParams recoveryParams)
    {
        this.account = account;
        this.recoveryParams = recoveryParams;
    }
}
```

**Example:**

```csharp
public async UniTask<EmbeddedAccount> RecoverWallet(string accountId, string password)
{
    try
    {
        var recoveryParams = new PasswordRecoveryParams(password);
        var request = new RecoverEmbeddedWalletRequest(accountId, recoveryParams);

        var account = await openfort.RecoverEmbeddedWallet(request);

        Debug.Log($"Wallet recovered: {account.Address}");

        return account;
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to recover wallet: {e.Message}");
        throw;
    }
}
```

### GetEmbeddedWallet

Get the current user's embedded wallet.

**Method Signature:**

```csharp
public async UniTask<EmbeddedAccount> GetEmbeddedWallet()
```

**Parameters:**

* None

**Returns:**

* `UniTask<EmbeddedAccount>` - The user's embedded wallet account

**Example:**

```csharp
public async UniTask<EmbeddedAccount> GetCurrentWallet()
{
    try
    {
        var account = await openfort.GetEmbeddedWallet();

        Debug.Log($"Wallet Address: {account.Address}");
        Debug.Log($"Account Type: {account.AccountType}");
        Debug.Log($"Recovery Method: {account.RecoveryMethod}");
        Debug.Log($"Chain ID: {account.ChainId}");

        return account;
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to get wallet: {e.Message}");
        throw;
    }
}
```

### ListWallets

List all wallets associated with the current user.

**Method Signature:**

```csharp
public async UniTask<EmbeddedAccount[]> ListWallets(ListWalletsRequest request)
```

**Parameters:**

* `ListWalletsRequest request` - Request with filtering options

**Returns:**

* `UniTask<EmbeddedAccount[]>` - Array of embedded wallet accounts

**ListWalletsRequest Structure:**

```csharp
public class ListWalletsRequest
{
    public string Address { get; set; }           // Filter by address
    public AccountType? AccountType { get; set; } // Filter by account type
    public ChainType? ChainType { get; set; }     // Filter by chain type
    public int? ChainId { get; set; }             // Filter by chain ID
    public SortOrdering? SortOrder { get; set; }  // ASC or DESC
    public int? Limit { get; set; }               // Max results
    public int? Skip { get; set; }                // Pagination offset
}
```

**Example:**

```csharp
public async UniTask<EmbeddedAccount[]> GetAllWallets()
{
    try
    {
        var request = new ListWalletsRequest
        {
            ChainType = ChainType.EVM,
            SortOrder = SortOrdering.DESC,
            Limit = 10
        };

        var wallets = await openfort.ListWallets(request);

        foreach (var wallet in wallets)
        {
            Debug.Log($"Wallet: {wallet.Address} on chain {wallet.ChainId}");
        }

        return wallets;
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to list wallets: {e.Message}");
        throw;
    }
}
```

### GetEthereumProvider

Get an EIP-1193 compatible Ethereum provider for the embedded wallet.

**Method Signature:**

```csharp
public async UniTask<Provider> GetEthereumProvider(EthereumProviderRequest request)
```

**Parameters:**

* `EthereumProviderRequest request` - Provider configuration request

**Returns:**

* `UniTask<Provider>` - EIP-1193 compatible provider interface

**EthereumProviderRequest Structure:**

```csharp
public class EthereumProviderRequest
{
    public EthereumProviderOptions options;

    public EthereumProviderRequest(EthereumProviderOptions options = null)
    // Defaults to new EthereumProviderOptions { announceProvider = true }
}

public class EthereumProviderOptions
{
    public bool announceProvider;  // Whether to announce the provider (default: true)
    public string policy;          // Gas sponsorship policy ID (optional)
}
```

**Provider Interface:**

```csharp
public interface Provider
{
    Task<object> Request(IRequestArguments request);
    void SendAsync(JsonRpcRequestPayload request, JsonRpcRequestCallback callback);
    void SendAsync(JsonRpcRequestPayload[] requests, JsonRpcRequestCallback callback);
    void Send(string request, JsonRpcRequestCallback callbackOrParams, JsonRpcRequestCallback callback);
    void Send(JsonRpcRequestPayload request, JsonRpcRequestCallback callbackOrParams, JsonRpcRequestCallback callback);
    void Send(JsonRpcRequestPayload[] requests, JsonRpcRequestCallback callbackOrParams, JsonRpcRequestCallback callback);
    void On(string @event, Action<object[]> listener);
    void RemoveListener(string @event, Action<object[]> listener);
    bool IsOpenfort { get; }
}
```

**Example:**

```csharp
public async UniTask<Provider> GetProvider()
{
    try
    {
        var request = new EthereumProviderRequest();
        var provider = await openfort.GetEthereumProvider(request);

        Debug.Log($"Provider ready: {provider.IsOpenfort}");

        // Use the provider for JSON-RPC calls
        var balanceRequest = new JsonRpcRequestPayload
        {
            method = "eth_getBalance",
            @params = new List<object> { "0x...", "latest" }
        };

        var balance = await provider.Request(balanceRequest);
        Debug.Log($"Balance: {balance}");

        return provider;
    }
    catch (Exception e)
    {
        Debug.LogError($"Failed to get provider: {e.Message}");
        throw;
    }
}
```

## Enums reference

### AccountType

```csharp
public enum AccountType
{
    EOA,               // Externally Owned Account
    SMART_ACCOUNT,     // Smart Contract Account
    DELEGATED_ACCOUNT  // Delegated Account
}
```

### ChainType

```csharp
public enum ChainType
{
    EVM,  // Ethereum Virtual Machine
    SVM   // Solana Virtual Machine
}
```

### RecoveryMethod

```csharp
public enum RecoveryMethod
{
    PASSWORD,   // Password-based recovery
    AUTOMATIC   // Automatic recovery
}
```

### SortOrdering

```csharp
public enum SortOrdering
{
    ASC,   // Ascending order
    DESC   // Descending order
}
```

## Wallet pregeneration

Openfort also allows you to pregenerate embedded wallets for your users, even before they first login to your game. Please see our [pregeneration guide](/docs/products/embedded-wallet/server/pregenerate-wallets) for more.
