Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.unlink.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Read balances

const { balances } = await unlink.getBalances();
Filter by token:
const { balances } = await unlink.getBalances({ token: "0xTokenAddress" });

Read transactions

const { transactions } = await unlink.getTransactions({
  status: "processed", // optional
  type: "transfer", // optional: "deposit" | "transfer" | "withdraw"
  limit: 20, // optional
  cursor: "...", // optional, for pagination
});

Poll transaction status

Poll until a transaction reaches a terminal state (relayed, processed, or failed).
const result = await unlink.pollTransactionStatus(txId, {
  intervalMs: 2000, // optional, default 2s
  timeoutMs: 60000, // optional, default 60s
});
Throws if the timeout is reached before a terminal status.

Get address

Get the Unlink address (Bech32m) for this account.
const address = await unlink.getAddress();
// "unlink1..."

Get public key

Get the spending public key (EdDSA on BabyJubJub).
const [x, y] = await unlink.getPublicKey();

Register account

Register an Unlink account once before deposit, transfer, or withdraw. Browser-side non-custodial flows should call the top-level registerAccount() helper so the Tenant does not need the user’s viewing or nullifying keys.
await registerAccount({
  engineUrl: "https://staging-api.unlink.xyz",
  apiKey: process.env.UNLINK_API_KEY!,
  accountKeys,
});

Derive identity from a wallet signature

The SDK can derive an Unlink identity from an EOA personal_sign signature instead of a mnemonic. The quickstart shows the one-shot wrapper (account.fromMetaMask); this section is the lower-level reference.

Message format

buildDeriveSeedMessage returns the exact string a wallet must sign:
import { buildDeriveSeedMessage } from "@unlink-xyz/sdk";

const message = buildDeriveSeedMessage({
  tenantName: "your-tenant-slug",
  chainId: 84532,
});
// "Unlink: derive identity\nTenant: your-tenant-slug\nChain: 84532\nVersion: 1"
Rules:
  • The message is the single source of truth — bumping its format is a breaking change for every existing identity.
  • tenantName is embedded verbatim. The SDK rejects empty strings, LF/CR, or anything over 64 bytes UTF-8 but does not canonicalise casing or whitespace. Use a stable slug from your tenant config.
  • chainId must be a positive integer.

Sign and derive

If you control the signing path yourself (viem, ethers, a hardware wallet, etc.), build the message, sign it, and feed the signature into account.fromEthereumSignature with the same tenantName/chainId:
import { account, buildDeriveSeedMessage } from "@unlink-xyz/sdk";

const message = buildDeriveSeedMessage({
  tenantName: "your-tenant-slug",
  chainId: 84532,
});

const signature = await walletClient.signMessage({
  account: evmAddress,
  message,
});

const accountProvider = account.fromEthereumSignature({
  signature,
  tenantName: "your-tenant-slug",
  chainId: 84532,
});
The signature is canonicalised internally (drop the v byte, enforce low-s) and expanded with HKDF-SHA256 into the 64-byte seed consumed by account.fromSeed. tenantName and chainId are bound into the HKDF salt so that any mismatch between the values used to build the message and the values passed to the factory derives a different address — caller error becomes immediately visible as a fresh, empty account rather than a silent identity swap. Stability across wallets relies on RFC-6979 deterministic ECDSA, which every mainstream wallet uses today. For long-term recovery, prefer the keystore export (account.export(keys)) over re-deriving from a wallet signature each session.

Error handling

import { ApiError, CapabilityError } from "@unlink-xyz/sdk";

try {
  await unlink.deposit({ token, amount });
} catch (err) {
  if (err instanceof ApiError) {
    // Unlink returned an error
    console.error(err.code, err.message);
  }
  if (err instanceof CapabilityError) {
    // Missing EVM provider capability (e.g. no sendTransaction)
    console.error(err.message);
  }
}