Skip to content

Login with an external wallet

Privy allows you to authenticate a user via an Ethereum wallet using Sign in with Ethereum, or SIWE. This is a three step process:

  1. Generate a Sign-In With Ethereum (SIWE) message
  2. From a connected wallet, request an EIP-191 personal_sign signature for the SIWE message.
  3. Send the verified signature to Privy to authenticate the user's wallet

TIP

With Privy, users can either login with their wallet or link their wallet to an existing account.

1. Generate a SIWE Message

To login a user by their Ethereum wallet or to have them link their wallet to an existing account, first generate a SIWE message for the user by calling Privy's siwe.generateSiweMessage method.

This returns the SIWE message for the user to sign as a String or throw an error. An error could be thrown if the network call fails.

Method definition

swift
func generateSiweMessage(
    params: SiweMessageParams,
    metadata: WalletLoginMetadata?
) async throws -> String

SiweMessageParams - set of parameters required to generate the message.

FieldTypeDescription
appDomainStringYour app's domain. e.g. "my-domain.com"
appUriStringYour apps URI. e.g. "https://my-domain.com"
chainIdStringEVM Chain ID, e.g. "1" for Ethereum Mainnet
walletAddressStringThe user's ERC-55 compliant wallet address.

WalletLoginMetadata - Optionally, you can pass additional metadata that will be stored with the linked wallet. You can also override these meatdata params when calling loginWithSiwe in step 3.

FieldTypeDescription
walletClientTypeWalletClientTypeAn enum specifying the type of wallet used to login. e.g. WalletClientType.metamask
connectorTypeStringA string identifying how wallet was connected. e.g. "wallet_connect"

Example integration

swift
do {
    let params = SiweMessageParams(
        appDomain: "my-domain.com",
        appUri: "https://my-domain.com",
        chainId: "1",
        walletAddress: "0x12345..."
    )

    let metadata = WalletLoginMetadata(
        walletClientType: WalletClientType.metamask,
        connectorType: "wallet_connect"
    )

    let siweMessage = try await privy.siwe.generateSiweMessage(params: params, metadata: metadata)
} catch let error {
    // An error can be thrown if the network call to generate the message fails,
    // or if invalid metadata was passed in.
}

2. Request an EIP191 signature on the message

Using the message returned by generateSiweMessage, request an EIP-191 personal_sign signature from the user's connected wallet. You should do this using the library your app uses to connect to external wallets (e.g. the MetaMask iOS SDK or WalletConnect). Once the user successfully signs the message, store the signature in a variable.

3. Authenticate the wallet

Once the user has signed the SIWE message, login the user via their wallet by passing the signature to the loginWithSiwe method, or have them link their wallet to an existing account by passing the signature to the linkWithSiwe method.

Optionally, you can specify additional metadata about the user's wallet as a second optional WalletLoginMetadata parameter. If omitted, the metadata passed from generateSiweMessage will be used. If specified, metadata passed in generateSiweMessage will be overridden.

Method Definition

swift
func loginWithSiwe(
    _ signature: String,
    metadata: WalletLoginMetadata?
) async throws -> AuthState

Example integration

swift
do {
    // Pass signature to privy to authenticate user.
    // Omitting metadata because we specified it in step 1.
    let authState = try await privy.siwe.loginWithSiwe(signature)
} catch let error {
    // An error can be thrown if a successful call to `generateSiweMessage`
    // wasn't made prior to call `loginWithSiwe`
}

Tracking SIWE login flow state

You can add provide a listener to receive state updates during the login with SIWE process.

swift
privy.siwe.setSiweFlowStateChangeCallback{ siweFlowState in
    switch siweFlowState {
        case .initial:
            // Starting state
        case .generatingMessage:
            // SIWE message being created
        case .awaitingSignature:
            // Waiting for you to pass the signature generated from the personal_sign request
        case .submittingSignature:
            // Submitted signature to authenticate
        case .done:
            // Complete
        case .error:
            // An error has occurred
    }
}

That's it! Your user is now authenticated via external wallet.