Embedded Wallet Integration

To get started with integrating embedded wallets into your dapp, follow these steps:

  1. Create an Apillon account: If you don't have an Apillon account or project yet, create one on the Apillon dashboardopen in new window.

  2. Open the Embedded Wallet page and create a new embedded wallet integration: Go to the Embedded Wallet pageopen in new window on the Apillon developer console and create a new embedded wallet integration. Integration UUID is needed for using the SDK.

  3. Setup whitelist: Input the domains on which you allow the integration to work on. Leave empty if you allow all website (this is not recommended).

TIP

If you want to learn more about Apillon's Embedded Wallet Service, visit the embedded wallet service wiki page.

Prerequisites

React, Vue

A Vite plugin is required for running and building Vite apps with Embedded Wallet. This plugin enables Node API in the browser (eg. buffer, crypto).

npm install -D vite-plugin-node-polyfills

HTTPS needs to be configured even for localhost, so that authentication can work accross multiple domains. This can be achieved with vite-plugin-mkcert Vite plugin.

npm i vite-plugin-mkcert -D
// vite.config.ts
import { defineConfig } from "vite";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import mkcert from "vite-plugin-mkcert";

export default defineConfig({
  plugins: [nodePolyfills(), mkcert() /* ... */],
});

Next.js

To use the Embedded wallet UI, your Next app has to be in app router mode. When in pages routing mode, global CSS file imports throw an error. Github Discussionopen in new window

Embedded wallet relies on browser APIs and it doesn't make sense to run it server-side. To avoid errors, the component including <WalletWidget /> should be marked with 'use client';

HTTPS needs to be configured even for localhost, so that authentication can work accross multiple domains. In Next.js you can do this by adding --experimental-https to dev command.

next dev --experimental-https

Nuxt

When using Vite as the build tool, a Vite plugin is required for running and building Nuxt apps with Embedded Wallet. This plugin enables Node API in the browser (eg. buffer, crypto).

npm i -D vite-plugin-node-polyfills

The Embedded wallet integration includes a style (CSS) file imported through JavaScript. Nuxt fails to resolve this import by default. To avoid errors, the Embedded wallet dependency needs to be added to the build.transpileopen in new window setting.

// nuxt.config.ts
import { nodePolyfills } from "vite-plugin-node-polyfills";

export default defineNuxtConfig({
  vite: {
    plugins: [nodePolyfills() /* ... */],
  },
  build: {
    transpile: [/@apillon[\\/]wallet-vue/],
  },
  /* ... */
});

Embedded wallet relies on browser APIs and it doesn't make sense to run it server-side. To avoid errors, wrap the wallet with <ClientOnly />.

<template>
  <ClientOnly>
    <WalletWidget clientId="..." />
  </ClientOnly>
</template>

HTTPS needs to be configured even for localhost, so that authentication can work accross multiple domains. In Nuxt.js you can do this by:

  • add --https to dev command

  • install mkcertopen in new window

  • make a localhost certificate in project folder: mkcert localhost

  • edit nuxt.config.ts to use the certificate for the dev server

// nuxt.config.ts
export default defineNuxtConfig({
  // ...
  devServer: {
    https: {
      key: "localhost-key.pem",
      cert: "localhost.pem",
    },
  },
});

Installation

npm install @apillon/wallet-react
npm install @apillon/wallet-vue
npm install @apillon/wallet-ui

Installing the wallet UI also installs the core wallet package @apillon/wallet-sdk as a dependency.

In the usage examples below you will see some imports from this package.

Add wallet widget

Replace the parameters with however you wish to setup your wallet.

import { WalletWidget } from "@apillon/wallet-react";

<WalletWidget
  clientId={"YOUR INTEGRATION UUID HERE"}
  defaultNetworkId={1287}
  networks={[
    {
      name: "Moonbase Testnet",
      id: 1287,
      rpcUrl: "https://rpc.testnet.moonbeam.network",
      explorerUrl: "https://moonbase.moonscan.io",
    },
    /* ... */
  ]}
/>;
import { WalletWidget } from "@apillon/wallet-vue";

<WalletWidget
  clientId="YOUR INTEGRATION UUID HERE"
  :defaultNetworkId="1287"
  :networks="[
    {
      name: 'Moonbase Testnet',
      id: 1287,
      rpcUrl: 'https://rpc.testnet.moonbeam.network',
      explorerUrl: 'https://moonbase.moonscan.io',
    },
    /* ... */
  ]"
/>
import { EmbeddedWalletUI } from "@apillon/wallet-ui";

EmbeddedWalletUI("#wallet", {
  clientId: "YOUR INTEGRATION UUID HERE",
  defaultNetworkId: 1287,
  networks: [
    {
      name: "Moonbase Testnet",
      id: 1287,
      rpcUrl: "https://rpc.testnet.moonbeam.network",
      explorerUrl: "https://moonbase.moonscan.io",
    },
    /* ... */
  ],
});

Parameters

FieldTypeRequiredDescription
clientIdstringYesUUID of the integration that you obtain when creating it on the Apillon embedded wallet dashboardopen in new window.
defaultNetworkIdnumberNoChain ID set as default when opening wallet.
networksNetwork[]NoArray of network specifications
broadcastAfterSignbooleanNoAutomatically broadcast with SDK after confirming a transaction.
disableDefaultActivatorStylebooleanNoRemove styles from "open wallet" button
authFormPlaceholderstringNoPlaceholder displayed in input for username/email

Network Object

The Network Object defines the properties required to connect to a blockchain network.

TIP

To find the information for your desired network, visit chainlist.orgopen in new window.

FieldTypeDescription
namestringThe name of the network
idnumberThe unique Chain ID of the network
rpcUrlstringThe URL to the network's RPC server
explorerUrlstringThe URL to the network's block explorer

Button style

You can style the activator button by targeting #oaw-wallet-widget-btn css ID. Use disableDefaultActivatorStyle option to make the button unstyled.

You can also hide the activator button (display: none;) and open the wallet programmatically, eg. from a menu item or your own button.

// const wallet = getEmbeddedWallet();
wallet.events.emit("open", true);

Use wallet

To access wallet signer and wallet information we provide core imports (hooks/composables):

import { useAccount, useContract, useWallet } from "@apillon/wallet-react";

export default function Component() {
  const { info, getBalance } = useAccount();
  const { wallet, signMessage, sendTransaction } = useWallet();

  const { read, write } = useContract({
    abi: [
      "function claim() public",
      "function balanceOf(address) view returns (uint256)",
      "function transfer(address to, uint256 amount) public returns (bool)",
    ],
    address: "0x67b9DA16d0Adf2dF05F0564c081379479d0448f8",
    chainId: 1287,
  });

  // using wallet core SDK
  const getWalletUserExists = (username: string) => {
    return wallet.userExists(username);
  };

  // get account info
  const getAccountInfo() = async () => {
    console.log(info.address);
    console.log(await getBalance());
  }

  // sign a message
  const onSignMessage = async (msg: string) => {
    await signMessage(msg);
  };

  // contract read
  const logContractBalance = async (address: string) => {
    console.log(await read("balanceOf", [address]));
  };

  // contract write
  const onContractTransfer = async (address: string, amount: string) => {
    await write("transfer", [address, amount], "Token transfer");
  };

  return <></>;
}
<script lang="ts" setup>
import { useAccount, useContract, useWallet } from "@apillon/wallet-vue";

const { info, getBalance } = useAccount();
const { wallet, signMessage, sendTransaction } = useWallet();

const { read, write } = useContract({
  abi: [
    "function claim() public",
    "function balanceOf(address) view returns (uint256)",
    "function transfer(address to, uint256 amount) public returns (bool)",
  ],
  address: "0x67b9DA16d0Adf2dF05F0564c081379479d0448f8",
  chainId: 1287,
});

// using wallet core SDK
function getWalletUserExists(username: string) {
  return wallet.value.userExists(username);
}

// get account info
async function getAccountInfo() {
  console.log(info.address);
  console.log(await getBalance());
}

// sign a message
async function onSignMessage(msg: string) {
  await signMessage(msg);
}

// contract read
async function logContractBalance(address: string) {
  console.log(await read("balanceOf", [address]));
}

// contract write
async function onContractTransfer(address: string, amount: string) {
  await write("transfer", [address, amount], "Token transfer");
}
</script>

<template>
  <div></div>
</template>

Ethers 5 and 6

To use with ethersopen in new window library we provide a specialized ethers signer.

import { EmbeddedEthersSigner } from "@apillon/wallet-sdk";

You can create a signer like with any other ethers signer as such:

const signer = new EmbeddedEthersSigner();

// eg. sign a message
await signer.signMessage("test message");

Viem

To use viemopen in new window we provide a specialized Viem adapter.

import { EmbeddedViemAdapter } from "@apillon/wallet-sdk";
import { moonbaseAlpha } from "viem/chains";

Use can use the adapter to get the user's account and use it with Viem:

const adapter = new EmbeddedViemAdapter();
const account = adapter.getAccount();

const walletClient = createWalletClient({
  chain: moonbaseAlpha,
  transport: http(),
  account,
});

// eg. sign a message
await account.signMessage({ message: "test message" });

// eg. send a plain transaction
await walletClient.sendRawTransaction({
  serializedTransaction: await walletClient.signTransaction(
    await walletClient.prepareTransactionRequest({
      to: "...",
      value: parseUnits("0.01", 18),
    })
  ),
});

Wagmi

We provide an EIP-1193 provider that can be used with Wagmiopen in new window or any other integration that supports it.

import { getProvider as getEmbeddedProvider } from "@apillon/wallet-sdk";

const wagmiConfig = {
  /* ... */
  connectors: [
    new InjectedConnector({
      chains,
      options: {
        getProvider() {
          return getEmbeddedProvider() as any;
        },
      },
    }),
  ],
};

TypeScript

Embedded wallet can be used with plain TypeScript by interacting with the wallet SDK directly.

TIP

Find out more about the SDK in the github repositoryopen in new window

<button id="sdk-sign">(SDK) Sign message</button>
<button id="sdk-native-transfer">(SDK) Transfer native balance</button>
<button id="sdk-contract-transfer">(SDK) Contract write (transfer)</button>
import { getEmbeddedWallet } from "@apillon/wallet-sdk";

document.getElementById("sdk-sign")?.addEventListener("click", async () => {
  const w = getEmbeddedWallet();

  if (w) {
    await w.signMessage({
      message: "test message",
      mustConfirm: true,
    });
  }
});
import { getEmbeddedWallet } from "@apillon/wallet-sdk";

document
  .getElementById("sdk-native-transfer")
  ?.addEventListener("click", async () => {
    const w = getEmbeddedWallet();

    if (w) {
      const result = await w.signPlainTransaction({
        tx: {
          to: "...",
          value: "10000000",
        },
        mustConfirm: true,
      });

      console.log(result);

      if (result) {
        console.log(
          await w.broadcastTransaction(result.signedTxData, result.chainId)
        );
      }
    }
  });
import { getEmbeddedWallet } from "@apillon/wallet-sdk";

document
  .getElementById("sdk-contract-transfer")
  ?.addEventListener("click", async () => {
    const w = getEmbeddedWallet();

    if (w) {
      const result = await w.signContractWrite({
        contractAbi: [
          "function claim() public",
          "function balanceOf(address) view returns (uint256)",
          "function transfer(address to, uint256 amount) public returns (bool)",
        ],
        contractAddress: "0x67b9DA16d0Adf2dF05F0564c081379479d0448f8",
        contractFunctionName: "transfer",
        contractFunctionValues: ["...", "10000000"],
        chainId: 1287,
        mustConfirm: true,
      });

      console.log(result);

      if (result) {
        console.log(
          await w.broadcastTransaction(
            result.signedTxData,
            result.chainId,
            "JS transfer"
          )
        );
      }
    }
  });

Create custom UI

All the functionalities of embedded wallets are contained in base package @apillon/wallet-sdk. The SDK exposes all the core methods of the wallet and you can create completely custom UI of the wallet on top of it.

TIP

For detailed technical documentation about the embedded wallet SDK, visit the github repositoryopen in new window

NPM Packages

Examples

React

Next.js

Vue.js

Nuxt

TypeScript