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
| Requirement | Value |
|---|---|
| Token category ID | 416d0f665cd0eabcd3e01a3508eb44099e1db08c89dd06a792a7f50d43522f18 |
| Indexer | Chaingraph GraphQL (or equivalent BCH UTXO indexer) |
| Network | BCH 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:
| Field | Value |
|---|---|
| Length | 05 (5 characters) |
| Name hex | 616c696365 |
| Prefix | 05616c696365 |
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
| # | Input | Requirement |
|---|---|---|
| 0 | Contract minting UTXO | BQUser minting token held at the contract address |
| 1 | Candidate payment UTXO | P2PKH UTXO controlled by the candidate's key; ≥ 1,000,000 sats |
| # | Output | Requirement |
|---|---|---|
| 0 | Contract tokenAddress | BQUser minting token returned unchanged (full tokenCategory including capability byte) |
| 1 | Candidate tokenAddress | Newly minted BQUser immutable NFT (see commitment format below) |
| 2 | Contract address | ≥ 500,000 sats (50% of registration fee — prize pool contribution) |
| 3 | Candidate address | ~497,200 sats returned to candidate (50% minus NFT dust and miner fee) |
| 4 | Candidate 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>
| Field | Value | Notes |
|---|---|---|
nameLen | 1 byte, value 4–15 | Length of username in bytes |
nameHex | ASCII hex | Lowercase a–z, 0–9, _ only |
platformId | 0x09 | BQuest contract — this value is enforced on-chain |
posValidity | 0x02 | Initial positive validity score |
negValidity | 0x01 | Initial 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
BQReputecontract — seeProtocol.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.