@chipi-stack/backend (14.16.0+) ships both halves:
createStarknetSignInTypedDatabuilds the message.verifyStarknetSignInchecks it: chain, domain, URI, nonce, expiry, and the signature.
is_valid_signature) and with accounts that are not deployed yet, which is how new Bramble and Ready accounts start out.
The browser example uses Bramble. Braavos and Ready take the same requests; only discovery differs.
SIWS is mainnet only. The domain’s chainId must be SN_MAIN; from 14.17.0 its felt form (0x534e5f4d41494e, hex or decimal) is accepted too, since wallets echo either.
1. Issue the message (server)
Store the nonce with a short expiry, then return the typed data to the browser.SN_MAIN in the domain, and single-line printable ASCII strings. It throws on anything else, because some wallets (Bramble among them) refuse to sign it.
2. Sign it (browser)
Bramble injectswindow.starknet_bramble (id "bramble"). It does not set window.starknet, and it can appear after DOMContentLoaded, so wait for its starknet#initialized event before deciding it is missing.
Bramble is strict, and the snippet follows its rules:
- Prompts need a click. Bramble opens its panel only during a user gesture, so start from a click handler. Each prompt stays pending until the user decides; don’t retry it automatically.
- Params are parsed strictly. Pass
{ silent_mode: false }towallet_requestAccounts, neverundefinedparams or extra keys (114). - Mainnet first. Bramble refuses typed data whose domain
chainIdis not its active network (114, before any prompt). Checkwallet_requestChainIdbefore you ask for a signature. - Deployment data.
wallet_deploymentDataanswers115when the account is already deployed (send nothing) and116when Bramble can’t derive it.
163 with no detail, so show one message with a manual retry button.
This snippet connects and signs from one click. Bramble opens its panel only during a user gesture, and the click’s gesture can expire while the user approves the connection. If the signature prompt doesn’t open, split the flow into two clicks: one to connect, one to sign. The signature is [r, s] for Bramble’s OpenZeppelin account and Braavos, and ["1", "0", publicKey, r, s] for Ready (Argent). Send it as returned; the server passes the whole array to the account.
With get-starknet v4, getAvailableWallets() finds Bramble by id === "bramble"; run it after injection. starknetkit needs Bramble added as an explicit InjectedConnector.
3. Verify (server)
Consume the nonce (once, atomically), then verify:ADDRESS_MISMATCH has two sources: deployment data for another account than the one in the message, or deployment data whose constructor arguments don’t produce its own address.
The typed data is rebuilt from its message before hashing, so a client can’t swap in extra types or a testnet domain. Only 'VALID' counts as valid: accounts answer 0 or revert on a bad signature, and a looser “anything non-zero” check would accept garbage.
Undeployed accounts are supported for OpenZeppelin 3.0 (Bramble’s default account, signature [r, s]) and Argent 0.4 without a guardian (Ready, signature ["1", "0", publicKey, r, s]). An undeployed Argent account with a guardian gets UNSUPPORTED_ACCOUNT_CLASS, because its second signature can’t be checked off-chain; so does any other class. Ask the user to make one transaction first: Bramble deploys an OpenZeppelin or Argent account in the same flow as its first transaction.
Lower-level helpers
isValidStarknetSignature(provider, address, hash, signature)asks any deployed account about any message hash.verifyUndeployedStarknetSignature(deploymentData, hash, signature)does the off-chain check on its own.
Related
- Trusted wallets: make Bramble a co-owner or guardian of a Chipi wallet
- Passkeys
- Connect with Chipi
✅ Verified against the SDK source on 2026-10-12.
