Getting Started With the Halliday Android Kotlin SDK

🚧

Contact Halliday for more details!

Before getting started below, please contact a Halliday representative ([email protected]) to activate this functionality.

Prerequisites

To use our Kotlin Passkeys API with your Android app, be sure that your project supports at least Android API 34+. This is due to a requirement from Google's Credential Manager API, which acts as the interface for Android passkeys.

Installation

Our latest library is published to mavenCentral! The latest version is 1.0.1.

To include our package, include the following line to your build.gradle or build.gradle.kts file

implementation "xyz.halliday.sdk:hallidayClient-kotlin-android:1.0.1"
implementation("xyz.halliday.sdk:hallidayClient-kotlin-android:1.0.1")

Setting Up Passkeys Infrastructure

In order for Google services to create and authenticate passkeys for your users, you will need to host an /.well-known/assetlinks.json file on your server.

  • If you have published your application, take note of the domain which you registered your app, and host the file under that domain (i.e. www.your-domain.com/.well-known/assetlinks.json). In this example, the domain specified in the file would be your-domain.com
  • If you are testing, you can temporarily host this file on AWS or some other server. The domain will be that server's domain

The assetlinks.json file has the following format:

[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls",
      "delegate_permission/common.get_login_creds"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "your-android-package-name",
      "sha256_cert_fingerprints": [
        "aa:bb:cc:dd:ee:ff:gg:...:zz"
      ]
    }
  }
]
  • The package_name should be the same name as defined in your application-layer build.gradle file:
android { 
  namespace: 'android_app'
  compileSdk: 34    // NOTE: Passkeys only work on API 34+

  defaultConfig {
    applicationId: "your-android-package-name" 
  } 
}
  • To get the SHA256_cert_fingrprint:
    • If you have published your app, then you should be able to find this through the same process as when you registered your app with Google. The SHA256_cert_fingerprint should be from whichever key that signed the application before publishing to Google.
    • Otherwise, you can run the command keytool -list -v -keystore ~/.android/debug.keystore -storepass android -keypass android(for AndroidStudio) to list the SHA256 fingerprint.

You can also use this helpful tool to generate your assetlinks.json file here

Note: When creating and testing passkeys, you must log in to your Google Account on your device or emulator, and have set up biometrics and/or screen lock on the device.

To learn more about setting up the assetlinks.json file, please refer to The Android Developer Reference on Passkeys.

Initialize

You can access the HallidayClient in your classes after initializing the HallidayClient with the following arguments:

  • apiKey -- Your Halliday sandbox or production API Key
  • blockchainType -- The blockchain that your app runs on. By default, this is set to BlockchainType.MUMBAI, but you can set it to other chains enumerated in the BlockchainType enum class
  • sandbox -- By default, this value is true so all requests are sent to our sandbox testing environment. This means all blockchain transactions will also be executed on testnets. When releasing your product, be sure to switch BlockchainType to be a mainnet type, and switch sandbox = false to hit our production servers.
  • domain -- This should be set to the domain in which your app registers with Google, as it is crucial for setting up passkeys properly. See the Setting Up Passkeys Infrastructure section for more details.
  • name -- Your application's name
  • context -- The Android application context in which this client is integrated in.
import org.halliday.client.HallidayClientInterface
import org.halliday.client.initializeHallidayClient

val hallidayClient:  HallidayClientInterface = initializeHallidayClient(apiKey, BlockchainType.MUMBAI, sandbox = true, "yourDomain", "yourAppName", context)
hallidayClient.isInitialized() 		// should return true if the client has been set up and initialized

Usage

The following snippet shows an integration of the HallidayClient for passkeys creation and login:

import org.halliday.client.HallidayClientInterface
import org.halliday.client.initializeHallidayClient

class SignUpFragment: Fragment () {
  
  // Initialize the HallidayClient
  val hallidayClient: HallidayClientInterface = initializeHallidayClient(apiKey, BlockchainType.MUMBAI, true, "halliday.xyz", "testApp", requireActivity())
  
  val inGamePlayerId = "test-player-id"								// The user's UNIQUE id in your application
  val userName: String = "test_userName"							// The user's in app name
  val appName: String = "testApp"											// The application's name
  
  // Register a new client to use passkeys with your app
  try {
    hallidayClient.register(inGamePlayerId, userName, appName, requireActivity())
  } catch (e: Exception) {
    Log.e("signUp", "Error creating passkey: ${e.message}")
  }
  // If no exceptions, the user should have successfully registered a passkey.
  // The user can now log in via passkeys, and approve transactions with their Halliday Smart Account with that passkey!
  
  // Get the user details, i.e. userId and signer keyAddress
  val userDetails: UserDetails = hallidayClient.getLoggedInUserDetails()
  val userId: String = userDetails.userId
  val keyAddress: String = userDetails.keyAddress
}


class SignInFragment: Fragment () {

  // Initialize the HallidayClient
  val hallidayClient: HallidayClientInterface = initializeHallidayClient(apiKey, BlockchainType.MUMBAI, true, "halliday.xyz", "testApp", requireActivity())
    
  val inGamePlayerid = "test-player-id"								// The user's UNIQUE in-game player id
  
  // Log in the user with an existing passkey
  // The function will prompt the user to sign in with an existing passkey.
  try {
    hallidayClient.login(inGamePlayerId)
  } catch (e: Exception) {
    if (e is NoPasskeyException) {
     Log.e("login", "No passkeys found for user. Please create one before logging in.")
     // Redirect users back to createPasskey page
    }
    Log.e("login", "Error logging in user: ${e.message}")
    // Redirect users back to pre-login page
  }
  
  // Get the user details, i.e. userId, userName, and non-custodial keyAddress
  val userDetails: UserDetails = hallidayClient.getLoggedInUserDetails()
  Log.d("login", "Got user details: $userDetails")		// We have overridden the toString() method so it prints nicely!
}

Once the user is logged in, they can leverage their Halliday Smart Account to build and send gas-sponsored transactions, which are approved with their passkey. For the user, this experience will be just like Apple Pay, where they will authenticate with their existing passkey once again to sign and approve the transaction.

// Set up client and log in the user

suspend fun sendTransaction() {
  // Set up the transaction details
  val targetAddress = "0x..."					// The target address for the transaction
  val value = "0"											// The amount of native tokens to send with the transaction
  val calldata = "0x..."							// The calldata for the contract function.
  val sponsor_gas = true							// Please make sure you have contacted us to enable gas sponsorship!

  val transaction: GetTransactionResponse = hallidayClient.callContract(targetAddress, value, calldata, sponsor_gas, requireActivity())
  Log.d("Transaction", "Transaction result: $transaction")		// toString() has been overridden to print nicely!
}

Beta: Session Keys

Using biometrics to approve transactions every time can still be tedious for users. With the Halliday SDK, however, you can enable session keys for your players, so that for a certain period of time, all transactions will be auto-signed and auto-approved with the session key. This way, players no longer need to input biometric data for every transaction for as long as the session key is active. See our Session Keys page for more information.


Common Issues and Errors

  • To test passkeys, you must run your application on an emulator or actual device. This is because Android unit tests do not run the entire android environment, but the entire context is needed for creating and accessing passkeys
  • You must also log into your Google account on your emulator or device, and set up biometrics / screen lock on the device to create and authenticate with passkeys.
    • If you get the error Cannot get create options, be sure to check if you are logged in!
  • If you get the error Cannot get sync account, try powering off then on the emulator or device. This may occur if you immediately test after logging in to your Google account on an emulator.
  • If you get the error The incoming request cannot be validated, this means that your assetlinks.json file has not been properly set up. Check the following:
    • The domain you initialized the HallidayClient with in initializeHallidayClient matches the domain under which you host the assetlinks.json file
    • The package_name and namespace fields in the assetlinks.json file matches the applicationId and namespace fields in your build.gradle file, respectively
    • The SHA256_Cert_fingerprint is from the same certificate that signs your application