# Set up an embedded wallet

Once a user is authenticated, you provision their embedded wallet by calling `configure`. This single call does two things: it chooses the **account type** (which determines the wallet's capabilities) and the **recovery method** (how the user's signing key is protected and restored on a new device). When it succeeds the wallet transitions to [`.ready`](/docs/products/embedded-wallet/swift/wallet/state).

```swift
let account = try await OFSDK.shared.configure(
    params: OFEmbeddedAccountConfigureParams(
        chainId: 84532, // Base Sepolia
        recoveryParams: OFRecoveryParamsDTO(recoveryMethod: .password, password: userPassword),
        accountType: .smartAccount
    )
)
```

:::note
`configure` requires an authenticated user. Make sure the wallet state is `.embeddedSignerNotConfigured` (or `.unauthenticated` has already been resolved) before calling it — see [Embedded wallet state](/docs/products/embedded-wallet/swift/wallet/state).
:::

## Choose an account type

`accountType` selects what the wallet can do. For the full comparison, see [Account types](/docs/products/embedded-wallet/account-types).

| `accountType`        | Gas sponsorship | Batching | Notes                                                                                  |
| -------------------- | --------------- | -------- | -------------------------------------------------------------------------------------- |
| `.smartAccount`      | Yes             | Yes      | ERC-4337 smart account. Gasless and batching with no extra wiring — recommended.       |
| `.eoa`               | No              | No       | Plain externally-owned account. Chain-agnostic; the user pays their own gas.           |
| `.delegatedAccount`  | Yes             | Yes      | EIP-7702 — the user's EOA delegated to a smart-account implementation (Calibur), keeping one address. Gasless through the provider. |

:::tip
Most apps should use `.smartAccount`. It gives you [gasless transactions](/docs/products/embedded-wallet/swift/wallet/eip-7702) and transaction batching for free, and the account is counterfactual — it costs nothing until its first transaction deploys it on-chain.
:::

:::warning
**EOA wallets are chain-agnostic — omit `chainId`.** An `.eoa` is a single address that works on every EVM chain, so it must be configured without a `chainId`. Smart accounts and delegated accounts are per-chain and **require** `chainId`.

```swift
// EOA: no chainId
OFEmbeddedAccountConfigureParams(
    recoveryParams: OFRecoveryParamsDTO(recoveryMethod: .password, password: userPassword),
    accountType: .eoa
)
```

:::

## Choose a recovery method

The recovery method controls how the wallet's key is encrypted and restored when the user signs in on a new device.

:::tip
We recommend enabling user-owned recovery (password or passkey). This becomes more important as the value of assets in a user's wallet grows — with password recovery, **only the user can decrypt the recovery share**; Openfort never sees the password.
:::

### Password recovery

The user supplies a password when the wallet is created, enforcing password-based recovery from the start. If you never plan to use automatic recovery, this keeps the encryption share entirely client-side.

```swift
do {
    let account = try await OFSDK.shared.configure(
        params: OFEmbeddedAccountConfigureParams(
            chainId: 84532, // Base Sepolia — omit for an .eoa
            recoveryParams: OFRecoveryParamsDTO(recoveryMethod: .password, password: userPassword),
            accountType: .smartAccount
        )
    )
    print("Wallet ready at \(account?.address ?? "?")")
} catch {
    print("Error configuring embedded wallet: \(error.localizedDescription)")
}
```

### Automatic recovery

Automatic recovery is silent — the user never enters a password. It requires a short-lived **encryption session** minted by your backend, which you fetch and pass to `configure`.

:::warning
The encryption session must be created server-side with your secret key. Never ship that secret in the app. The endpoint below is Openfort's reference implementation for demos; in production, host your own.
:::

```swift
import Foundation

struct EncryptionSessionResponse: Decodable { let session: String }

func getEncryptionSession() async throws -> String {
    let url = URL(string: "https://your-backend.example.com/api/protected-create-encryption-session")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.httpBody = try JSONSerialization.data(withJSONObject: [:]) // empty body {}

    let (data, _) = try await URLSession.shared.data(for: request)
    return try JSONDecoder().decode(EncryptionSessionResponse.self, from: data).session
}
```

Then pass the session through `encryptionSession`:

```swift
do {
    let session = try await getEncryptionSession()
    let account = try await OFSDK.shared.configure(
        params: OFEmbeddedAccountConfigureParams(
            chainId: 84532,
            recoveryParams: OFRecoveryParamsDTO(recoveryMethod: .automatic, encryptionSession: session),
            accountType: .smartAccount
        )
    )
    print("Wallet ready at \(account?.address ?? "?")")
} catch {
    print("Error configuring embedded wallet: \(error.localizedDescription)")
}
```

### Passkey recovery

Pass `recoveryMethod: .passkey` with an `OFPasskeyInfoDTO`. The user authenticates with Face ID / Touch ID to recover the wallet. See [Recovery methods](/docs/configuration/recovery-methods) for the full passkey flow.

## Recovering on a new device

When a user signs in on a new device, the same key needs to be restored. Re-running `configure` with the recovery method the wallet was created with recovers it automatically (the underlying call may resolve to `recover(params:)` internally). The state moves `.embeddedSignerNotConfigured` → `.creatingAccount` → `.ready`.

For automatic recovery, fetch a fresh encryption session first — sessions are single-use and short-lived.

## Parameters

```swift
public struct OFEmbeddedAccountConfigureParams: OFCodableSendable {
    // Properties are internal — use the initializer.
    public init(
        chainId: Int? = nil,              // required for smart/delegated; OMIT for .eoa
        recoveryParams: OFRecoveryParamsDTO,
        chainType: OFChainType? = nil,    // defaults to .evm
        accountType: OFAccountType? = nil // defaults to .smartAccount
    )
}

public struct OFRecoveryParamsDTO: OFCodableSendable {
    public let recoveryMethod: OFRecoveryMethod
    public let encryptionSession: String?  // automatic
    public let password: String?           // password
    public let passkeyInfo: OFPasskeyInfoDTO? // passkey
}

public enum OFRecoveryMethod: String, OFCodableSendable {
    case automatic
    case password
    case passkey
}

public enum OFAccountType: String, OFCodableSendable {
    case eoa = "Externally Owned Account"
    case smartAccount = "Smart Account"
    case delegatedAccount = "Delegated Account"
}

public enum OFChainType: String, OFCodableSendable {
    case evm = "EVM"
    case svm = "SVM"
}
```

## Returns

`configure` returns an optional `OFEmbeddedAccount` describing the provisioned wallet:

```swift
public struct OFEmbeddedAccount: OFEmbeddedAccountProtocol, OFCodableSendable {
    public let id: String
    public let chainType: OFChainType
    public let address: String                 // the wallet address you fund/receive to
    public let createdAt: Int?
    public let implementationType: String?
    public let implementationAddress: String?
    public let factoryAddress: String?
    public let salt: String?
    public let accountType: OFAccountType
    public let recoveryMethod: OFRecoveryMethod?
    public let recoveryMethodDetails: OFRecoveryMethodDetails?
    public let chainId: Int?
    public let ownerAddress: String?           // @deprecated
    public let type: String?                   // @deprecated
}
```

You can list all of a user's accounts at any time with `OFSDK.shared.list()`, which returns `[OFEmbeddedAccount]`.

## Next steps

* [Embedded wallet state](/docs/products/embedded-wallet/swift/wallet/state) — wait for `.ready` before using the wallet.
* [Send a transaction](/docs/products/embedded-wallet/swift/wallet/send) — once the wallet is configured.
* [Sponsored (gasless) transactions](/docs/products/embedded-wallet/swift/wallet/eip-7702) — for `.smartAccount` and `.delegatedAccount`.
