Skip to main content
The IXO @ixo/signx-sdk orchestrates mobile-to-web authentication and transaction signing through QR code scanning by the Impacts X mobile wallet. A web app generates a QR code, the user scans it with their Impacts X app, and the SDK long-polls a relayer server until the wallet returns either a login payload or a signed-and-broadcast transaction. Once a transaction session is open, additional transactions are added to the same session without re-scanning the QR — the wallet activates the next transaction in sequence on each broadcast. This is the publicly documented signing path. There is no in-browser private key — every signature comes from the user’s mobile wallet.

Before you start

You need:
npm install @ixo/signx-sdk @ixo/impactxclient-sdk

Initialize the SignX client

import { SignX } from "@ixo/signx-sdk";

const signXClient = new SignX({
  endpoint: "https://your-message-relayer.example.com",
  sitename: "Your dApp",
  network: "mainnet",
});
network is one of mainnet | testnet | devnet. The sitename is shown to the user inside the Impacts X app on every confirmation.

Log the user in

login() returns the QR payload to render and starts long-polling for the wallet’s response. Subscribe to SIGN_X_LOGIN_SUCCESS and SIGN_X_LOGIN_ERROR to receive the result.
const loginRequest = await signXClient.login({ matrix: true });

signXClient.on("SIGN_X_LOGIN_SUCCESS", (event) => {
  const { name, address, pubKey, did, algo, matrix } = event.data;
  // persist the user; pubKey is hex-encoded, decode if you need bytes.
});

signXClient.on("SIGN_X_LOGIN_ERROR", (error) => {
  // user cancelled, polling timed out, or the wallet returned an error.
});
Pass { useDeeplink: true } on mobile devices so the SDK opens the Impacts X app via deeplink before polling — this prevents some mobile browsers from cancelling the long-polling request when the user navigates away.

Sign and broadcast a transaction

Build the transaction body using the @ixo/impactxclient-sdk registry, then pass the hex-encoded txBody to signXClient.transact(). The wallet signs the transaction and broadcasts it on the user’s behalf.
import { createRegistry, ixo } from "@ixo/impactxclient-sdk";
import { toHex } from "@cosmjs/encoding";

const registry = createRegistry();

const messages = [
  {
    typeUrl: "/ixo.entity.v1beta1.MsgCreateEntity",
    value: ixo.entity.v1beta1.MsgCreateEntity.fromPartial({
      ownerAddress: user.address,
      entityType: "asset",
      context: [],
      verification: [],
      controller: [],
      service: [],
      linkedResource: [],
      accordedRight: [],
      linkedEntity: [],
      linkedClaim: [],
    }),
  },
];

const txBodyHex = toHex(registry.encodeTxBody({ messages, memo: "" }));

const transactRequest = await signXClient.transact({
  address: user.address,
  did: user.did,
  pubkey: user.pubKey,
  timestamp: new Date().toISOString(),
  transactions: [{ sequence: 1, txBodyHex }],
});

signXClient.on("SIGN_X_TRANSACT_SUCCESS", (event) => {
  // event.data.transactionHash, event.data.code (0 on success)
});

signXClient.on("SIGN_X_TRANSACT_ERROR", (error) => {
  // wallet rejected, broadcast failed, or session timed out.
});
If transactRequest.sessionHash is present a new session was opened — render a QR with transactRequest. If it is absent the transaction was added to the existing session — call signXClient.pollNextTransaction() and update the UI to indicate the next signature is pending in the wallet. The wallet returns the chain’s broadcast result (code, transactionHash, …) directly through SIGN_X_TRANSACT_SUCCESS.

Manage the session

Every transaction extends the session’s validUntil timestamp. Inspect signXClient.transactSessionHash to know whether a session is active and signXClient.transactSequence for the current sequence the wallet is being asked to sign.
window.addEventListener("beforeunload", () => {
  signXClient.stopPolling("Page closing", "SIGN_X_TRANSACT_ERROR");
});
Sessions are intentionally not persisted across browser refresh — transactSessionHash lives in memory only. After a refresh, the next transact() call starts a new session and renders a fresh QR.

Transaction-type restrictions

Restricting which message types a session can sign is enforced on the wallet — the Impacts X app inspects each transaction body against the user’s saved policy before signing. The web SDK does not impose its own filter. To restrict the messages a session can sign in your dApp, build the txBody only for the message types your flow needs and refuse to call transact() for anything else; pair this with smart-account authenticators (see Manage smart accounts) for an on-chain enforcement layer. When you build a MsgGrant (cosmos.authz.v1beta1.MsgGrant) with a GenericAuthorization, the msg field is the fully qualified message URL, e.g. /ixo.entity.v1beta1.MsgCreateEntity. The same convention applies everywhere transaction filtering is configured.

Verify the result

SIGN_X_TRANSACT_SUCCESS returns the transaction hash directly. To independently verify, query cosmos.tx.v1beta1.GetTx on the gRPC gateway API with that hash, or fetch the transaction from the Blocksync GraphQL API.

Troubleshooting

Confirm endpoint points to a reachable Message Relayer for the same network as the user’s wallet, and that network matches the chain the user logged in on. Check the relayer is healthy.
Mobile browsers may cancel ongoing requests when navigating to deeplinks. Pass useDeeplink: true to login, matrixLogin, dataPass, and as the third argument to transact so the SDK opens the deeplink first and waits 300 ms before polling.
The wallet may reject if the message type is outside the user’s signing policy, the chain ID does not match, or the user manually denies. SIGN_X_TRANSACT_ERROR carries the reason. Re-issue the transaction after the user adjusts their policy.
transactSessionHash is cleared when the relayer reports the session expired. The next transact() call starts a new session — surface this to the user (a fresh QR is needed). Call stopPolling(message, event, false) to stop polling without clearing the session if you want to retain it.

Next steps

SignX SDK reference

Methods, events, and types.

SignX SDK README

Source of truth for the SDK.

Smart accounts

Add on-chain enforcement of which message types may be signed.

Manage authorization

Combine SignX with Cosmos x/authz and x/feegrant.