Appchain Wallet Quickstart

Seemlessly onboard users via Email OTP Login

Creating an Appchain Wallet

For your Login experience, we'll be implementing an Email OTP, based on your existing user ID system. We offer many other options for login (including Google, Facebook, Twitter/X, Passkeys and external wallets/custom signers), which you can find in Account Creation Options.

Here, you will learn how to create accounts for users using an email OTP. We offer many other ways to onboard users, which you can find in the "Creating Accounts" section.

For more in-depth information & resources, please visit the Email OTP page.


Login with Email OTP

Logging in with Email OTP consists of two steps. The user calls sendOTP(email) to send the OTP to the user's email. Then, the user calls login(OTP) with the OTP to log the user in. Both of these functions also take an argument in the form of EmailOTPClientParams to specify the user that is logged in.

EmailOTPClientParams

To call functions that would initialize a Halliday EmailOTPClient, namely restoreSession(), sendOTP(), and login(), the user must pass a set of parameters EmailOTPClientParams.

The EmailOTPClientParams contains the fields hallidayPublicApiKey, blockchainType, sandbox, and inGamePlayerId that specifies the user to be logged in and the game wallet to log them in to.

interface EmailOTPClientParams {
    hallidayPublicApiKey: string;
    blockchainType: BlockchainType;
    sandbox: boolean;
    inGamePlayerId: string;
}

sendOTP

Call this function with EmailOTPClientParams and email to send an OTP to the user email. Only the OTP from the most recently sent email can be used to register.

/**
     *
     * Call this function to send an email OTP to the user. This function will take in
     * the user's email and id, which will be used to send an email OTP to the user.
     * Only the OTP from the most recently sent email can be used to register.
     *
     * @param {EmailOTPClientParams} params Parameters for the Halliday client
     * @param {string} email The user's email where the OTP would be sent to
     *
     * @returns {Promise<boolean>} Returns true if successful, throws error if not
     */
async sendOTP(params, email): Promise<boolean>

login

After receiving the OTP, the login function should be called with EmailOTPClientParams and OTP to return a EmailOTPClient, which is an instance of a Halliday Client logged in to the user.

/**
     *
     * Call this function to login a user. After receiving their email OTP, the user
     * should be able to enter their OTP into a form. This function should then be
     * called with the OTP.
     *
     * If the user is trying to register, the OTP must match the OTP sent from the most
     * recently sent email from the sendOTP function. If the OTP matches, the user is
     * registered and the signer is initialized.
     *
     * Returns true if successful, throws error if not
     *
     * @param {EmailOTPClientParams} params Parameters for the Halliday client
     * @param {string} OTP The OTP entered by the user
     *
     * @returns {Promise<EmailOTPClient>} The Halliday client if successful, throws error if not
     */
async login(params: EmailOTPClientParams, OTP: string): Promise<EmailOTPClient>

Using an Appchain Wallet

Viewing Assets

Once you have access, you can view assets and balances in the smart account as demonstrated below:

// Get NFTs in the Halliday Smart Account.
const assets = await hallidayClient.getAssets(userInGameId);

// Get ERC20 balances in the Halliday Smart Account.
const balances = await hallidayClient.getBalances(userInGameId);

Create a transferAsset Transaction

To transfer an ERC721 token from one account to another, you can construct and send the transaction as demonstrated below:

// Transfer an NFT to a different user, with gas sponsored by the paymaster.
const transferAssetTxInfo = await hallidayClient.transferAsset({
  from_in_game_player_id: userInGameId,                                 // Your user's id in your application
  to_in_game_player_id: "other_player_id",                              // Other user's id in your application
  collection_address: "0xeeaf9e39057002eae4bea8bb4e65b01a9cfd59be",     // ERC721 token contract address
  token_id: "3988",                                                     // Token id to transfer
  sponsor_gas: true,                                                    // Send with gas sponsorship
});
console.log(transferAssetTxInfo.status, transferAssetTxInfo.on_chain_id);

Create a transferBalance Transaction

To transfer an ERC20 token or native token from one account to another, you can construct and send the transaction as demonstrated below:

// Transfer an ERC20 to a different user, with gas sponsored by the paymaster.
const balanceTransferTxInfo = await hallidayClient.transferBalance({
  from_in_game_player_id: userInGameId,                         // Your user's id in your application
  to_in_game_player_id: "other_player_id",                      // Other user's id in your application
  token_address: "0x0799ea468f812e40dbabe77b381cac105da500cd",  // ERC20 token contract address
  value: "100000000000000000",                                  // Amount of token to transfer, in the lowest decimal demonition of the token
  sponsor_gas: true,                                            // Send with gas sponsorship
});
console.log(balanceTransferTxInfo.status, balanceTransferTxInfo.on_chain_id);

Create a Transaction to Call Any Smart Contract Function

To call any on-chain smart contract function, you can construct and send the transaction as demonstrated below:

// Call an arbitrary contract.
const contractAddress = '0x1f6557356bfb310a556300a36fb18f54fb4791b1';
// The contract's ABI
const contractAbi = [...]; // Replace with your contract's ABI
// Create an instance of the contract
const contract = new ethers.Contract(contractAddress, contractAbi, signer);
// Get the calldata for a contract call
const calldata = contract.interface.encodeFunctionData('someFunction', ['arg1', 'arg2']);

// Call the contract, with gas sponsored by the paymaster.
const contractCallTxInfo = await hallidayClient.callContract({
  from_in_game_player_id: userInGameId,                    // Your user's id in your application
  target_address: contractAddress,
  value: "0",
  calldata,
  sponsor_gas: true,
});
console.log(contractCallTxInfo.status, contractCallTxInfo.on_chain_id);