Prerequisites

This guide assumes that you have completed the setup guide.

Authenticate your user

Privy offers a variety of authentication mechanisms. The example below showcases authenticating a user via SMS.

This is a two step process:

  1. Send an OTP to the user provided phone number.
  2. Verify the OTP sent to the user.

Please be sure to configure SMS as a login method on the Privy Developer Dashboard under User Management > Authentication.

1. Send an OTP to the user’s phone number via SMS

After collecting and validating your users phone number, send an OTP by calling the sendCode method. Note: you must provide the phone number in E.164 format.

val result: Result<Unit> = privy.sms.sendCode(phoneNumber = "+14155552671")

result.fold(
  onSuccess = {
    // OTP was successfully sent
  },
  onFailure = {
    println("Error sending OTP: ${it.message}")
  }
)

If the OTP is sent successfully, sendCode will return Result.success with no associated type. If the provided phone number is invalid, or sending the OTP fails, sendCode will return Result.failure.

2. Authenticate with OTP

The user will then receive an SMS with a 6-digit OTP. Prompt for this OTP within your application, then authenticate the user with the loginWithCode method. Pass the following parameters to this method:

val result: Result<PrivyUser> = privy.sms.loginWithCode(code = "123456", phoneNumber = "+14155552671")

result.fold(
  onSuccess = { user ->
    // User logged in
  },
  onFailure = {
    println("Error logging in user: ${it.message}")
  }
)

If the OTP/phone number combination is valid, Privy will successfully authenticate your user and loginWithCode will return Result.success with an encapsulated PrivyUser. If the provided OTP/phone number combination is invalid, loginWithCode will return Result.failure.

The Embedded wallet

Create an embedded wallet

To create an EVM embedded wallet for your user, call the createEthereumWallet method on your PrivyUser instance.

public interface PrivyUser {
  // Other privy user methods

  public suspend fun createEthereumWallet(allowAdditional: Boolean = false):  Result<EmbeddedEthereumWallet>

  // ...
}

If a wallet is successfully created for the user, an EmbeddedEthereumWallet object is returned as an encapsulated value of Result.success.

This method will Result.failure if:

  • The user is not authenticated
  • If a user already has 9 or more wallets
  • If the network call to create the wallet fails
  • If a user already has an embedded wallet and allowAdditional is not set to true.

Example

privy.user?.let { privyUser ->
  // non null user means user is authenticated
  val result = privyUser.createEthereumWallet(allowAdditional = false)

  result.fold(
    onSuccess = { ethereumWallet ->
      println("Created wallet with address: ${ethereumWallet.address}")
    },
    onFailure = {
      println("Error creating Ethereum wallet: ${it.message}")
    }
  )
}

Using the embedded wallet

To enable your app to request signatures and transactions from the embedded wallet, Privy Ethereum embedded wallets expose a provider inspired by the EIP-1193 provider standard. This allows you request signatures and transactions from the wallet via a familiar JSON-RPC API (e.g. personal_sign).

Once you have an instance of an EmbeddedEthereumWallet, you can make RPC requests by using the provider: EmbeddedEthereumWalletProvider hook and using its request method. For example, wallet.provider.request(request: rpcRequest). This request method will suspend and await if the embedded wallet needs to wait for any internal ready state.

public interface EmbeddedEthereumWalletProvider {
 /**
  * Sends a request to the Ethereum provider
  *
  * @param The RPC request
  * @return The response received
  */
 public suspend fun request(request: EthereumRpcRequest): Result<EthereumRpcResponse>
}

As a parameter to this method, to this method, pass an EthereumRpcRequest object that contains:

  • method: the name of the JSON-RPC method for the wallet to execute (e.g. 'personal_sign')
  • params: an array of parameters required by your specified method

By default, embedded wallets are connected to the Ethereum mainnet. To send a transaction on a different network, simply set the wallet’s chainId in the transaction request.

Example usage:

// Get Privy user
val user = privy.user

// check if user is authenticated
if (user != null) {
  // Retrieve list of user's embedded Ethereum wallets
  val ethereumWallets = user.embeddedEthereumWallets

  if (ethereumWallets.isNotEmpty()) {
    // Grab the desired wallet. Here, we retrieve the first wallet
    val ethereumWallet = ethereumWallets.first()

    // Make an rpc request
    ethereumWallet.provider.request(
      request = EthereumRpcRequest(
        method = "personal_sign",
        params = listOf("A message to sign", ethereumWallet.address),
      ),
    )
  }
}

Was this page helpful?