Developer Documentation

Build on Blip

Blip is a self-custody Quai wallet for iOS and Android. It ships an in-app dApp browser with an injected, EIP-1193-style provider, plus per-origin app wallets that sandbox each site and can auto-fund themselves from the user's main vault. If your dApp runs on Quai, it can run inside Blip with no SDK to install.

Overview

There are two ways to integrate with Blip, and most dApps use both:

  • The injected provider. When your site loads in the Blip in-app browser, Blip injects a Quai provider at window.quai (also aliased to window.pelagus and window.ethereum). You call request() exactly like any EIP-1193 wallet.
  • Deep links. From anywhere on the web you can hand a user off into Blip — to open your dApp in the in-app browser, to share a contact, or to attach a referral — using https://blippay.me/... universal links or the blip:// URL scheme.
Network. Blip is a Quai Network wallet. The default chain is Cyprus1, chain id 0x9. Supported assets include native QUAI, private Qi, wrapped Qi (WQI), and USDT.

App wallet spec

Blip is fully self-custody. Keys never leave the device, and there is no Blip account or server-side custody. Two wallet concepts matter for builders:

  • Main vault wallet. The user's primary, mnemonic-backed wallet. It owns the user's balance and is the only thing that authorizes funding transfers into app wallets.
  • App wallets. Deterministic per-origin wallets derived from the same mnemonic — one wallet per canonical dApp origin. They are standard EOAs (not smart-contract wallets), so everything works through ordinary RPC signing and sending. App wallets sandbox each site: a balance and history scoped to your origin, isolated from the user's main funds.

Derivation

App wallets are derived locally and deterministically. Index 0 is the main vault, index 1 is the referral wallet, and app-wallet allocation begins at index 2. A canonical origin keeps its assigned index unless the user explicitly rotates it, so your dApp sees a stable address across sessions and reinstalls.

Encrypted registry

Because the index-to-origin mapping is what makes the addresses stable, Blip publishes an encrypted snapshot of origin → derivationIndex on-chain, keyed by the user's main vault address. The plaintext never leaves the device — the contract stores opaque ciphertext only — so app-wallet structure survives a device loss or fresh install without any centralized database. You don't call this contract; it's listed here so the model is transparent.

Registry contract — BlipEncryptedLookupRegistry
NetworkAddress
Quai Mainnet0x0027fAbcE3b2399ce2Dab7E3944E0F6C334A33C6
Orchard (testnet)0x00288C5a0615C03faAA485F689B877a2bf49144D
What this means for you: the address Blip hands your dApp is the app wallet for your origin, not the user's main vault. Send funds to it, read its balance from it, and use the funding protocol below when it needs a top-up.

Referral design

Blip has a built-in referral graph. Every user can share a link that, when opened, adds them as the referrer for the new user and keeps the referral tree intact through install and setup.

01Share before setup

A referrer shares their link before the friend sets up Blip. The link carries the referrer's identity and referral code.

02Open & install

The friend opens the link. If Blip isn't installed, the share page offers App Store / Google Play, preserving the referral code through the store handoff (Android passes it via the Play referrer parameter; the page also exposes UTM tags utm_source=blip_referral, utm_medium=share_page).

03Earn & claim

Once the new user starts sending QUAI in Blip, the referrer earns rewards and claims them in the in-app Rewards screen. The referral wallet lives at derivation index 1.

Link format

Referral and contact links share one shape. Universal links live under blippay.me; the same payload maps to a blip:// deep link once Blip is installed.

Referral — universal link
https://blippay.me/referral/<code>?name=Ada&q=0x...&qi=<qiPaymentCode>&uqi=<upstreamQiCode>
Referral — deep link
blip://referral?code=<code>&name=Ada&q=0x...&qi=<qiPaymentCode>&uqi=<upstreamQiCode>
ParamAliasesMeaning
codereferralCodeReferral code (also accepted as the path segment /referral/<code>)
nameusername, displayName, labelDisplay name of the referrer / contact
qquai, quaiAddress, addressQuai address
qiqiPaymentCode, paymentCodeQi payment code
uqiupstreamQiPaymentCode, secondaryQiPaymentCodeUpstream Qi payment code (referral only)

The /contact and /profile paths take the same params (a referral code rides along as referralCode) but share a profile instead of opening a referral.

In-app browser

Blip ships a full dApp browser — WKWebView on iOS, WebView on Android — with a Safari-style chrome that hides on scroll. At document start, before your page scripts run, Blip injects a Quai provider and announces it via EIP-6963. From your side it behaves like any injected wallet:

  • The provider is reachable at window.quai, and aliased to window.pelagus and window.ethereum for compatibility with existing Quai/EVM dApp code.
  • It carries identity flags: isBlip: true, isPelagus: true, isQuai: true, and a marker _isSwiftBlip: true. isMetaMask is false.
  • It implements request({ method, params }), the on(...) / removeListener(...) event interface, and a legacy enable() that maps to quai_requestAccounts.
  • Discovery also works via the EIP-6963 eip6963:announceProvider event, so libraries that enumerate providers will find Blip without touching globals.
If you already support Pelagus or an EVM injected wallet, you likely support Blip with zero changes — detect it with window.quai?.isBlip and proceed.

The Blip provider

Requests follow EIP-1193: provider.request({ method, params }) returns a promise. Errors are thrown as provider errors with a numeric code and message. Both quai_* and the equivalent eth_* method names are accepted.

Supported methods

MethodNotes
quai_requestAccounts / eth_requestAccountsPrompts the user to connect. Returns the app-wallet address for your origin.
quai_accounts / eth_accountsReturns connected accounts without prompting.
wallet_requestPermissions / wallet_revokePermissionsEIP-2255-style permission grant / revoke.
quai_chainId / eth_chainId / net_versionReturns the current chain (default 0x9) without a network round-trip.
wallet_switchEthereumChain / wallet_addEthereumChainSwitch / add chain; fires chainChanged.
personal_sign / eth_sign / quai_signMessage signing.
quai_sendTransaction / eth_sendTransactionBuild, sign and broadcast. The primary send path. eth_sendRawTransaction is not supported.
wallet_getProviderStateDiscover app-wallet state & funding features (see below).
blip_requestAppWalletFundingAsk Blip to top up your app wallet from the main vault.

Events

The provider emits accountsChanged, chainChanged, connect, and disconnect. Subscribe with on():

window.quai.on("accountsChanged", (accounts) => { /* re-render */ });
window.quai.on("chainChanged", (chainId) => { /* chainId is hex, e.g. "0x9" */ });
window.quai.on("connect", (info) => { /* info.chainId */ });
window.quai.on("disconnect", (err) => { /* tear down */ });

App-wallet funding

Because each origin gets its own app wallet, that wallet may be empty when your dApp needs to act. The funding protocol lets you ask Blip to move funds from the user's main vault into the app wallet — either with an approval sheet, or silently for native QUAI within the user's per-app limit.

Discover support

Call wallet_getProviderState to learn the active app-wallet address and whether automatic native top-up is enabled.

wallet_getProviderState — response
{
  "wallet": "blip",
  "accounts": ["0x..."],
  "chainId": "0x9",
  "isConnected": true,
  "isUnlocked": true,
  "appWallet": {
    "connected": true,
    "address": "0x...",
    "autoTopUpEnabled": true,
    "nativeAutoTopUpLimitWei": "1000000000000000000000"
  },
  "features": {
    "appWalletNativeTopUp": true,
    "appWalletTokenFunding": true
  }
}
Auto top-up happens without an approval sheet only when the request is native QUAI only, autoTopUpEnabled is true, and amountWei is within nativeAutoTopUpLimitWei (default 1000 QUAI). Token funding always requires explicit approval. If the app wallet is underfunded at send time, Blip applies the same rules as a fallback before signing.

Request funding

Request the full action's shortfall, not just the input — Blip recommends estimating gas against the real transaction and adding at least a 10% fee buffer.

blip_requestAppWalletFunding — params
{
  "chainId": "0x9",
  "reason": "swap",
  "continueLabel": "Continue QUAI swap",
  "assets": [
    {
      "type": "native",
      "symbol": "QUAI",
      "decimals": 18,
      "amountWei": "0x56bc75e2d63100000",
      "purpose": "swapInput"
    },
    {
      "type": "erc20",
      "token": "0x...",
      "symbol": "WQI",
      "decimals": 18,
      "amount": "0x8ac7230489e80000",
      "purpose": "swapInput"
    }
  ]
}
Response
{
  "funded": true,
  "txHashes": ["0x..."],
  "balances": { "native": "0x...", "0xTokenAddress": "0x..." }
}

Errors use provider codes: 4001 user rejected, 4100 app wallet unavailable, 4902 unsupported chain, 4200 unsupported asset, -32010 insufficient main-vault balance, -32602 malformed params.

dApp quickstart

A minimal connect → sign → send flow inside the Blip browser. No SDK; this is plain EIP-1193.

1 · Detect Blip
function getBlip() {
  const p = window.quai || window.pelagus || window.ethereum;
  return p && (p.isBlip || p._isSwiftBlip) ? p : null;
}

const blip = getBlip();
if (!blip) {
  // Not inside Blip — offer the deep link instead.
  // location.href = "https://blippay.me/browser?url=" + encodeURIComponent(location.href);
}
2 · Connect
const [account] = await blip.request({ method: "quai_requestAccounts" });
console.log("Connected app wallet:", account);

const chainId = await blip.request({ method: "quai_chainId" }); // "0x9"
3 · Sign a message
const signature = await blip.request({
  method: "personal_sign",
  params: ["0x" + Buffer.from("Welcome to my dApp").toString("hex"), account],
});
4 · Fund the app wallet, then send
// Optional: top up the per-origin app wallet before a value transfer.
const state = await blip.request({ method: "wallet_getProviderState" });

if (state.appWallet?.connected) {
  await blip.request({
    method: "blip_requestAppWalletFunding",
    params: [{
      chainId: "0x9",
      reason: "gas",
      assets: [{ type: "native", symbol: "QUAI", decimals: 18,
                 amountWei: "0x16345785d8a0000", purpose: "gas" }],
    }],
  });
}

const txHash = await blip.request({
  method: "quai_sendTransaction",
  params: [{
    from: account,
    to: "0xRecipient...",
    value: "0xde0b6b3a7640000", // 1 QUAI
    data: "0x",
  }],
});
console.log("Submitted:", txHash);
5 · React to wallet changes
blip.on("accountsChanged", ([next]) => setAccount(next));
blip.on("chainChanged", () => window.location.reload());
Reminder: the from address is the app wallet for your origin, not the user's main vault. Read its balance with quai_getBalance, and lean on the funding protocol rather than asking users to manually move funds.

For AI agents

Building an agent, an LLM tool, or an automated client that needs to understand Blip? Start with the machine-readable summary — it follows the llms.txt convention and distills this page into a compact, link-rich brief.

Fetch the summary
curl https://blippay.me/llms.txt

What is automatable

  • Deep links. Agents can construct and hand off https://blippay.me/browser?url=..., /referral, and /contact links deterministically — see Deep links for exact params.
  • Transaction building. Prepare Quai transactions off-app with quais.js against a Quai RPC, then have the user submit them in Blip.
  • In-browser provider. An agent driving a browser session inside the Blip in-app browser uses the same EIP-1193 window.quai surface documented above.

What is not

  • No headless wallet API. Blip exposes no remote endpoint to control the wallet. The provider exists only inside the in-app browser.
  • Human in the loop. Every signature and every send requires explicit on-device user approval. Blip is self-custody by design — there is no key an agent can hold or act with autonomously.
Design agent flows so the human approves on device: prepare the work, hand off via a deep link or the in-browser provider, and let the user confirm. Treat /llms.txt as the canonical, always-current source for the facts on this page.

Where to go next