Arkena Docs

Quickstart

From zero to a working connect button and a signed transaction in ten minutes.

This walks you through detecting the Arkena wallet from a dApp, requesting connection, signing in with Canton (SIWC), and signing a test transaction. By the end you'll have all the pieces you need to wire Arkena into a real integration.

  1. Detect the provider

    The Arkena wallet injects window.arkena (and window.canton) after page load. Injection is asynchronous, so detection needs to handle both already injected and not yet injected states.

    Code
    function getArkenaProvider(timeoutMs = 1000): Promise<typeof window.arkena | null> {
      if (typeof window === "undefined") return Promise.resolve(null);
      if (window.arkena) return Promise.resolve(window.arkena);
      return new Promise((resolve) => {
        const onReady = () => resolve(window.arkena);
        window.addEventListener("arkena#initialized", onReady, { once: true });
        setTimeout(() => resolve(window.arkena ?? null), timeoutMs);
      });
    }

    The arkena#initialized event is dispatched once on injection.

  2. Request connection

    Code
    const provider = await getArkenaProvider();
    if (!provider) throw new Error("Arkena wallet not detected");
    
    const { accounts, network } = await provider.request({
      method: "canton_connect",
    });
    
    console.log("Connected as", accounts[0], "on", network);

    The user sees an approval popup with your origin and the requested permissions. If they reject, the request rejects with an Error whose code is 4001.

  3. Sign in with Canton

    Most backend reads need a JWT scoped to the user's party. Get one with Sign-In with Canton:

    Code
    const challenge = await fetch("/api/auth/nonce", {
      method: "POST",
      body: JSON.stringify({ partyId: accounts[0] }),
      headers: { "Content-Type": "application/json" },
    }).then((r) => r.json());
    
    const { signature } = await provider.request({
      method: "canton_signMessage",
      params: { message: challenge.message },
    });
    
    const { token } = await fetch("/api/auth/verify", {
      method: "POST",
      body: JSON.stringify({ partyId: accounts[0], signature, nonce: challenge.nonce }),
      headers: { "Content-Type": "application/json" },
    }).then((r) => r.json());
    
    // Store `token` and attach as Authorization: Bearer <token> on subsequent calls.

    See Sign-In with Canton for the structured-message format and rotation rules.

  4. Sign a test transaction

    Most state-changing actions on Arkena follow a prepare → sign → execute loop. Here's the smallest example: transfer 0.1 CC to yourself (a no-op trade that exercises the full path).

    Code
    // 1. Prepare
    const prep = await fetch("/api/wallet/cc-transfer/prepare", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        from: accounts[0],
        to: accounts[0],
        amount: "0.1",
      }),
    }).then((r) => r.json());
    
    // 2. Sign
    const { signature: txSig } = await provider.request({
      method: "canton_signTransaction",
      params: {
        hash: prep.hash,
        commandId: prep.commandId,
        hashingSchemeVersion: prep.hashingSchemeVersion,
        summary: prep.summary,
      },
    });
    
    // 3. Execute
    const result = await fetch("/api/wallet/cc-transfer/execute", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        commandId: prep.commandId,
        transaction: prep.transaction,
        signature: txSig,
      }),
    }).then((r) => r.json());
    
    console.log("Settled:", result.commandId, result.status);

    Read The signing model for why this is three steps instead of one.

Where to go next