KaspireBack home

Kaspire Mobile · WalletConnect v2 · Kaspa Mainnet

Connect to Kaspire Mobile.

Connect websites to a selected Kaspire account through an encrypted WalletConnect v2 session. Request accounts, KIP-5 signatures, KAS payments, token transfers, and reviewed PSKT marketplace or covenant flows without exposing private keys to the browser.

01 / Requirements

What your project needs

Create a Reown project ID, allowlist every production origin, and initialize a WalletConnect-compatible SignClient. Kaspire currently supports Android and Kaspa Mainnet only.

  • A Reown project ID with your website origins allowlisted
  • WalletConnect v2 using the irn relay
  • A dedicated Kaspire QR-code view for desktop visitors
  • The explicit Kaspire Android intent with the verified HTTPS fallback
npm install @walletconnect/sign-client

The project ID is public application configuration. Pairing URIs are secrets and must never be logged, persisted, placed in analytics, or sent to unrelated services.

02 / Quick start

Initialize and connect

import SignClient from "@walletconnect/sign-client";

const signClient = await SignClient.init({
  projectId: import.meta.env.VITE_REOWN_PROJECT_ID,
  metadata: {
    name: "Example Kaspa dApp",
    description: "Connect to Kaspire",
    url: window.location.origin,
    icons: [window.location.origin + "/icon.png"]
  }
});

Request only the methods your dApp really uses. A sign-in-only integration should request only kaspa_getAccounts and kaspa_signPersonal.

const { uri, approval } = await signClient.connect({
  requiredNamespaces: {
    kaspa: {
      chains: ["kaspa:mainnet"],
      methods: [
        "kaspa_getAccounts",
        "kaspa_signPersonal",
        "kaspa_sendTransaction",
        "kaspa_sendKrc20",
        "kaspa_sendKcc20",
        "kaspa_signPskt",
        "kaspa_signVaultTransaction"
      ],
      events: ["accountsChanged"]
    }
  }
});

if (!uri) throw new Error("WalletConnect did not return a pairing URI");

// Important: use this HTTPS App Link as the QR payload.
// Do not put the raw wc: URI into the Kaspire QR code.
const kaspireLink =
  "https://kaspire.kaslab.space/kaspire/wc?uri=" +
  encodeURIComponent(uri);
const kaspireIntent =
  "intent://wc?uri=" + encodeURIComponent(uri) +
  "#Intent;scheme=kaspire;package=space.kaspire.wallet;" +
  "S.browser_fallback_url=" + encodeURIComponent(kaspireLink) + ";end";

if (/Android/i.test(navigator.userAgent)) {
  window.location.assign(kaspireIntent);
} else {
  renderKaspireQrCode(kaspireLink);
}

const session = await approval();
03 / Desktop QR and Android

Do not use the generic wallet QR for Kaspire

Kaspire accepts WalletConnect v2 pairings. However, a generic WalletConnect modal commonly puts the raw wc: URI in its QR code or sends the user to Reown's wallet-selection screen after scanning.

Kaspire is not currently registered in the Reown WalletGuide. The generic picker can therefore offer MetaMask and other listed wallets without showing Kaspire, even when Kaspire is installed. This is expected and does not indicate an installation problem.

Required Kaspire QR payloadhttps://kaspire.kaslab.space/kaspire/wc?uri=<URL-ENCODED-WALLETCONNECT-URI>

Provide a dedicated Connect with Kaspire button. On desktop, encode this complete HTTPS link in your QR code. On Android, open the explicit intent://wc URL shown in the quick start so the installed app wins; keep the verified HTTPS link as its browser fallback. Never encode only the raw wc: URI in the Kaspire QR code.

const { uri, approval } = await signClient.connect({
  requiredNamespaces: {
    kaspa: {
      chains: ["kaspa:mainnet"],
      methods: [
        "kaspa_getAccounts",
        "kaspa_signPersonal",
        "kaspa_sendTransaction",
        "kaspa_sendKrc20",
        "kaspa_sendKcc20",
        "kaspa_signPskt",
        "kaspa_signVaultTransaction"
      ],
      events: ["accountsChanged"]
    }
  }
});

if (!uri) throw new Error("WalletConnect did not return a pairing URI");

// Important: use this HTTPS App Link as the QR payload.
// Do not put the raw wc: URI into the Kaspire QR code.
const kaspireLink =
  "https://kaspire.kaslab.space/kaspire/wc?uri=" +
  encodeURIComponent(uri);
const kaspireIntent =
  "intent://wc?uri=" + encodeURIComponent(uri) +
  "#Intent;scheme=kaspire;package=space.kaspire.wallet;" +
  "S.browser_fallback_url=" + encodeURIComponent(kaspireLink) + ";end";

if (/Android/i.test(navigator.userAgent)) {
  window.location.assign(kaspireIntent);
} else {
  renderKaspireQrCode(kaspireLink);
}

const session = await approval();
QR codeVerified Kaspire App LinkAndroid opens KaspireKaspire processes the wc: URI“Connect dApp?” approval

If Kaspire is not installed, Android opens the HTTPS fallback page instead. That page links to the current official APK. After installation, the user should return to the dApp and generate a new pairing QR code.

If your dApp also supports other WalletConnect wallets, expose a separate Other WalletConnect wallets action for the generic Reown modal.

Canonical launch URLhttps://kaspire.kaslab.space/kaspire/wc?uri=<encoded wc: URI>

The legacy kaslab.space/kaspire/wc route remains accepted for existing integrations. New integrations should always use the canonical Kaspire subdomain.

04 / Sessions

Restore the selected account

Kaspire publishes the account as CAIP-10: kaspa:mainnet:q.... RPC results return the normal full address kaspa:q....

Accounts versus subwallets

A selected address may be the first address of a BIP-44 account, such as m/44'/111111'/1'/0/0, or an address-index subwallet such as m/44'/111111'/0'/0/2. Treat the returned Kaspa address as the authoritative opaque account identifier. Never infer or request a derivation path, and update your UI when Kaspire emits accountsChanged.

const sessions = signClient.session.getAll();
const session = sessions.find(
  item => item.namespaces.kaspa?.accounts?.length
);

const caip10 = session?.namespaces.kaspa.accounts[0];
// kaspa:mainnet:q...
const address = caip10
  ? "kaspa:" + caip10.split(":").slice(2).join(":")
  : null;
const accounts = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_getAccounts",
    params: {}
  }
});
// ["kaspa:q..."]
signClient.on("session_event", ({ params }) => {
  if (params.event.name === "accountsChanged") {
    const accounts = params.event.data;
    // Update the selected account in your application.
  }
});

signClient.on("session_delete", ({ topic }) => {
  // Clear local UI state for this topic.
});
05 / Methods

Supported JSON-RPC requests

kaspa_getAccounts

Returns the approved full Kaspa address.

kaspa_signPersonal

Creates a user-approved KIP-5 personal-message signature.

kaspa_sendTransaction

Builds, reviews, signs, and broadcasts a native KAS payment.

kaspa_sendKrc20

Executes the complete KRC-20 commit/reveal transfer flow.

kaspa_sendKcc20

Validates and executes a typed KCC20 covenant transfer.

kaspa_signPskt

Signs selected inputs of a fully reviewed Kaspa SafeJSON transaction.

kaspa_signVaultTransaction

Signs only a native Rust policy-approved vault create or DMS heartbeat transaction.

KIP-5 sign-in

const message = [
  "Sign in to Example dApp",
  "Domain: example.com",
  "Address: " + address,
  "Nonce: " + serverNonce,
  "Issued At: " + new Date().toISOString(),
  "Expiration Time: " + expiresAt
].join("\n");

const signature = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_signPersonal",
    params: { address, message }
  }
});

Generate the nonce on your server, bind it to the intended domain and address, verify the KIP-5 signature server-side, and consume the nonce exactly once.

Native KAS

const transactionId = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_sendTransaction",
    params: {
      from: address,
      to: "kaspa:q...",
      amountSompi: "100000000"
    }
  }
});

KRC-20

const result = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_sendKrc20",
    params: {
      from: address,
      to: "kaspa:q...",
      ticker: "SOULS",
      amount: "100000000"
    }
  }
});

// result:
// {
//   ticker, amount,
//   commitTransactionId, revealTransactionId,
//   commitFeeSompi, revealFeeSompi
// }

Commit and reveal require two authorization steps. Keep the request pending while Kaspire waits for the commit output. A delayed reveal can be resumed safely inside Kaspire.

KCC20

const result = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_sendKcc20",
    params: {
      from: address,
      to: "kaspa:q...",
      covenantId: "64 lowercase hexadecimal characters",
      amount: "100000000"
    }
  }
});

// result:
// {
//   transactionId, covenantId, ticker, amount,
//   feeSompi, mass, validation: "toccata-node"
// }

Identify KCC20 assets by their complete covenant ID—not by ticker. Kaspire accepts only a verified balance with a complete live-cell mapping and reconstructs the covenant transition locally before signing.

Generic PSKT: marketplaces, KRC-721, KNS and covenants

const signedTxJson = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_signPskt",
    params: {
      txJsonString: draft.txJson,
      options: {
        signInputs: [
          { index: 0, sighashType: 1 } // SIGHASH_ALL
        ]
      }
    }
  }
});

// Kaspire returns the signed SafeJSON string.
// Your dApp finalizes or combines the PSKT and decides when to broadcast.

Use kaspa_signPskt when your dApp has already constructed a Kaspa transaction and needs Kaspire to sign only specified inputs. This method is dApp-independent: no KaspaCom-specific, marketplace-specific, or vault-specific cooperation is required. It supports transaction versions 0 and 1 and sighash values 1, 2,4, 129, 130, and132.

Not a blind signer

Kaspire's native Rust core parses the SafeJSON and every embedded UTXO, rejects duplicate outpoints and inconsistent fields, calculates the fee and wallet net effect, and binds all inputs, outputs, payload, covenant bindings and selected sighashes to the approval. The wallet displays every output and warns for partial signatures, non-standard scripts, and mutable sighashes. It signs only the requested inputs and never broadcasts automatically.

Kaspire guarantees that the displayed transaction is the one signed. It cannot certify your dApp's marketplace price, royalty policy, listing semantics, or covenant intent. Reown's verified domain is anti-phishing context, not a substitute for user review.

Vault policy transactions

const result = await signClient.request({
  topic: session.topic,
  chainId: "kaspa:mainnet",
  request: {
    method: "kaspa_signVaultTransaction",
    params: {
      txJsonString: draft.txJson,
      signInputIndexes: [0, 1],
      redeemScript: draft.redeemScript
    }
  }
});

// result: { signedTxJson, profile, reviewHash }
// Creation profiles use signInputIndexes: [0] and redeemScript: "".
// Heartbeats must use exactly [0, 1] and the covenant redeem script.

This is an optional stricter profile for the version-2 KasLab time-lock create, DMS create, and DMS heartbeat flows. Other dApps should use kaspa_signPskt; they do not need a dedicated Kaspire policy.

06 / Data rules

Amounts, addresses, and responses

  • Send integer strings, never floating-point values. One KAS is 100000000 sompi.
  • KRC-20 and KCC20 amounts are exact raw token units before applying token decimals.
  • Recipient and optional from values must be full kaspa:q... Mainnet addresses.
  • If supplied, from must exactly match the account approved for the session.
  • Do not treat human-readable ticker or metadata as asset identity. Use the covenant ID for KCC20.
07 / Errors

Handle rejection without retry loops

4001User rejected the request
-32600Duplicate or malformed request
-32601Unsupported method or chain
-32602Invalid session account
-32000Request failed safely in Kaspire
5000Session proposal rejected
6000Session disconnected

Never automatically resubmit a rejected payment or signature. Show a clear status, let the user correct the request, and require a new deliberate action.

08 / Security

Production checklist

  • Allowlist every legitimate production origin in Reown.
  • Request the minimum methods required by the current flow.
  • Never log or persist a wc: pairing URI.
  • Percent-encode the entire URI exactly once in the App Link.
  • Use a server nonce with expiry and one-time consumption for login.
  • Verify KIP-5 signatures on the server before creating a session.
  • Represent every amount as a base-10 integer string.
  • Bind UI state to the WalletConnect topic and approved account.
  • Clear local connection state on session deletion or expiry.
  • Never infer successful payment before receiving the RPC result.
09 / Testing

Test the unhappy paths

Begin with a low-value Mainnet wallet. Test approval, rejection, app switching, expired pairings, disconnected sessions, insufficient funds, malformed addresses, unsupported methods, duplicate requests, interrupted KRC-20 reveal, and background return to the browser.

Kaspa does not currently define an official WalletConnect namespace for these methods. The API on this page is Kaspire protocol v2 and should be version-pinned in your integration.