Skip to content

dApp integration

This document is the reference for engineers building dApps that talk to TaoApp Wallet.

The wallet injects a provider that follows the Polkadot.js extension injectedWeb3 convention, with a few TaoApp-specific additions.

Detection

The wallet registers window.injectedWeb3['wallet.tao.app'] and dispatches wallet#initialized on window when installation completes (src/inpage/index.ts).

javascript
window.addEventListener('wallet#initialized', () => {
    const wallet = window.injectedWeb3['wallet.tao.app'];
    console.log('TaoApp Wallet version:', wallet.version);
});

if (window.injectedWeb3?.['wallet.tao.app']) {
    // already initialized if your script loads late
}

version comes from __APP_VERSION__ at build time, with a '0.1.0' fallback in src/inpage/index.ts. name is always 'wallet.tao.app'.

Connecting

Call enable() or connect() (they share the same implementation) to request access. That opens the approval window so the user can choose accounts.

javascript
const wallet = window.injectedWeb3['wallet.tao.app'];
const extension = await wallet.enable('My dApp Name');

The parameter is an optional display string shown in the approval UI.

The background wallet_enable handler also understands forceReauth on the payload (src/background/controller/approval.ts). The public enable() / connect() wrappers currently forward only { appName } (src/inpage/index.ts). To force a fresh approval from JavaScript today, call disconnect() (or have the user revoke the site) and then enable() again.

The returned extension exposes accounts and signer.

Accounts

accounts.get()

Returns the accounts the user authorized for your origin.

javascript
const accounts = await extension.accounts.get();
// WalletAccountPublic[] - fields in `src/shared/types.ts`
  • address - SS58 with prefix BITTENSOR_SS58_PREFIX (42) from src/shared/constants.ts
  • name - user label (mapped from the vault label in toPublicAccounts, src/domain/accounts.ts)
  • type - sr25519 for software, ed25519 for Ledger, sr25519 or ed25519 for Vault depending on the key imported from the device (getCryptoType in src/shared/types.ts, called from src/domain/accounts.ts)
  • genesisHash - currently always null for dApp exports (toPublicAccounts)

accounts.subscribe(callback)

Re-fetches accounts on an interval. The first callback runs immediately; later updates use a 10 second timer (src/inpage/index.ts).

javascript
const unsubscribe = extension.accounts.subscribe((accounts) => {
    console.log('Updated accounts:', accounts);
});

unsubscribe();

On failure, subscribers receive an empty array. Implementation detail: each tick calls wallet_getAccounts, so the same origin authorization rules apply as for get().

Signing

signer.signRaw(request)

Signs arbitrary bytes. Opens the approval window.

javascript
const result = await extension.signer.signRaw({
    address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
    data: '0x68656c6c6f',
    type: 'bytes',
});
// { id?, signature }

Limits such as MAX_SIGN_RAW_BYTES are enforced in src/shared/constants.ts.

signer.signPayload(request)

Standard Polkadot extrinsic signing. Opens the approval window.

javascript
const result = await extension.signer.signPayload({
  address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
  blockHash: '0x...',
  blockNumber: '0x...',
  era: '0x...',
  genesisHash: '0x...',
  method: '0x...',
  nonce: '0x...',
  specVersion: '0x...',
  tip: '0x...',
  transactionVersion: '0x...',
  signedExtensions: [...],
  version: 4,
  withSignedTransaction: true,
});
// { id?, signature, signedTransaction? }

If the signing account has proxy or multisig configuration inside the wallet, the approval UI walks through the right wrapping path. The wallet may embed the call in proxy.proxy or multisig pallets before signing.

When withSignedTransaction is true and the adapter can produce it (Ledger always can; software and Vault use buildSignedExtrinsic in src/background/signing/build-extrinsic.ts), signedTransaction contains the hex-encoded extrinsic ready for submission.

signer.notifyTxSubmitted(params)

Tells the wallet about broadcast progress so the UI can track operations. Payload shape matches WalletNotifyTxSubmittedParams in src/shared/types.ts (also mirrored in src/inpage/index.ts).

javascript
await extension.signer.notifyTxSubmitted({
    txHash: '0x...',
    phase: 'submitted', // 'submitted' | 'in_block' | 'finalized'
    address: '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
    kind: 'transfer', // optional: 'transfer' | 'staking' | 'other'
});

Rate limiting is per dApp origin: consumeNotifyRateLimit in src/background/controller/tx-ops.ts enforces TX_NOTIFY_RATE_LIMIT_MAX_EVENTS events inside each rolling TX_NOTIFY_RATE_LIMIT_WINDOW_MS window (src/shared/constants.ts).

Disconnecting

disconnect()

Revokes the origin entirely and notifies subscribers with an empty list.

javascript
await extension.disconnect();

disconnectAccounts(addresses)

Drops specific addresses. If none remain, the origin is fully revoked.

javascript
await extension.disconnectAccounts(['5GrwvaEF...', '5HGjWAeF...']);

Approval lifecycle

  1. The wallet answers with { status: 'pending', requestId } for interactive calls.
  2. The inpage helper polls wallet_pollRequest every 900 ms (src/inpage/index.ts).
  3. Approval or rejection ends the loop and resolves the Promise.
  4. User-facing timeout: 10 minutes, matching PENDING_REQUEST_TTL_MS in src/shared/constants.ts (the inpage waiter uses the same duration literally).
  5. Transport timeout for a single postMessage hop: 30 seconds (src/inpage/index.ts).

Error handling

Typical failure modes:

  • Wallet locked - background returns errorCode: 'WALLET_LOCKED' when applicable; the Promise rejects with the message from the bridge.
  • User rejection - approval returns status: 'rejected'.
  • Approval timeout - "Approval request timeout." after the window above.
  • Missing authorization - calling accounts.get() or signing without a successful enable().

Exact codes surface through the { ok: false, error, errorCode } envelope.

Compatibility with @polkadot/extension-dapp

Standard methods (enable, accounts.get, signPayload, signRaw) behave like other injectedWeb3 providers. TaoApp-specific additions:

  • connect() alias for enable()
  • accounts.subscribe() polling helper
  • signer.notifyTxSubmitted() lifecycle hook
  • disconnect() / disconnectAccounts() permission helpers
  • Background-only forceReauth flag until the inpage wrapper exposes it

dApps that only rely on the common subset should work unchanged. Opt into the extras when you need them.

Address format

All addresses use SS58 prefix 42 (Bittensor). The wallet does not expose other prefixes for dApp accounts.