Skip to content

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

bash
# 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 120

Signing Paths

Pick exactly one.

PathFlagUse when
Browser wallet--browserYour 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:

text
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

bash
bb deploy col.json --browser --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --wait-for-indexer 60000
FlagDefaultDescription
[input], --msg-file <path>, --msg-stdinMessage 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-runSimulate and print expected gas and balance changes; never broadcast. Needs an API key off --local.
--wait-for-indexer [timeout-ms]30000After 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, 400000Burner fee in ubadge; 0 requests estimation at 10ubadge/gas. Gas is a floor for the buffered estimate.
network flagsmainnetSee CLI

--dry-run differs from bb build --simulate: --simulate augments the build output and still emits JSON; --dry-run simulates and exits.

--browser

bash
bb deploy --browser --msg-file collection.json --manager bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d
json
{ "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.

FlagDescription
--expected-address <addr>Required signer binding; defaults to an explicit creator/manager or the action’s sender. The wallet must match.
--sign-onlyCompatible 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-openPrint 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

bash
bb deploy --browser --message "Sign in to my application with this one-time challenge" --expected-address bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d
json
{
  "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

bash
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
FlagDefaultDescription
--from <name>Keyring identity
--execRun the printed command in place. Inherits the TTY so keyring password prompts work. Multi-message transactions run sequentially, not atomically.
--binary <name>bitbadgeschaindChain binary on PATH
--keyring-backend <backend>osos, file, test, pass, kwallet
--gas-adjustment <n>1.3Passthrough

--gen-payload

bash
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 600000

The payload for the third command (synthesized for a funded account; byte strings shortened):

json
{
  "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"
}
FieldPresent whenUse
signDirect.signBytes--from is a bb1 address, or a Cosmos pubkey is on chainSign for SIGN_MODE_DIRECT
legacyAmino.signBytessameHardware wallets that speak amino
signDirect.bodyBytes, authInfoBytessameBuild TxRaw{bodyBytes, authInfoBytes, signatures: [sig]} after signing
evmTx--from is a 0x address, or --evm-from or --with-evm-tx is setwallet.sendTransaction({to, data, value, chainId, gasLimit})
broadcastEndpointalwaysWhere to POST the assembled TxRaw
FlagDescription
--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-txAlso 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-fetchSkip 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

bash
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
json
{
  "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: MsgCreateCollection or MsgUniversalUpdateCollection with 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/ (files 0600, directory 0700), with mnemonic, address, network, and broadcast status. Never fund them beyond fees; sweep any excess with bb burner sweep.
  • Not offline-capable; each run leaves a new on-chain account.
FlagDefaultDescription
--fund <faucet|manual>faucetfaucet calls the BitBadges API faucet (needs an API key off --local). manual prints the address and waits.
--newSkip the picker; always a fresh burner
--reuse <selector>Reuse a saved burner by address or recovery file path
--non-interactiveNever prompt; save state and exit at any prompt. Forced when stdout is not a TTY.
--poll-timeout <seconds>60Wait 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

bash
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 -y

The selector is an address or a recovery file path.

tx status and tx wait

bash
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.code

The first command, for a real mainnet transaction (events trimmed):

json
{
  "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.

FlagDefaultDescription
--node-url <url>per networkChain LCD override
--evm-rpc-url <url>per networkEVM JSON-RPC override
--timeout <seconds> (wait)60Max wait
--interval <seconds> (wait)2Poll interval
Exit codeMeaning
0Committed (Cosmos code === 0 or EVM status === 0x1)
1Included but failed (non-zero code or revert)
2RPC 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:

  1. The CLI starts a loopback HTTP listener on 127.0.0.1:<port>.
  2. It opens https://bitbadges.io/sign?... with the request in the URL (or a short code when the payload is large).
  3. You review and sign with the connected wallet.
  4. The page redirects to 127.0.0.1:<port>/callback?... with the signature or hash. The listener accepts one callback.
ModeCommandSignedReturned
loginbb auth login --browserThe SIWBB challengeSignature; the CLI replays it on /auth/verify for a session cookie
msgbb deploy --browser --messageAny stringSignature, address, pubkey for Cosmos
txbb deploy --browser, bb build vault --browserOne transaction; collection messages get the full review sidebarHash, 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:

text
Host dev-server
    LocalForward 4849 localhost:4849
bash
bb auth login --browser --address bb1p0rrel3365scadq5k9pv0x0zp9j22js6dnw70d --port 4849

Or 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.

Edit this page on GitHub