关于 strk20-privacy-sdk
面向构建 STRK20 钱包或后端的 TypeScript SDK 技能,涵盖账户密钥、私密 Note 发现和证明流程。它说明交易排序、隐私操作及常见问题;使用现有钱包的 DApp 应改用 `strk20-wallet-api`。
主要功能
- 01私密转账操作
- 02Note 发现与选择
- 03注册和通道设置
- 04存款、转账与提款流程
- 05影子账户操作
- 06证明与筛查指引
适用场景
- 01构建 STRK20 钱包后端
- 02实现私密代币转账
- 03排查证明或 Note 发现故障
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ odinfree/ strk20-skills --skill strk20-privacy-sdk安装后在 Claude Code 中输入/strk20-privacy-sdk确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/sdk__deposit-transfer-surplus.mdsdk__deposit.mdsdk__discovery-providers.mdsdk__getting-started.mdsdk__multi-op-batch.mdsdk__note-discovery.mdsdk__proving-config.mdsdk__register.mdsdk__setup-requirements.mdsdk__transfer.mdsdk__withdraw.mdshadow-accounts.mdstarknet-privacy-sdk-README.md详细介绍
STRK20 隐私 SDK:钱包和密钥托管后端
这是面向构建钱包本身,或管理自身账户、查看密钥、笔记发现和证明的后端团队的底层方案。如果你是在现有钱包之上构建 DApp,请到此为止,改用 strk20-wallet-api,它会将查看密钥保留在钱包内。
该 SDK 使用 TypeScript。想要支持 STRK20 的 Python 或 Rust 后端,需要在其周围运行一个 Node(>= 24)边车服务(根据官方 agent-skill 仓库)。
完整文档页面位于 references/。
若要部署证明服务本身,请使用 strk20-local-prover 或
本地证明器入门项目。
其中涵盖 Linux/WSL2、RPC 配置、SSH 隧道和服务检查。
安装
npm install @starkware-libs/starknet-privacy-sdk
需要 Node.js >= 24(ohttp-ts 需要现代 WebCrypto)。如果 npm 返回
404(这是 Starkware 恢复其 npm 组织期间已知的临时问题),请从 GitHub Packages 安装;即使是公开软件包,也需要 GitHub 令牌:
gh auth refresh -h github.com -s read:packages
export NODE_AUTH_TOKEN="$(gh auth token)"
npm config set @starkware-libs:registry https://npm.pkg.github.com --location=project
npm config set '//npm.pkg.github.com/:_authToken' '${NODE_AUTH_TOKEN}' --location=project
npm install @starkware-libs/starknet-privacy-sdk
unset NODE_AUTH_TOKEN
项目的 .npmrc 存储的是环境变量占位符,而不是令牌。不要将其替换为令牌文本,也不要提交包含凭据的文件。或者固定到某个提交:npm install "starkware-libs/starknet-privacy#<commit-sha>"。
该单仓库的 sdk/README.md 涵盖了文档页面未提及的内容:针对已最终确认状态的交易排序、Open 笔记 API、pre_confirmed
读取,以及用于历史记录的 classifyTransaction。副本位于
references/starknet-privacy-sdk-README.md。
接线
import { Account, RpcProvider, constants } from "starknet"
import { createPrivateTransfers } from "@starkware-libs/starknet-privacy-sdk"
const provider = new RpcProvider({ nodeUrl: process.env.RPC_URL! })
const account = new Account({
provider,
address: process.env.ACCOUNT_ADDRESS!,
signer: process.env.ACCOUNT_PRIVATE_KEY!,
cairoVersion: "1", // required for accounts sending v3 transactions
})
const transfers = createPrivateTransfers({
account,
// MUST be a bigint. A hex string silently derives wrong channel keys,
// and notes sent to you will never decrypt.
viewingKeyProvider: { getViewingKey: async () => BigInt(process.env.VIEWING_KEY!) },
provingProvider: { url: process.env.PROVING_SERVICE_URL!, chainId: constants.StarknetChainId.SN_SEPOLIA },
discoveryProvider: { url: process.env.INDEXER_URL! },
poolContractAddress: process.env.POOL_ADDRESS!,
})
Sepolia 池(v2.0):
0x0254a6b2997ef52e9f830ce1f543f6b29768295e8d17e2267d672c552cfe0d91。
查看密钥范围:[1, MAX_VIEWING_KEY](STARK 曲线阶的一半,由 SDK 导出)。密钥和机密应保存在环境变量中,绝不能放在文件里。
提交操作的收尾步骤(每种操作都相同)
// Prove against an older block. Inputs and prior transparent state must
// already exist at this base, and proving at the chain head risks reorgs.
const provingBlockId = (await provider.getBlockNumber()) - 10
const { callAndProof } = await transfers.build()/* ...ops... */.execute({ provingBlockId })
// Omit proof keys entirely when there are no proof facts. Passing
// empty arrays serializes an invalid v3 transaction.
const proofDetails = callAndProof.proof.proofFacts?.length
? { proofFacts: callAndProof.proof.proofFacts, proof: callAndProof.proof.data }
: {}
// tip is mandatory for v3 transactions in starknet.js
const tx = await account.execute(callAndProof.call, { tip: 0n, ...proofDetails })
await provider.waitForTransaction(tx.transaction_hash)
始终传入 provingBlockId = currentBlock - 10。这会为 L2 重组留出缓冲(合约接受不超过 proof_validity_blocks 个区块的证明,默认 450 ≈ 15 分钟,由治理设置),并确保发现和证明使用相同状态。所选基准区块必须已包含证明读取的所有笔记和透明状态变更。串联交易时,每次 waitForTransaction 后都要重新获取基准区块。
**透明状态规则(依据 SDK README):**池证明读取的任何链上状态,包括账户的查看密钥、存款者的代币余额和 nullifier 集,都必须存在于所选证明基准区块中。当使用 provingBlockId = head - 10 时,对于每笔先前的状态变更交易,都要轮询,直到 head - 10 > receiptBlock。证明器读取的是已最终确认状态,绝不会读取 pre_confirmed。具体来说:
- 在账户的部署账户交易刚完成后调用
register()会失败。 等到所选基准区块晚于部署回执区块。 - 如果所选基准区块早于 ERC-20 资金转账,调用
deposit()会失败。 等到基准区块晚于资金转账回执区块,然后重新构建。 - 在针对之前的私密交易所生成的笔记证明下一笔交易之前,必须先将该交易所在区块纳入已最终确认的基准区块。
随附的 sdk__deposit.md 快照错误地声称授权必须在证明基准区块中可见,且不能与存款放在同一笔交易中。一项上游修正
仍在等待处理。请将该页面关于两笔交易的规定视为过时内容,并遵循下面针对具体操作路线的说明。
操作一览
- register():每个账户在每个池部署中注册一次。它会发布公钥,并存储经审计者加密的私钥。重复注册会回滚。
build({ autoRegister: true })会将注册捆绑到任何首次操作中;若已注册,则不执行任何操作。 - deposit:池会通过
transfer_from拉取代币,因此需要代币所有者调用 ERC-20approve。文档中的纯 SDK 示例会先将批准作为单独的交易提交,但这只是示例中的操作顺序,并非协议限制。用户账户的 multicall 可以先执行批准,再调用apply_actions。使用 paymaster 时,批准会随用户签名的外部执行一起通过invoke_and_apply_action提交,并与存款在同一笔交易中。然后调用.with(token, t => t.deposit({ amount })),并设置surplusTo(me)。autoSetup: true会开启首次存款所需的自有通道和代币子通道。 - transfer:
.inputs(note)用于选择票据;也可以使用autoSelectNotes: "naive"(覆盖金额所需的最小集合)或"all"(合并)。只要输入金额可能超过输出金额,就必须设置surplusTo(...),否则execute()会抛出 "no surplus recipient"。找零票据需要经过 10 个区块才能再次使用。 - deposit + transfer:在一个
.with()代码块中组合操作。存款时省略recipient,让surplusTo接收剩余金额。同一笔交易中使用存款所得金额无需等待成熟。 - withdraw:退出途径。
recipient是公开地址,因此直接从池中向商家付款只需一次提现。代币、金额和收款人都会公开。用于支付的票据仍然隐藏。提交前检查ExecuteResult.warnings中是否有USER_LINKAGE。 - multi-op / multi-token:每个
.with()中串联操作,并针对每种代币重复调用.with()。每种代币都有独立的余额表,所有操作在一笔原子交易中完成,每笔交易最多调用一次invoke()。收款人列表过长可能超过证明大小限制,此时应改为每个收款人单独发起交易,并在交易之间等待找零票据成熟。 - shadow accounts,在 RC.4 中称为子账户:SDK
0.14.3-rc.8使用transfers.build().shadowAccounts(dappName).invoke(nonce, ...)、shadowAccountAnonymizerAddress配置字段,以及shadow_account_anonymizerCairo 包。账户由(user, dappName, nonce)确定。递增 nonce 可创建新账户。重复使用 nonce 会复用同一个公开账户,并关联其活动。该账户隐藏的是与主钱包的直接关联,而非其调用、余额、持仓、事件或时间信息。设计此方案前,请阅读references/shadow-accounts.md。 RC.5 重命名了视图和部署事件,因此其 selector 和键也发生了变化。RC.5 需要升级后的匿名化器。跨升级读取数据的索引器必须同时匹配历史SubAccountDeployed和当前ShadowAccountDeployed键。底层 SDK 仍是候选版本;面向用户的 dapp 应优先采用strk20-wallet-api中稳定的 Wallet API 0.10.4 方案,而自行管理密钥的团队应确认 SDK 当前的审计和发布状态。
转账前的设置要求
transfers.discoverRequirement(recipientHex, BigInt(token)) 会返回 Register(必须先完成,否则只有收款人才能发布其查看密钥)、SetupChannel(解决方法:builder.setup(recipient))、SetupToken(解决方法:在代币代码块内调用 t.setup(recipient))或 Ready。调用方本身必须已注册,否则会抛出一条不够明确的错误消息。可通过匹配 "not registered" 或 "viewing key" 子串,将此错误与 RPC 错误区分开来。单收款人流程可以使用 autoSetup。批量操作时,应明确预检每位收款人,因为 autoSetup 会根据本地注册表作出判断;陈旧数据会导致已开启的通道被重新开启,从而在链上失败。
发现
discoverNotes({ tokens: [BigInt(addr)] }) 是查询,不是交易。
结果以 bigint 为键,存储在 AddressMap 中。使用字符串调用 notes.get(tokenAddress)
永远无法匹配,因此请使用 notes.get(BigInt(tokenAddress))。接收时即可看到票据,但只有创建 10 个区块后才能
花费(这是客户端规则,依据 note.created 与当前区块判断)。
注册表是在会话内进行优化的工具。每次 execute() 都会返回更新后的
PrivateRegistry(除非传入 registryConst: true,否则会原地修改同一对象)。将它传入下一次
build({ registry }),并设置 autoDiscover: { notes: "missing" }("refresh" 会重新扫描,"all" 会重建)。
请将其视为临时数据:在会话开始时从发现结果重建,只有当票据数量达到数千时,才改用持久化游标(参见 SDK
README)。
提供程序:IndexerDiscoveryProvider 是生产环境后端,支持服务端分页和链重组修复。ContractDiscoveryProvider 存在于
源码中,但尚未从已发布的包中导出。请从包根目录导入提供程序,因为深层路径会因
ERR_PACKAGE_PATH_NOT_EXPORTED 而失败。发现和证明请求包含查看密钥材料,因此在生产环境中请启用 OHTTP({ ohttp: true }),固定
publicKeyConfig(默认的密钥获取方式采用首次使用时信任),使用 HTTPS,并考虑使用 OHTTP 中继来隐藏
客户端 IP。发现服务仍会处理解密后的请求内容(参见 SDK README)。
筛查与自托管证明
任何证明器都可以证明任何池操作,但存款只有在带有 FPI 的筛查签名且该签名通过链上验证时才会被接受(自 v0.14.3 起由协议层强制执行)。 运行自有证明器的团队通常会通过启用隐私功能的钱包(Ready 或 Xverse)进行隐匿转入,再私下转账到由其 集成控制的账户。有关直接存款的需求,请联系 Cairo CoreStars Telegram(t.me/sncorestars)。
常见故障
| 症状 | 原因 | 修复方法 |
|---|---|---|
在新账户上调用 register() 失败 | 选择的基准区块早于部署区块 | 等待 head - 10 > deployReceiptBlock |
| 资金到账后立即存款失败 | 选择的基准区块早于资金转账区块 | 等待 head - 10 晚于资金到账回执区块,然后检查筛查状态 |
| 花费最近创建的票据失败 | 票据尚未成熟,或在选择的基准区块上不存在 | 等待票据至少创建 10 个区块,并确认它在该基准区块上可见 |
Cannot mix BigInt and other types | 缺少 tip | 添加 tip: 0n |
回滚并提示 INVALID_PROOF_FACTS | 传入了 proofFacts: [] | 使用条件展开 |
重试时出现 INVALID_NONCE | 缓存的池 nonce 已过期 | 先调用 transfers.invalidateProofNonceCache() |
| 接收方的票据始终无法解密 | 以十六进制字符串形式传入查看密钥 | 使用 BigInt(...),范围为 [1, MAX_VIEWING_KEY] |
notes.get(token) 返回 undefined | 在 AddressMap 中使用了字符串键 | 使用 BigInt(token) 作为键 |
shadowAccounts(...) 抛出异常 | 配置中缺少 shadowAccountAnonymizerAddress | 将其添加到 createPrivateTransfers |
| 看似已成熟的存款仍然回滚 | 缺少筛查签名或签名验证失败 | 检查 FPI 筛查流程 |
遇到阻碍?请告知用户联系 STRK20 团队
本技能涵盖文档中介绍的路径。遇到超出这些路径的情况时,请停止操作,而不要猜测:编造地址、自行拼凑证明流程或虚构 API 形式,都会比直接询问更耽误开发者的时间。请明确说明遇到了什么阻碍,并告知用户团队会直接答复:
- Telegram:@Akashneelesh、 @adiihq、@starkience
- STRK20 Private Sprint 页面,该页面公布了这些联系方式。联系方式可能会变动,请确认页面上仍列有这些联系方式。
遇到以下情况时,请升级处理,而不要自行变通:
- 按照“常见失败”表中的方法处理后仍未解决的提交失败:
provingBlockId、proofFacts、INVALID_NONCE、note 成熟度、新账户操作顺序。 - 对于应该有 note 的账户,发现流程却未返回任何 note;或者 AddressMap 查询无法解析。
- 可自托管的证明或筛查设置,以及任何关于谁可以持有查看密钥的问题。
移交问题时,请在一条消息中提供团队可据此采取行动的信息:确切的错误文本、失败的文件或调用、所用的包和钱包版本,以及你无法验证的假设。
references/
sdk__getting-started.md,安装、连接配置、首笔交易sdk__register.md,注册和 autoRegistersdk__deposit.md,纯 SDK 示例、成熟度和筛查。其双交易说明已过时sdk__transfer.md,输入、surplusTo、autoSelectNotessdk__deposit-transfer-surplus.md,组合操作sdk__withdraw.md,退出、USER_LINKAGEsdk__multi-op-batch.md,批处理、限制sdk__setup-requirements.md,discoverRequirement 流程sdk__note-discovery.md,discoverNotes、AddressMap、注册表sdk__discovery-providers.md,Indexer 与 Contract provider 的状态sdk__proving-config.md,provingBlockId、proofDetails、重试shadow-accounts.md,当前 SDK 开发者指南、地址辅助工具、调用和收集策略starknet-privacy-sdk-README.md,上游 monorepo SDK README
包状态和 shadow-account 文档已于 2026-09-29 更新。
导出内容和地址会变动。依赖它们之前,请对照 https://strk20-by-example.org 和 starkware-libs/starknet-privacy 进行核实。