SuperAPI AI Directory

strk20-privacy

Security & auditing

View repository

Updated 10-16 10:21

About this skill

STRK20, Starknet privacy: router and mental model

STRK20 is a live-on-mainnet, note-based (UTXO) privacy pool for any ERC-20 on Starknet. It runs as a contract on Starknet itself, on existing wallets and existing liquidity, with a built-in compliance path. Unlike a fixed-denomination mixer, notes carry arbitrary amounts and change is handled automatically. Users shield tokens into the pool as encrypted notes, transact privately with onchain STARK-proof verification, and unshield when they want transparency.

Full doc pages sit verbatim in references/. The sections below are a condensed map. When a detail is load-bearing (an address, a version, an API shape), open the reference page.

Pick the route first

Goal Route Skill
Private dapp (DeFi, consumer, games) on top of users' wallets Starknet Wallet API; choose an invoke helper or shadow account by state lifetime strk20-wallet-api
The Cairo adapter a private DeFi flow calls Anonymizer contract (privacy_invoke) strk20-anonymizer-contracts
A privacy wallet, or a backend holding its own keys Privacy SDK (createPrivateTransfers) strk20-privacy-sdk
Embedded-wallet or AA product (Privy, Cartridge, chipi, cavos, Dynamic) These manage user keys and are not privacy-enabled today. Treat the product as the key-holder and take the SDK route strk20-privacy-sdk
Hide the main-wallet link during persistent account-based app activity Stable Wallet API shadow accounts for dapps; Privacy SDK for key-holding wallets and backends strk20-wallet-api or strk20-privacy-sdk
Fund from or withdraw to an EVM wallet (USDC) Privacy Bridge over Circle CCTP see Ecosystem below
Operate proof generation yourself Local Docker prover or remote host over SSH; screening still applies strk20-local-prover for deployment, strk20-privacy-sdk for wallet wiring

Rules of thumb from the docs. Start with the narrowest route that keeps user keys in the right place. Wallet API first for user-facing dapps. Never ask a normal dapp user for their viewing key. For private DeFi, use an app-specific invoke helper for stateless atomic operations and the canonical shadow-account infrastructure for persistent positions or returning pseudonymous identities. Check for a first-party path before writing Cairo: AVNU ships private swaps.

Map the trust boundary before coding

For the selected route, state who holds the signing key and viewing key, who discovers notes, who constructs the private action, who proves it, and who submits it. Name the wallet, RPC, relayer, prover, screening service, and app operators that can observe the request. End with a hidden-versus-visible table and list the wallet versions, package versions, addresses, audits, and network assumptions that still need live verification.

The mental model, one screen

  • Note: an immutable record of (owner, token, u128 amount), stored encrypted. UTXO semantics, spent whole, change comes back as a new note. Open notes skip amount encryption (protocol salt OPEN_NOTE_SALT = 1, encrypted notes use salt ≥ 2) so a DeFi output amount can be filled in after proving.
  • Nullifier: a Poseidon hash bound to the note and the owner's private viewing key, published on spend. Deterministic, unique, unlinkable. The sender cannot compute it, so senders cannot watch for their payment being spent.
  • Viewing keypair K = k·G on the STARK curve, registered once via SetViewingKey and treated as immutable. Registration is a prerequisite: both sender and recipient must be registered before a private transfer, and only the recipient can register themselves (wallets do it on first use). Note data is hidden with domain-separated Poseidon masking, channel secrets come from ephemeral ECDH, and at registration k is also encrypted to the auditor's public key (the compliance escrow).
  • Channels are directional sender-to-recipient lanes with per-token subchannels and dense, WriteOnce note indices. Recipients discover funds by scanning only their own channels, so cost scales with your own activity, not pool volume. A deposit is a channel from yourself to yourself.
  • Transactions are batches of actions in fixed phases 0 to 7: SetViewingKey, OpenChannel, OpenSubchannel, Deposit, UseNote, CreateEncNote/CreateOpenNote, Withdraw, InvokeExternal/ ComputeAndInvoke (phase 7 jointly limited to at most one). Per-token temporary balance may never go negative and must end exactly zero.
  • Proofs: the transaction executes in a virtual Starknet environment anchored to a recent block, then Stwo generates a STARK proof (~29 s on a 12-core / 46 GiB machine, hardware-dependent). Onchain checks before applying: program variant (VIRTUAL_SNOS), anchor within proof_validity_blocks of the tip (default 450 ≈ 15 min, governance-set), and the proven message hash must match the submitted actions.
  • Deposit screening: FPI screens the shielding address and signs every deposit, and the pool verifies that signature onchain. Protocol-level since the v0.14.3 upgrade, so it applies on every route, self-hosted provers included.

Hidden vs visible. Always be explicit

Hidden inside the pool: sender, receiver, token, amount, which notes were spent. Visible to everyone: registration events, deposits (depositor, token, amount), withdrawals (recipient, token, amount), published nullifiers (unlinkable without a viewing key), open-note token and amount in plaintext, and timing. A paymaster can decouple the submitting address from the user.

Per the official agent-skill repo: never attribute pool activity to a transaction's sender. Private transactions are relayed, so the sender is the relayer's account for every user. Read per-user activity from the pool's Deposit event (first indexed key), never from the transaction envelope.

Known limitations the docs state outright. Repeat them in anything you build or write:

  • Channel-open linkability. Opening a channel and moving funds in the same transaction or in tight succession can link a recipient to public activity. Spread setup and movement over time.
  • Distinctive amounts or rapid in-and-out patterns shrink the anonymity set.
  • The edges are public by design. Only movement inside the pool is encrypted.

Compliance model (get this exactly right)

  • Screening at the door: every deposit is FPI-screened and signature-verified onchain. No proving route bypasses it.
  • Selective disclosure after the fact: the user's private viewing key is escrowed to the auditor's public key at registration, using the same ephemeral ECDH scheme as channels. The auditor key is set by governance and supports threshold keys. Disclosure targets only users under a lawful request. There is no bulk-surveillance mode.
  • A viewing key can read, never spend. Auditor-key compromise would break confidentiality, never custody.
  • For public copy, the framing is "private by default, disclosable when required". Do not call the escrow a backdoor, and do not oversell. The edges (deposits, withdrawals, timing) are public.

Route status (snapshot 2026-09-29, verify before relying on it)

  • Stable Wallet API 0.10.4 and starknet.js 10.8.0 include shadow accounts. Starknet.js documents Ready and Xverse as STRK20-capable. Detect the connected wallet's advertised capability instead of inferring support from the brand.

  • Shadow accounts, called private sub-accounts in RC.4 and older docs, hide the main-wallet link during account activity. Privacy SDK 0.14.3-rc.8 uses the names introduced in RC.5: build().shadowAccounts(dappName), shadowAccountAnonymizerAddress, and the Cairo package shadow_account_anonymizer. The renamed views and event use new selectors, so RC.5 requires the upgraded anonymizer and an indexer spanning the upgrade must match both event keys. Stable @starknet-io/types-js 0.10.4 and starknet.js 10.8.0 include the action and commitment method. Require the wallet to advertise Wallet API 0.10.4 or later, then handle an unsupported-method response. The low-level SDK remains a release candidate; its release status does not make the stable Wallet API route prerelease.

    The canonical ShadowAccountAnonymizer is deployed at 0x04f33230dc57855c6e7eabe66dfa0fde82c5458fd0e54827cdb7cb4c474888a7 on Mainnet and 0x010a2285310c107c731d997afc147afb7495daff6397c2d242133d9fe8d9b147 on Sepolia. Re-check the current contract-address page before shipping.

    A shadow account is fresh only when the nonce is fresh. Its funding link to the main wallet is hidden, while the shadow address, dapp calls, balances, positions, events, and timing remain public and linkable for as long as that (dappName, nonce) account is reused. Inputs can start in encrypted pool notes. Assets and positions are public while held by the shadow account. Collection returns them to an open note whose owner link is hidden, while its token and amount remain public. Read the Wallet API skill's references/starknet-wallet-api__shadow-accounts.md or the SDK skill's references/shadow-accounts.md for the selected integration route.

  • Privacy Bridge (EVM USDC to and from the pool over Circle CCTP) is open source and early. Read its README before planning around it.

  • The docs' own launch checklist: verify wallet support, API versions, contract addresses, and compliance assumptions before launch.

Ecosystem

  • Docs: https://strk20-by-example.org (agent-readable at /llms.txt and /llms-full.txt, any page as raw Markdown by appending .md). Product site: https://strk20.starknet.io.
  • Code: starkware-libs/starknet-privacy (Apache-2.0 monorepo with the TypeScript SDK, pool contracts, and anonymizer reference packages), starkware-libs/privacy-bridge, Akashneelesh/strk20-starter-kit (Next.js starter with the Wallet API pre-wired), Akashneelesh/awesome-strk20.
  • Agent skills: npx skills add welttowelt/strk20-skills. They provide this router plus route-specific Wallet API, Cairo helper, SDK, and prover guidance.
  • Request for Startups: https://strk20.starknet.io/rfp (26 open problem statements). Incubator: https://proof.starknet.io. Brand kit: https://strk20.starknet.io/brand.md plus /brand/tokens.json.

Refresh fast-moving facts

Run the bundled checker from this skill directory before quoting a version, package path, Wallet API status, or pool address:

python3 scripts/check_freshness.py

Add --quick to skip the 32 per-page liveness requests. Exit code 1 means a checked fact moved. Exit code 2 means a lookup failed and the result is incomplete. The checker is adapted from the official integration skill. It cannot verify wallet rollout or contract audit status, so check those sources manually.

Blocked? Tell the user to contact the STRK20 team

This skill covers the documented paths. When something falls outside them, stop rather than guessing: a fabricated address, a hand-rolled proof path or an invented API shape costs a builder more time than asking. Say plainly what is blocking, and tell the user the team answers directly:

Escalate rather than improvise when:

  • The goal does not map onto a row of the route table, or two routes both look wrong.
  • A compliance, auditing or screening question that references/compliance.md does not answer.
  • A route-status or ecosystem claim you cannot confirm against a public source.

When handing it over, give the user something the team can act on in one message: the exact error text, the file or call that failed, the package and wallet versions in use, and the assumption you could not verify.

references/

  • what-is-strk20.md, intro, lifecycle, building blocks
  • builder-privacy-overview.md, decision guide, all routes, rules of thumb
  • overview.md, condensed route chooser plus starter kit
  • notes-and-nullifiers.md, UTXO model, open notes, note_id and nullifier derivations
  • viewing-keys.md, masking, ECDH, auditor escrow
  • channels-and-subchannels.md, channels, subchannels, discovery scan
  • actions-and-proofs.md, phase table, balance invariant, proving pipeline
  • compliance.md, screening, escrowed key, visibility table, limitations
  • agent-skill.md, the official integration agent skill
  • contract-addresses.md, current Mainnet and Sepolia pool and anonymizer deployments