Status: draft, version 1. The interface is implemented by the KaleidoSwap
browser extension ≥ 0.3.0 and described by index.d.ts in this repository.
The burnAsset and getConsignment methods below are optional BFA extensions
provided by this fork; they are not part of that reference implementation.
signMessage is also an optional extension provided by this fork.
witnessReceive is a standard provider method added by this fork.
A wallet injects a provider into a web page. A dApp calls it to issue,
receive, send and track RGB assets, and to pay or receive
them over Lightning, without running an RGB backend of its own. WebRGB sits
next to WebLN (window.webln) and WebBTC (window.webbtc) and follows their
conventions.
"MUST", "SHOULD" and "MAY" are used as in RFC 2119.
For browser injection, a wallet MUST expose the provider object as
window.rgb and MUST dispatch a rgb:ready CustomEvent on window once
it is installed:
window.rgb = provider;
window.dispatchEvent(new CustomEvent("rgb:ready", { detail: { version: "1.0.0" } }));A page may run before or after the wallet, so a dApp MUST handle both orders —
requestProvider() in this package does.
window.rgb is a single slot. A wallet SHOULD NOT overwrite a provider another
wallet installed; it MUST still announce itself (§2) so the page can choose.
A remote wallet MAY expose the same interface through a transport adapter. The dApp then uses a local provider object; browser injection and discovery are not required. Connection and method consent still apply.
window.rgb cannot represent two installed wallets. Discovery works like
EIP-6963: pages ask, wallets answer.
- A page asks by dispatching an
Eventnamedrgb:requestProvideronwindow. - A wallet answers, and also announces once on load, by dispatching a
CustomEventnamedrgb:announceProviderwhosedetailis{ info, provider }.
interface RgbProviderInfo {
uuid: string; // UUIDv4, fresh per page load — identifies the announcement
name: string; // "KaleidoSwap"
rdns: string; // "com.kaleidoswap.extension" — stable, identifies the wallet
icon?: string; // data: URI
}A wallet MUST keep answering rgb:requestProvider for the lifetime of the
page, MUST use a reverse-DNS rdns it controls, and MUST announce the same
object it installed at window.rgb when it installed one. A page MUST
deduplicate by rdns.
Discovery is additive: a wallet that only sets window.rgb still works, and a
page that only reads window.rgb still works.
enable() asks the user to connect the origin. Every other method MUST reject
with NOT_ENABLED until it has resolved. A wallet MAY share one approval
across its RGB, WebLN and WebBTC providers. enable() MUST be idempotent and
MUST NOT prompt again for an origin already connected.
Beyond the connection, consent is per call:
| Method | Prompts | Notes |
|---|---|---|
getInfo, getAddress, listAssets, getAssetBalance, listTransfers, getTransferStatus, decodeRgbInvoice |
MUST NOT | Read-only; a page may poll them |
blindReceive |
MUST | Creates an invoice that binds a UTXO |
witnessReceive |
MUST | Creates an invoice for an output in the sender's transaction |
signMessage |
MUST | Signs the displayed message for the requesting origin |
issueAsset |
MUST | Mints; MAY also require a separate wallet capability |
sendAsset |
MUST | Moves assets |
burnAsset |
MUST | Burns assets |
getConsignment |
MUST unless already approved | Shares a consignment with a third party to verify a burn proof |
makeLnInvoice, payLnInvoice |
MUST | Moves, or commits to receiving, assets over Lightning |
A prompt the user dismisses MUST reject with USER_REJECTED.
Signatures are in index.d.ts; this section is the contract
around them.
getInfo()returns{ ready, network, protocol, methods }.readyisfalsewhen no RGB wallet is connected — every other call then rejects.protocolis"RGB_L1"(node-less, client-side RGB),"RGB_LN"(an RGB Lightning node), ornullwhen nothing is connected.methodsis the feature-detection contract. It MUST list exactly the methods the wallet will serve. A method absent from it MUST reject withMETHOD_NOT_SUPPORTED; a method present in it MUST NOT.makeLnInvoiceandpayLnInvoiceMUST appear only whenprotocolis"RGB_LN", andissueAssetonly when the runtime can mint.burnAsset,getConsignmentandsignMessageare optional and MAY be absent when unsupported; dApps MUST check both the method list and their presence.getAddress()returns a Bitcoin address of the wallet that anchors its RGB state. It is not an RGB invoice.signMessage(message)signs a string and returns{ signature }as an LND-compatible zbase32 signature. The wallet MUST display the message and obtain user approval before signing it unchanged.blindReceive({ assetId?, amount?, minConfirmations?, … })returns an RGB invoice against a blinded UTXO. Omittingamountmeans any amount. OmittingassetIdmeans any asset: the invoice names no contract, and it is the only way to receive an asset the wallet has never held, since a wallet can only name a contract it already knows. AnassetIdthe wallet does not know MUST reject withASSET_NOT_FOUND, notINTERNAL_ERROR. A wallet SHOULD NOT accept fewer than 3 confirmations forminConfirmations: RGB wallets do not handle reorgs today, so a transfer accepted as settled whose anchoring transaction is later reorged out loses the received assets. A wallet MAY enforce a higher floor, and SHOULD raise a lower request to its floor rather than reject. The confirmation MUST show the value actually used, and the result MUST carry it asminConfirmations.witnessReceive(args?)returns an RGB invoice for an output created by the sender's transaction, without requiring an existing receiver UTXO. Its parameters, result and confirmation rules are the same asblindReceive.issueAsset({ schema, ticker, name, amounts, precision? })mints.schemais"nia","uda"or"cfa"; a wallet that cannot serve a schema MUST reject withMETHOD_NOT_SUPPORTEDrather than substituting another.sendAsset(args)takes either{ invoice }(preferred) or the explicit{ assetId, amount, recipientId }. It returns at least the wallet's handle on the transfer —txidand/ortransferId— so the page can track it. A wallet MAY refuse{ invoice }for an any-amount invoice, since nothing in the request fixes what leaves the wallet; it MUST then reject withINVALID_PARAMS, and the explicit form is how a page pays one.burnAsset({ network, assetId, amount, burnRecipient, … })burns a BFA (bridged fungible asset) and returnstxidandtransferIdafter broadcast.amountis a positive u64 decimal string in base units;networkMUST match the connected RGB network.burnRecipientcontains a CAIP-2chainIdand native address, with validation and proof encoding defined inRgbBurnRecipient. dApps MUST checkgetInfo().burnRecipientNamespaces(defaults to["eip155"]). Wallets MUST reject unsupported chains or invalid addresses withINVALID_PARAMSbefore confirmation. The wallet MUST validate the bridge route and show the asset, amount, destination and Bitcoin fee in its confirmation. A dApp MUST NOT automatically repeat a burn after a timeout.getConsignment({ assetId, txid })returns the saved burn consignment as Base64, with its byte length and digest as defined inindex.d.ts. The consignment can be shared with a third party, such as a bridge, to verify the burn proof. The wallet MUST obtain consent to share this proof with the requesting dApp and explain that it may be forwarded to a third party; consent already given for that proof and dApp MAY be reused until revoked. The result MUST contain the proof bytes, not a local file path, and retrieval MUST NOT perform another burn.listAssets()andlistTransfers(assetId?)MUST return arrays. (Wallets that wrap them exist;toAssetArray/toTransferArrayin this package tolerate that, and the conformance suite reports it.)getTransferStatus(transferId, assetId?)MUST resolve{ found: false, status: null, transfer: null }for an unknown transfer rather than rejecting.transferIdMAY be matched against the wallet's own id, the recipient id, or the txid.decodeRgbInvoice(invoice)reads what an invoice asks for, so a page can show it before callingsendAsset. It MUST NOT prompt and MUST NOT move anything.amountMUST be the same number the wallet's own confirmation would show, andnullfor an any-amount invoice rather than0. An invoice whose fungible assignment is0is an any-amount invoice: rgb-lib writes an unconstrained invoice both asAssignment::Anyand asAssignment::Fungible(0), and a wallet MUST read the two the same way —amount: nullhere, and the amount the user or the page supplies on send — never as a request for zero. Issuers SHOULD preferAny, which says so.makeLnInvoice(args)returns a BOLT-11 invoice carrying the asset. The node enforces a minimum HTLC value, so the wallet MAY raiseamountSats; the confirmation MUST show the figure actually encoded.payLnInvoice(invoice)pays a BOLT-11 invoice that carries an asset. A plain Bitcoin invoice SHOULD be refused — that iswebln.sendPayment().
on(event, listener) / off(event, listener) deliver:
| Event | Fires when |
|---|---|
transferReceived |
The wallet sees an incoming transfer for this wallet |
transferSettled |
A known transfer reaches Settled |
The listener receives the transfer. A wallet SHOULD start whatever polling
backs this only while a page holds a listener, and SHOULD deliver events only
to origins that have called enable().
Every rejection MUST be an Error carrying a code:
| Code | Meaning |
|---|---|
USER_REJECTED |
The user declined the connection or a confirmation |
NOT_ENABLED |
Called before enable() resolved for this origin |
METHOD_NOT_SUPPORTED |
This wallet cannot serve this method |
INVALID_PARAMS |
An argument is malformed or out of range; message names it |
ASSET_NOT_FOUND |
The call names an asset the wallet does not know |
INTERNAL_ERROR |
Anything else; message carries the detail |
A wallet SHOULD reject bad arguments with INVALID_PARAMS before raising any
confirmation. A code a wallet's own backend produces MUST be mapped onto this
table, never forwarded as-is. A dApp MUST treat a code it does not recognise
as INTERNAL_ERROR: older wallets predate INVALID_PARAMS and
ASSET_NOT_FOUND, and later versions may add codes.
A dApp MUST NOT rely on instanceof: the error crosses a postMessage
boundary and arrives as a plain Error. Use isProviderError().
A wallet MUST NOT leak wallet state through error messages to an origin that has not been enabled.
@utexo/webrgb/conformance runs the read-only half of this document
against a live provider:
import { runConformance, formatReport } from "@utexo/webrgb/conformance";
console.log(formatReport(await runConformance(window.rgb)));It never issues, sends or creates an invoice, so it raises no confirmation and costs nothing to run against a funded wallet. It checks advertised optional methods without burning assets, sharing a consignment or signing a message.
This document and index.d.ts version together. A method added to the
interface is a minor version of the package; a changed signature is a major
one. Where a wallet and this document disagree, the wallet is what pages see —
open an issue.