bb deploy and bb tx
Sign and broadcast with bb deploy (browser wallet, keyring, burner, or generated payload), confirm with bb tx status and tx wait, and use the sign bridge.
bb deploy broadcasts a message through one of four signing paths, and bb tx status / bb tx wait confirm the hash on chain. This page also documents the browser sign bridge that --browser and bb auth login --browser share.
Example
# review and sign in a browser wallet (Keplr, MetaMask)
bb build vault --backing-coin USDC --uri ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/collection.json --quiet \
| bb deploy --browser --msg-stdin --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d
# sign with a key in the chain binary keyring
bb deploy --with-keyring --from alice --exec --msg-file col.json
# emit a signable payload for ethers, viem, cosmjs, or an HSM
bb deploy --gen-payload --from bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --msg-file col.json --gas 600000
# dry run first, then confirm the hash
bb deploy col.json --browser --dry-run --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d
bb tx wait 903D4A6E205AD77D334933E3C9BB455012D8A334AA2D98DFD301C3F7E8AB92C6 --timeout 120Signing Paths
Pick exactly one.
| Path | Flag | Use when |
|---|---|---|
| Browser wallet | --browser | Your key lives in Keplr, MetaMask, Phantom, or WalletConnect. The CLI opens /sign, you confirm, the hash comes back. |
| Keyring | --with-keyring --from <name> | A key imported with bb keys add. Headless scripts where one long-lived key signs many transactions. |
| Payload | --gen-payload --from <address> | A programmatic signer: ethers, viem, cosmjs, custodial, hardware. Nothing is signed or sent. |
| Burner | --burner --manager <address> | A throwaway signer funded from the faucet, create-collection only. Needs a faucet; testnet is offline, so this path is unavailable until it returns. |
--browser and --burner use the standard output envelope, with path-specific result fields. --with-keyring prints the chain binary command, or runs it with --exec. --gen-payload prints the payload and exits.
Starting with v35, transactions require fees of at least 10ubadge per unit of gas. Fund the signing account with BADGE before broadcasting. With the updated CLI, burner --fee 0 means automatic fee estimation; it does not produce a zero-fee transaction. If you need BADGE, use the faucet when available or ask in the BitBadges Discord.
Ask your agent:
I have collection.json. I want to sign with Keplr in my browser on mainnet. Give me the exact bb deploy command with the right flags, then tell me how to confirm the hash landed.deploy
bb deploy col.json --browser --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --wait-for-indexer 60000| Flag | Default | Description |
|---|---|---|
[input], --msg-file <path>, --msg-stdin | Message JSON. [input] is a path, -, or inline JSON. A bb build envelope is unwrapped on read. | |
--manager <address> | Owner of the created collection. Required for --burner; recommended for --browser. | |
--dry-run | Simulate and print expected gas and balance changes; never broadcast. Needs an API key off --local. | |
--wait-for-indexer [timeout-ms] | 30000 | After broadcast, poll the BitBadges API until the created collection or dynamic store appears. Adds waited: { entity, id, attempts, elapsedMs, ok, body } (or { ok: false, lastStatus }). |
--fee <amount>, --fee-denom <symbol|denom>, --gas <n> | 0, ubadge, 400000 | Burner fee in ubadge; 0 requests estimation at 10ubadge/gas. Gas is a floor for the buffered estimate. |
| network flags | mainnet | See CLI |
--dry-run differs from bb build --simulate: --simulate augments the build output and still emits JSON; --dry-run simulates and exits.
--browser
bb deploy --browser --msg-file collection.json --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d{ "success": true, "path": "browser", "mode": "sign-and-broadcast", "outcome": "submitted", "confirmed": false, "verification": "unverified", "txHash": "E5B4C3A6E5B1F3B9F0F4C1F2B7A6D5C4E3F2A1B0C9D8E7F6A5B4C3D2E1F0A9B8", "chain": "cosmos" }The site's transaction modal fetches account number, sequence, gas, and fees at sign time, so the message JSON needs none of them. Browser requests bind the signer, network, chain IDs, expiration, and reviewed messages. CLI fee/gas flags are not applied to this path and do not impose a wallet fee cap. MetaMask signs an MsgEthereumTx-wrapped transaction and the Cosmos hash comes back; set --manager to the bb1 form of your ETH address.
| Flag | Description |
|---|---|
--expected-address <addr> | Required signer binding; defaults to an explicit creator/manager or the action’s sender. The wallet must match. |
--sign-only | Compatible Cosmos adapters only: return base64 signed bytes without sending. EVM send-on-sign paths are rejected before wallet invocation. |
--frontend-url <url> | Override the frontend base (defaults per network) |
--no-open | Print the sign URL to stderr instead of launching the browser |
--port <n> | Pin the loopback listener port (for SSH-forwarded setups) |
--timeout <seconds> | Wait for the wallet (default 300, max 1800) |
Browser transaction results include outcome: "submitted" or "signed", confirmed: false, and verification: "unverified". The success flag indicates a matching browser result, not successful chain execution. Confirm a submitted hash with bb tx status or bb tx wait on the same network.
Sign-only returns signedTx only for a compatible Cosmos adapter. It is limited to 4096 base64 characters by the current URL callback transport. Larger results return an explicit error; bytes are never truncated. The CLI does not independently verify the signature or compare decoded signed bytes to the requested messages. Inspect and verify them before any later broadcast through another system.
The request expires within the selected timeout (at most 30 minutes), including upload time. Expiration prevents a new signing invocation, not an already submitted transaction. A timeout after browser launch returns outcome: "unknown" with retrySafe: false and a nonzero exit. Closing a browser can also leave the outcome unknown; reconcile wallet activity and chain state before retrying. New versioned requests require the matching frontend release; legacy transaction approval pages cannot provide the required result binding. The new frontend accepts supported legacy transaction requests through a separate adapter and returns their original callback format, while applying the same final wallet and message checks. Missing legacy signer/network fields are pinned once at review; they do not carry the stronger original-request guarantees of V2. Login and personal-message callbacks retain their existing transport.
See Main-Wallet Payment Requests for exact invoice and direct-transfer flows.
Browser transaction requests are saved locally. Use bb dev requests list and bb dev requests status <requestId> to inspect them. bb dev requests resume <requestId> returns the same signing URL only while the original CLI listener is alive and the request is unexpired and incomplete. Keep that process running. These commands never create or submit a replacement payment; a lost listener reports an unknown outcome. See request recovery.
bb build <type> --browser composes build and this path in one step, and accepts --sign-only.
--browser --message
bb deploy --browser --message "Sign in to my application with this one-time challenge" --expected-address bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d{
"signature": "Bk/AuuDrt/kChEgF4myQJMCX//1wqoEffd+4RghpKaU0q2QRpVjw0zaf+WHs3FnM8AsEnKm3CeMLc/2/AfKQ9A==",
"address": "bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d",
"publicKey": "A7TliNEJ+WoiaZvtnrOrdIuLIaVayagqvp47kF2L3Np3",
"chain": "Cosmos"
}Personal-sign a message with the browser wallet (the signature and key above are from a throwaway key, shown for shape). This proves a signature over that message; it does not install an on-chain allowance or authorize an invoice payment. Application login challenges need their own domain, nonce, and expiration validation. Input is --message <text>, --message-file <path> (- for stdin), or a positional (- or @path). ETH signatures omit publicKey because personal_sign is recoverable. This replaces the standalone sign-with-browser command.
--with-keyring
bb deploy --with-keyring --from alice --msg-file col.json # print the bitbadgeschaind tx command
bb deploy --with-keyring --from alice --exec --msg-file col.json # run it, capture the hash, emit the envelope| Flag | Default | Description |
|---|---|---|
--from <name> | Keyring identity | |
--exec | Run the printed command in place. Inherits the TTY so keyring password prompts work. Multi-message transactions run sequentially, not atomically. | |
--binary <name> | bitbadgeschaind | Chain binary on PATH |
--keyring-backend <backend> | os | os, file, test, pass, kwallet |
--gas-adjustment <n> | 1.3 | Passthrough |
--gen-payload
bb build vault --backing-coin USDC --uri ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/collection.json --quiet \
| bb deploy --gen-payload --from bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --gas 600000
bb deploy --gen-payload --msg-file col.json --from 0x0bc63cfe31d5218eb414b142c799e20964a54a1a --gas 600000
bb deploy --gen-payload --msg-file col.json --from bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --with-evm-tx --gas 600000The payload for the third command (synthesized for a funded account; byte strings shortened):
{
"chain": "cosmos+evm",
"chainId": "bitbadges-1",
"sender": { "address": "bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d", "accountNumber": "7", "sequence": "3", "publicKey": "A7TliNEJ+WoiaZvtnrOrdIuLIaVayagqvp47kF2L3Np3" },
"evmAddress": "0x0bc63cfe31d5218eb414b142c799e20964a54a1a",
"fee": { "amount": "6000000", "denom": "ubadge", "gas": "600000" },
"memo": "",
"messages": [{ "typeUrl": "/tokenization.MsgCreateCollection", "value": { "creator": "bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d" } }],
"signDirect": { "bodyBytes": "CpIBCo8BCiEvdG9rZW5pemF0aW9uLk1zZ0NyZWF0ZUNvbGxlY3Rpb24=", "authInfoBytes": "ClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleQ==", "signBytes": "Athauu8aoa3qxL2n1o1yQ2Q2dQ6n3n8Uu1G9xI0uY0k=" },
"legacyAmino": { "bodyBytes": "CpIBCo8BCiEvdG9rZW5pemF0aW9uLk1zZ0NyZWF0ZUNvbGxlY3Rpb24=", "authInfoBytes": "ClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleQ==", "signBytes": "9bJ06ZSKq8v2Q0m4t1r7c3d5e6f8a9b0c1d2e3f4g5h6i7j8k9l0=" },
"evmTx": { "to": "0x0000000000000000000000000000000000001001", "data": "0x059dfe130000000000000000000000000000000000000000000000000000000000000020", "value": "0", "functionName": "createCollection", "chainId": 50024, "gasLimit": "600000" },
"broadcastEndpoint": "https://api.bitbadges.io/api/v0/broadcast"
}| Field | Present when | Use |
|---|---|---|
signDirect.signBytes | --from is a bb1 address, or a Cosmos pubkey is on chain | Sign for SIGN_MODE_DIRECT |
legacyAmino.signBytes | same | Hardware wallets that speak amino |
signDirect.bodyBytes, authInfoBytes | same | Build TxRaw{bodyBytes, authInfoBytes, signatures: [sig]} after signing |
evmTx | --from is a 0x address, or --evm-from or --with-evm-tx is set | wallet.sendTransaction({to, data, value, chainId, gasLimit}) |
broadcastEndpoint | always | Where to POST the assembled TxRaw |
| Flag | Description |
|---|---|
--from <address> | A bb1 address emits signDirect + legacyAmino; a 0x address emits evmTx (plus signDirect if a pubkey is on chain) |
--evm-from <address> | EVM address for evmTx next to a bb1 sender. Implies --with-evm-tx. |
--with-evm-tx | Also emit the precompile call for a bb1 sender |
--public-key <b64>, --account-number <n>, --sequence <n> | Overrides. Required with --no-fetch or for an account not yet on chain. |
--chain-id <id>, --memo <text> | Cosmos chain ID override; memo |
--no-fetch | Skip the BitBadges API account lookup (offline or air-gapped) |
For a 0x --from the EVM transaction signs itself with the wallet's secp256k1 key; no separate pubkey. signDirect for an ETH user needs a pubkey on chain, which one prior transaction sets. Covers every tokenization.Msg* type, which is every bb build output. Other modules (IBC, gov) are out of scope. This replaces the standalone gen-tx-payload command.
--burner
bb build subscription --interval monthly --price 10 --denom USDC \
--recipient bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d \
--uri ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi/collection.json --quiet \
| bb deploy --burner --msg-stdin --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --local --fund faucet{
"success": true,
"ephemeralAddress": "bb1hz59w73vsqygl7z9zl49yvwjnl534n0yyexmkf",
"recoveryPath": "/home/you/.bitbadges/burners/2026-09-06T00-00-00-000Z-bb1hz59w73vsqygl7z9zl49yvwjnl534n0yyexmkf.json",
"txHash": "E5B4C3A6E5B1F3B9F0F4C1F2B7A6D5C4E3F2A1B0C9D8E7F6A5B4C3D2E1F0A9B8",
"collectionId": "28"
}--fund faucet needs a live faucet. Testnet is offline, so the burner path works only against a local chain today. Mainnet has no faucet; --fund manual waits for you to send dust yourself. See Testnet.
Two addresses are involved: the burner, generated fresh and discarded after one signature, and the owner you pass as --manager, who holds the collection from the first block. The burner has no lasting authority.
Constraints:
- Create-collection only:
MsgCreateCollectionorMsgUniversalUpdateCollectionwith a new collection ID. Updates, transfers, approvals, and manager changes are rejected before the wallet is generated. - Dust only. Burners are stored in plaintext under
~/.bitbadges/burners/(files0600, directory0700), with mnemonic, address, network, and broadcast status. Never fund them beyond fees; sweep any excess withbb burner sweep. - Not offline-capable; each run leaves a new on-chain account.
| Flag | Default | Description |
|---|---|---|
--fund <faucet|manual> | faucet | faucet calls the BitBadges API faucet (needs an API key off --local). manual prints the address and waits. |
--new | Skip the picker; always a fresh burner | |
--reuse <selector> | Reuse a saved burner by address or recovery file path | |
--non-interactive | Never prompt; save state and exit at any prompt. Forced when stdout is not a TTY. | |
--poll-timeout <seconds> | 60 | Wait for funding before prompting or exiting |
In a TTY with saved burners, a picker lists them with balance and status. Reusing a funded wallet skips the faucet. If funding stalls past --poll-timeout, the CLI offers: keep waiting, retry the faucet, pause and exit (writes pending state), or give up. Non-interactive runs default to pause and exit. Failure envelopes carry a hint (for example pointing at bb burner sweep on insufficient funds).
burner
bb burner list --network local
bb burner show bb1hz59w73vsqygl7z9zl49yvwjnl534n0yyexmkf # includes the mnemonic
bb burner resume bb1hz59w73vsqygl7z9zl49yvwjnl534n0yyexmkf --msg-file col.json \
--manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --fund faucet --fee 0 --fee-denom ubadge --gas 400000 --poll-timeout 60
bb burner sweep bb1hz59w73vsqygl7z9zl49yvwjnl534n0yyexmkf --to bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --denom ubadge --fee 0 --gas 200000
bb burner forget bb1hz59w73vsqygl7z9zl49yvwjnl534n0yyexmkf -yThe selector is an address or a recovery file path.
tx status and tx wait
bb tx status 903D4A6E205AD77D334933E3C9BB455012D8A334AA2D98DFD301C3F7E8AB92C6
bb tx status 0x9f1c2b3a4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f8 # EVM hash
bb tx wait 903D4A6E205AD77D334933E3C9BB455012D8A334AA2D98DFD301C3F7E8AB92C6 --timeout 120 --interval 2
bb tx status 903D4A6E205AD77D334933E3C9BB455012D8A334AA2D98DFD301C3F7E8AB92C6 | jq -r .data.codeThe first command, for a real mainnet transaction (events trimmed):
{
"ok": true,
"data": {
"via": "cosmos",
"hash": "903D4A6E205AD77D334933E3C9BB455012D8A334AA2D98DFD301C3F7E8AB92C6",
"height": "11980239",
"code": 0,
"gasUsed": "462095",
"events": []
},
"warnings": [],
"error": null
}An unknown hash returns "ok": false with error.code not_found, the list of backends tried, and a hint to run tx wait.
Both hit the chain directly: the Cosmos LCD /cosmos/tx/v1beta1/txs/{hash} first, then the EVM JSON-RPC eth_getTransactionReceipt for keccak256 hashes (via becomes evm). No BitBadges API round trip, so they work while the API is degraded. Hashes are accepted with or without 0x, any case.
| Flag | Default | Description |
|---|---|---|
--node-url <url> | per network | Chain LCD override |
--evm-rpc-url <url> | per network | EVM JSON-RPC override |
--timeout <seconds> (wait) | 60 | Max wait |
--interval <seconds> (wait) | 2 | Poll interval |
| Exit code | Meaning |
|---|---|
0 | Committed (Cosmos code === 0 or EVM status === 0x1) |
1 | Included but failed (non-zero code or revert) |
2 | RPC error, not found, or (wait) timeout. On timeout error.code is timeout and hint says to re-run tx status or extend --timeout. |
Chain releases before the forwarder fix do not expose bb tx status or bb tx wait. If they print unknown command, run bitbadges-cli tx status 903D4A6E205AD77D334933E3C9BB455012D8A334AA2D98DFD301C3F7E8AB92C6.
Sign Bridge
--browser on deploy and bb auth login --browser share one mechanism, modeled on gh auth login --web:
- The CLI starts a loopback HTTP listener on
127.0.0.1:<port>. - It opens
https://bitbadges.io/sign?...with the request in the URL (or a short code when the payload is large). - You review and sign with the connected wallet.
- The page redirects to
127.0.0.1:<port>/callback?...with the signature or hash. The listener accepts one callback.
| Mode | Command | Signed | Returned |
|---|---|---|---|
login | bb auth login --browser | The SIWBB challenge | Signature; the CLI replays it on /auth/verify for a session cookie |
msg | bb deploy --browser --message | Any string | Signature, address, pubkey for Cosmos |
tx | bb deploy --browser, bb build vault --browser | One transaction; collection messages get the full review sidebar | Hash, or signed bytes with --sign-only |
The /sign page shows a review-and-trust warning, the full request, a wallet-mismatch panel that disables Sign until the connected address matches (with a disconnect shortcut), and one Sign button. Defenses: loopback-only redirect targets (RFC 8252 native-app exception), a per-request state nonce (mismatch is a 403), a single-shot listener (later requests get 410 Gone), and the wallet's own confirmation popup. Out of scope: an attacker-supplied CLI, a malicious wallet extension, and multi-transaction atomicity on EVM (each deploy --browser is one transaction).
SSH-forwarded setups: the browser's loopback is the laptop's, not the server's. Pin a port and forward it:
Host dev-server
LocalForward 4849 localhost:4849bb auth login --browser --address bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --port 4849Or run the CLI on the laptop and point it at the remote services with --frontend-url http://localhost:3000 --url http://localhost:3001/api/v0.