← Back

Integration Guide

BQUser Integration Guide

Third-party wallets, exchanges, or services that want to resolve BQUser identities (username → BCH address) can do so via the hosted REST API or by querying the blockchain directly. No API keys, no registration with BQuest, and no BCH spend is required.

For the underlying protocol rules see Protocol.spec.md.


Option A — Hosted REST API (recommended)

A lightweight lookup server is hosted alongside the BQuest app. Send plain HTTP GET requests — no Chaingraph interaction needed on your end.

Base URL: https://api.bquest.cash/v1

Endpoints

GET /v1/lookup?username=alice

Resolve a username to its canonical BCH address.

Optional query parameter: platform=9 (filter by platform ID; default returns the first valid identity regardless of platform).

Success (200)

{
  "username": "alice",
  "address": "bitcoincash:z...",
  "pkh": "7c8e...",
  "platformId": 9,
  "platformName": "BQuest",
  "valid": true,
  "posValidity": 2,
  "negValidity": 1,
  "txHash": "ab12..."
}

Not found (404)

{ "error": "Identity not found", "username": "alice" }

Disputed (404)

{ "error": "Identity exists but is disputed", "username": "alice" }

GET /v1/users

Returns all valid identities across all platforms.

Optional query parameter: platform=9 (filter by platform ID).

{
  "users": [ { "username": "alice", "address": "...", ... }, ... ],
  "count": 42
}

GET /v1/health

{ "status": "ok", "tokenCategory": "416d0f..." }

Option B — Direct Blockchain Lookup

Query Chaingraph directly if you prefer not to depend on the hosted API.

What You Need

RequirementValue
Token category ID416d0f665cd0eabcd3e01a3508eb44099e1db08c89dd06a792a7f50d43522f18
IndexerChaingraph GraphQL (or equivalent BCH UTXO indexer)
NetworkBCH mainnet only

Chaingraph public endpoint: https://gql.chaingraph.pat.mn/v1/graphql


Step 1 — Encode the Username as a Commitment Prefix

Usernames are 4–15 characters, lowercase a–z, 0–9, _ only. Always normalise to lowercase before encoding.

The commitment prefix is:

<1 byte: name length as hex> <N bytes: ASCII hex of each character>

Example — alice:

FieldValue
Length05 (5 characters)
Name hex616c696365
Prefix05616c696365

Step 2 — Query Chaingraph for Unspent Outputs

Fetch all unspent BQUser NFT outputs whose commitment starts with the prefix. Chaingraph does not support prefix filtering natively, so fetch all outputs for the token category and filter client-side (or narrow by commitment equality if you have the full commitment).

query GetBQUserByUsername {
  output(
    where: {
      token_category: { _eq: "\\x416d0f665cd0eabcd3e01a3508eb44099e1db08c89dd06a792a7f50d43522f18" }
      nonfungible_token_commitment: { _is_null: false }
      _not: { spent_by: {} }
    }
    limit: 1000
  ) {
    locking_bytecode
    nonfungible_token_commitment
    transaction { hash }
  }
}

Filter results: keep rows where nonfungible_token_commitment starts with your encoded prefix (after stripping the \x escape Chaingraph adds to binary fields).


Step 3 — Apply the Validity Check

Each matching commitment has the structure:

<nameLen:1><nameHex:N><platformId:1><posValidity:1><negValidity:1>

An identity is valid when posValidity > negValidity. Discard any row where negValidity >= posValidity.

BQuest platform ID is 09. A future platform using the same token category would use a different platform ID — filter to 09 if you only want BQuest identities.


Step 4 — Apply the Canonical Identity Rule

If multiple valid outputs match the same <username, platformId> pair, the earliest on-chain occurrence of the initial state (posValidity=02, negValidity=01) is the canonical identity. Identify it by the transaction.hash (earliest block height / lowest tx index wins).

Later duplicates registered out-of-band are ignored by compliant validators.


Step 5 — Extract the BCH Address

The locking bytecode of a BQUser output is standard P2PKH:

76a914<20-byte public key hash>88ac

Extract the 20-byte PKH (bytes 3–22, i.e. hex chars 6–45) and encode as a token-aware CashAddress:

type:  p2pkhWithTokens
prefix: bitcoincash

This gives the canonical send address for the identity holder.


Quick Reference

TOKEN_CATEGORY = 416d0f665cd0eabcd3e01a3508eb44099e1db08c89dd06a792a7f50d43522f18
PLATFORM_ID   = 09  (BQuest)
VALID         = posValidity > negValidity
CANONICAL     = earliest on-chain tx with initial state 02 01
ADDRESS_TYPE  = p2pkhWithTokens CashAddress

Self-Hosting the API Server

The API server source lives at api/server.mjs in the BQuest repository. Requires Node.js 18+ (no extra npm packages).

node api/server.mjs           # runs on port 3100
PORT=8090 node api/server.mjs # custom port
BQUSER_TOKEN_ID=<hex> node api/server.mjs  # override token category

Behind nginx, add a location block that proxies to the server port.


Issuing Identities (Third-Party Registration)

Any application can register a BQUser identity on behalf of a user by calling the createBQ_User function on the deployed BQUserGeneration contract. No permission from BQuest is required — the contract enforces the rules on-chain and accepts any transaction that satisfies them.

Contract address (mainnet): derive from BQUserGenerationArtifact.json with admin pubkey 035903fa52eba1313e2482316f3265b3c8822b4aaa771f18710b07450febe10593 and address type p2sh32.

Compiled artifact: BQUserGenerationArtifact.json in the BQuest repository.


Transaction Structure — createBQ_User

#InputRequirement
0Contract minting UTXOBQUser minting token held at the contract address
1Candidate payment UTXOP2PKH UTXO controlled by the candidate's key; ≥ 1,000,000 sats
#OutputRequirement
0Contract tokenAddressBQUser minting token returned unchanged (full tokenCategory including capability byte)
1Candidate tokenAddressNewly minted BQUser immutable NFT (see commitment format below)
2Contract address≥ 500,000 sats (50% of registration fee — prize pool contribution)
3Candidate address~497,200 sats returned to candidate (50% minus NFT dust and miner fee)
4Candidate address(optional) excess change if payment > 1,000,000 sats

Input and output counts are enforced: exactly 2 inputs, 4–5 outputs.


NFT Commitment Format

<nameLen: 1 byte> <nameHex: nameLen bytes> <platformId: 1 byte> <posValidity: 1 byte> <negValidity: 1 byte>
FieldValueNotes
nameLen1 byte, value 4–15Length of username in bytes
nameHexASCII hexLowercase a–z, 0–9, _ only
platformId0x09BQuest contract — this value is enforced on-chain
posValidity0x02Initial positive validity score
negValidity0x01Initial negative validity score

The trailing 3 bytes must be exactly 0x09 0x02 0x01 — the contract rejects any other value. Platform ID 0x09 is accurate: it means "registered via the BQUser generation contract".

Example — username alice:

05 616c696365 09 02 01

Candidate Signature Requirement

Input 1 must be a P2PKH UTXO controlled by the candidate's key. The contract extracts the PKH from input 1's locking bytecode and verifies that playerPub hashes to it. The candidate must sign the transaction (playerSig).

This means the candidate's wallet must construct and sign the transaction — or your application must hold the candidate's key temporarily (not recommended). The standard pattern is to have the candidate sign client-side and broadcast the fully-formed transaction.


Notes

  • The registration fee of 1,000,000 sats (0.01 BCH) is enforced by the contract. Half goes to the prize pool; half is returned to the candidate.
  • After registration, the BQUser NFT sits permanently in the candidate's wallet. It must not be spent or used as a contract input — doing so resets UTXO age and breaks canonical disambiguation.
  • BQRepute auto-minting (the second step of full identity activation) is handled by the BQuest app on first load. Third-party issuers can trigger it manually via the BQRepute contract — see Protocol.spec.md §8 for the full flow.

Notes

  • The lookup is mainnet only. Chaingraph does not index chipnet.
  • Validity is a point-in-time snapshot — re-query before high-value sends.
  • The protocol does not block funds sent to disputed addresses. Senders bear responsibility for validating identity before transferring funds.