关于 opnet-development
opnet-development 是用于构建 OPNet 项目的开发技能,涵盖智能合约和前端。它引导 Agent 按任务查阅文档,并规定实现、安全、测试和部署流程。
主要功能
- 01按任务查阅文档的流程
- 02智能合约与前端开发指南
- 03OP_WALLET 集成要求
- 04合约模拟与安全检查
- 05Lint、类型检查和构建要求
适用场景
- 01规划 OPNet 智能合约项目
- 02构建连接合约的前端
- 03检查交易安全与实现模式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ btc-vision/ opnet-skills --skill opnet-development安装后在 Claude Code 中输入/opnet-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
docs/asconfig.jsonclients-bip32-QUANTUM.mdclients-bitcoin-address.mdclients-bip32-README.mdclients-ecpair-README.mdclients-bitcoin-payments.mdclients-bitcoin-transaction.mdclients-bitcoin-psbt.mdclients-bitcoin-README.mdclients-bitcoin-script.mdclients-walletconnect-README.mdcontracts-as-bignum-README.mdcontracts-btc-runtime-advanced-cross-contract-calls.mdcontracts-btc-runtime-README.mdcontracts-btc-runtime-advanced-contract-upgrades.mdcontracts-btc-runtime-advanced-quantum-resistance.mdcontracts-btc-runtime-advanced-signature-verification.mdcontracts-btc-runtime-api-reference-blockchain.mdclients-walletconnect-wallet-integration.mdcontracts-btc-runtime-advanced-bitcoin-scripts.mdcontracts-btc-runtime-advanced-plugins.mdcontracts-btc-runtime-api-reference-events.mdcontracts-btc-runtime-api-reference-op721.mdcontracts-btc-runtime-api-reference-op20.mdcontracts-btc-runtime-contracts-op-net-base.mdcontracts-btc-runtime-api-reference-storage.mdcontracts-btc-runtime-api-reference-safe-math.mdcontracts-btc-runtime-contracts-op20-token.mdcontracts-btc-runtime-contracts-reentrancy-guard.mdcontracts-btc-runtime-contracts-op721-nft.md详细介绍
OPNet 开发技能
用于在 OPNet 上进行开发的综合技能。OPNet 是用于无信任智能合约的 Bitcoin L1 共识层。
步骤 1:先了解用户的需求
在阅读任何文档或指南之前,必须先了解用户的请求。
- 仔细阅读用户的消息
- 确定用户需要哪种类型的项目(合约、前端、后端、插件、审计或问题)
- 如果请求含糊不清,请提出澄清问题
- 只有在此之后,才开始阅读该项目类型对应的文档
不要不加判断地开始阅读所有文档。先了解任务,再只阅读相关文档。
步骤 2:编写任何代码之前必须阅读
如果不阅读必需的文档就编写代码,你会创建出有缺陷、可被利用的代码。
AI Agent 的关键规则
绝对规则——禁止使用原生 JavaScript
绝不编写原生 JavaScript。始终使用:
- 前端:TypeScript + Vite + npm 包
- 后端:TypeScript + Node.js + npm 包
- 没有例外,连“快速示例”也不行。
不要:
- 只阅读几个文件就说“我已经掌握了足够的背景信息”
- 阅读项目类型列表中未列出的随机技能或指南
- 只浏览 2-3 个文件就开始编写代码
- 假设自己了解其他框架中的 OPNet 模式
- 因为某些文件“看起来和已读文件相似”就跳过它们
- 未运行
npm run lint和npm run typecheck就创建 zip 文件或交付成果 - 只说“运行 npm run lint 进行验证”,而不实际运行
必须:
- 阅读下方为项目类型列出的每一个文件
- 按顺序阅读——后面的文件依赖前面的文件
- 阅读完整文件,而不只是前几个章节
- 只阅读本技能中列出的文件——不要随意探索其他内容
- 在编写任何代码之前,确认已阅读完所有文件
- 实际运行
npm install、npm run lint、npm run typecheck、npm run build - 创建任何交付成果(zip、PR 等)之前,修复所有错误
强制工作流规则
1. 先构建合约,再构建前端
当用户要求构建一个与智能合约交互的网站/dApp 时,必须先构建 .wasm 合约,再构建前端。
工作流顺序:
- 构建并编译 AssemblyScript 智能合约 → 确认
.wasm输出存在 - 运行合约测试,确保正确性
- 然后构建与该合约交互的前端
绝不要构建引用尚不存在的合约的前端。
2. 钱包集成:始终使用 @btc-vision/opwallet
所有 OPNet 网站都必须使用 @btc-vision/opwallet 集成 OP_WALLET。
OP_WALLET 是唯一官方钱包,能够全面支持 OPNet 功能(MLDSA 签名、抗量子密钥和完整的 OPNet 集成)。每个前端 dApp 都必须集成它。
3. VITE 为强制要求——没有例外
**所有前端项目都必须使用 Vite 作为构建工具。**不得使用 webpack、parcel、独立 rollup 或独立 esbuild。只能使用 Vite。
4. IPFS 部署:仅使用 OPNET-CLI
发布到 IPFS 时:
- 使用 Vite 构建:
npm run build(生成dist/文件夹) - 输出内容为静态 HTML + JS + CSS 文件
- 使用 opnet-cli IPFS 上传脚本上传:
- 仅上传到 IPFS(不使用域名):
bash /root/openclaw/skills/opnet-cli/scripts/ipfs-upload.sh ./dist - 上传并发布到 .btc 域名:
bash /root/openclaw/skills/opnet-cli/scripts/ipfs-upload.sh ./dist mysite - 试运行(上传但不在链上发布):
bash /root/openclaw/skills/opnet-cli/scripts/ipfs-upload.sh ./dist mysite --dry-run
- 仅上传到 IPFS(不使用域名):
- 网站必须是完整的静态网站——不得使用服务端渲染或 API 路由
- 所有合约交互都通过 OPNet provider 在客户端进行
vite build 后的 dist/ 文件夹包含部署到 IPFS 所需的全部内容。
上传脚本会返回包含 CID 和网关 URL 的 JSON:
{
"cid": "QmXyz...",
"files": 12,
"totalSize": 245000,
"gateway": "https://ipfs.opnet.org/ipfs/QmXyz..."
}
禁止使用以下 IPFS 方法——绝不可使用:
- ❌ 本地 IPFS 守护进程(Kubo)——不得运行
ipfs add、ipfs pin或任何本地 Kubo 命令 - ❌
ipfs-website技能——此技能已弃用并删除 - ❌ 直接向 IPFS API 发送 HTTP 请求——不得使用
curl、fetch或向 IPFS 端点发送任何原始 HTTP 请求 - ❌ 逐个上传文件——该脚本会通过适当的分块处理目录上传
- ❌ 除上述 opnet-cli 脚本外的任何 IPFS 上传方法
所有 IPFS 上传一律使用 bash /root/openclaw/skills/opnet-cli/scripts/ipfs-upload.sh。没有例外。
常见的 Agent 错误——不要重蹈覆辙
以下是在多个 AI Agent 中观察到的真实错误。如果你犯了其中任何一个错误,代码就会出问题。
| 错误 | 错误原因 | 正确做法 |
|---|---|---|
| 使用 Keccak256 选择器(EVM 风格) | OPNet 使用的是 SHA256,而不是 Keccak256。这是 Bitcoin,不是 Ethereum。 | 所有哈希和方法选择器都使用 SHA256 |
对 OP-20 代币调用 approve() | OP-20 没有 approve()。它使用 increaseAllowance() / decreaseAllowance() 来防止众所周知的 approve 竞态条件。 | 使用 increaseAllowance(spender, amount) 和 decreaseAllowance(spender, amount) |
将 bc1p... 地址传给 Address.fromString() | Address.fromString() 接受两个十六进制公钥参数,而不是 Bech32 地址字符串 | Address.fromString(hashedMLDSAKey, tweakedPublicKey)——两者都必须是十六进制字符串 |
使用 bitcoinjs-lib | OPNet 有自己的 Bitcoin 库,其中包含关键补丁,PSBT 速度快 709 倍 | 使用 @btc-vision/bitcoin——绝不要使用 bitcoinjs-lib |
| 使用工厂部署子合约的模式 | OPNet 合约无法部署其他合约。这里没有 CREATE 或 CREATE2 操作码。 | 分别部署每个合约,然后通过存储的地址将它们关联起来 |
在 sendTransaction() 之前跳过模拟 | Bitcoin 转账不可撤销。如果合约回滚,你的 BTC 就没了。 | 务必先模拟:调用 contract.method() 进行模拟,确认成功后再调用 result.sendTransaction(params) |
| 后端使用 Express/Fastify/Koa | 这些框架是禁止使用的,而且速度明显更慢。 | 只能使用 @btc-vision/hyper-express 和 @btc-vision/uwebsocket.js |
设置后未运行 npm-check-updates | 软件包版本不断变化。版本过时就会导致构建失败。 | 务必运行 npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 ...(参见 Install Commands 部分) |
未提供弃用警告就让用户使用 verifyECDSASignature 或 verifySchnorrSignature | ECDSA 和 Schnorr 均已弃用。ECDSA 受 UNSAFE_QUANTUM_SIGNATURES_ALLOWED 限制,当共识切换该标志时将无法使用。Schnorr 也已弃用,但仍可通过安全的 verifySignature 路径使用。直接把已弃用的方法交给用户而不发出警告,会让他们面临合约故障。 | 务必先警告:ECDSA 和 Schnorr 均已弃用。明确指出 Blockchain.verifySignature(address, signature, hash) 是唯一正确的做法——它能感知共识状态,并自动选择正确的算法(目前为 Schnorr,强制启用后为 ML-DSA)。绝不要直接引导用户使用 verifyECDSASignature、verifyBitcoinECDSASignature 或 verifySchnorrSignature。请阅读 guidelines/ethereum-migration-guidelines.md 第 3 节。 |
使用 MessageSigner.signMessage() 或 signMLDSAMessage(),而不是 Auto 方法 | 非 Auto 方法因环境而异。signMessage() 仅适用于本地密钥对(后端),signMLDSAMessage() 仅适用于 OP_WALLET(浏览器)。用错方法会导致运行时崩溃。 | 务必使用 Auto 方法:MessageSigner.signMessageAuto()、tweakAndSignMessageAuto()、signMLDSAMessageAuto()。这些方法会自动检测你是在浏览器(OP_WALLET)还是后端(本地密钥对),并调用正确的底层方法。除非有明确理由且了解当前环境,否则绝不要使用非 Auto 变体。 |
如果发现自己犯了上述任何错误,请立即停下来并修正。
这为何重要
OPNet 开发涉及:
- 经常变化的 Beta 软件包版本——猜测版本会导致构建失败
- 严格的 TypeScript 规则——违反规则会造成安全漏洞
- 平台特定模式——模式错误会导致漏洞、Gas 攻击和数据丢失
指南只是摘要。docs/ 文件夹才包含实际的实现模式。
只阅读 3 到 4 个文件远远不够。你必须阅读适用于项目类型的所有文件。
按项目类型规定的必读顺序
所有项目(每次都先阅读)
| 顺序 | 文件 | 原因 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 规则——类型系统规则、禁止的结构、必需的模式 |
| 2 | guidelines/setup-guidelines.md | 软件包版本、tsconfig、ESLint 配置 |
智能合约(AssemblyScript)
编写合约代码前,请阅读以下所有内容:
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 类型规则(同样适用于 AS) |
| 2 | guidelines/setup-guidelines.md | 软件包版本、asconfig.json |
| 3 | guidelines/contracts-guidelines.md | 合约模式概要 |
| 4 | docs/contracts-btc-runtime-README.md | 运行时概览 |
| 5 | docs/contracts-btc-runtime-getting-started-installation.md | 设置 |
| 6 | docs/contracts-btc-runtime-getting-started-first-contract.md | 入口点、工厂模式 |
| 7 | docs/contracts-btc-runtime-getting-started-project-structure.md | 目录结构 |
| 8 | docs/contracts-btc-runtime-core-concepts-storage-system.md | 存储类型、指针 |
| 9 | docs/contracts-btc-runtime-core-concepts-pointers.md | 指针分配 |
| 10 | docs/contracts-btc-runtime-api-reference-safe-math.md | SafeMath(u256 必须使用) |
| 11 | docs/contracts-btc-runtime-gas-optimization.md | Gas 模式、禁止的循环 |
| 12 | docs/contracts-btc-runtime-core-concepts-security.md | 安全检查清单 |
对于 OP20 代币,还需阅读:
docs/contracts-btc-runtime-api-reference-op20.mddocs/contracts-btc-runtime-contracts-op20-token.md
对于 OP721 NFT,还需阅读:
docs/contracts-btc-runtime-api-reference-op721.mddocs/contracts-btc-runtime-contracts-op721-nft.md
前端(React/Vite)
编写前端代码前,请阅读以下所有内容:
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 类型规则、禁止的结构 |
| 2 | guidelines/setup-guidelines.md | 软件包版本、Vite 配置 |
| 3 | guidelines/frontend-guidelines.md | 前端模式概要 |
| 4 | docs/core-opnet-README.md | 客户端库概览 |
| 5 | docs/core-opnet-getting-started-installation.md | 安装 |
| 6 | docs/core-opnet-getting-started-quick-start.md | 快速入门 |
| 7 | docs/core-opnet-providers-json-rpc-provider.md | Provider 设置 |
| 8 | docs/core-opnet-providers-internal-caching.md | 缓存(必须阅读) |
| 9 | docs/core-opnet-contracts-instantiating-contracts.md | 合约实例 |
| 10 | docs/core-opnet-contracts-simulating-calls.md | 读取操作 |
| 11 | docs/core-opnet-contracts-sending-transactions.md | 写入操作 |
| 12 | docs/core-opnet-contracts-transaction-configuration.md | 交易选项 |
| 13 | docs/core-transaction-transaction-factory-interfaces.md | 高级 TX 参数(费用、备注、锚点) |
| 14 | docs/clients-walletconnect-README.md | 钱包连接 |
| 15 | docs/frontend-motoswap-ui-README.md | 标准——参考实现 |
后端/API(Node.js)
编写后端代码前,请阅读以下所有文件:
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 类型规则、禁止使用的构造 |
| 2 | guidelines/setup-guidelines.md | 软件包版本 |
| 3 | guidelines/backend-guidelines.md | 后端模式概要 |
| 4 | docs/core-opnet-backend-api.md | 必需框架——hyper-express、uWebSockets.js |
| 5 | docs/core-opnet-providers-json-rpc-provider.md | Provider 设置 |
| 6 | docs/core-opnet-providers-threaded-http.md | 线程处理(强制要求) |
| 7 | docs/core-opnet-providers-internal-caching.md | 缓存(强制要求) |
| 8 | docs/core-opnet-contracts-instantiating-contracts.md | 合约实例 |
| 9 | docs/core-opnet-contracts-sending-transactions.md | 发送交易 |
| 10 | docs/core-transaction-transaction-factory-interfaces.md | 高级交易参数(费用、备注、锚点) |
禁止使用的框架: Express、Fastify、Koa、Hapi、Socket.io——仅使用 hyper-express 和 uWebSockets.js。
插件(OPNet Node)
编写插件代码前,请阅读以下所有文件:
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 类型规则、禁止使用的构造 |
| 2 | guidelines/setup-guidelines.md | 软件包版本 |
| 3 | guidelines/plugin-guidelines.md | 插件模式概要 |
| 4 | docs/core-OIP-OIP-0003.md | 插件规范——完整规范 |
| 5 | docs/plugins-plugin-sdk-README.md | SDK 参考 |
| 6 | docs/plugins-opnet-node-README.md | 节点集成 |
**重要:**必须实现 onReorg() 来处理链重组,否则数据将不一致。
单元测试(TypeScript)
编写测试代码前,请阅读以下所有文件:
| 顺序 | 文件 | 内容 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 类型规则 |
| 2 | guidelines/setup-guidelines.md | 软件包版本 |
| 3 | guidelines/unit-testing-guidelines.md | 测试模式概要 |
| 4 | docs/testing-unit-test-framework-README.md | 框架概述 |
| 5 | docs/testing-opnet-unit-test-README.md | 测试设置 |
| 6 | docs/testing-opnet-unit-test-docs-Blockchain.md | 区块链模拟 |
| 7 | docs/testing-opnet-unit-test-docs-ContractRuntime.md | 合约运行时 |
**重要:**单元测试使用 TypeScript(不是 AssemblyScript),并且有单独的 package.json。
对于一般性问题(架构、概念、最佳实践)
阅读: guidelines/generic-questions-guidelines.md
适用于以下问题:
- “OPNet 如何运作?”
- “OPNet 能抵御 51% 攻击吗?”
- “OPNet 上的空投如何运作?”
- “OPNet 与 Runes/Ordinals 有何区别?”
- “为什么合约不能持有 BTC?”
- “什么是交易钉死?”
- “OPNet 与 Ethereum 有何不同?”
- “如何将我的 Solidity 合约迁移到 OPNet?”
- “OPNet 上与 ecrecover 等效的是什么?”
请参阅完整指南,了解完整的主题映射,以及针对每种问题类型应阅读哪些文档。
**对于 Ethereum 迁移问题:**阅读 guidelines/ethereum-migration-guidelines.md——其中涵盖从 Ethereum 到 OPNet 的概念映射(地址、签名、代币、钱包、交易、DEX 模式)。
关键——ecrecover / ECDSA / Schnorr / 签名验证问题:
当用户询问 ecrecover、ECDSA、Schnorr 或签名验证时,你必须:
- 首先警告:ECDSA 和 Schnorr 在 OPNet 上都已弃用。当共识禁用
UNSAFE_QUANTUM_SIGNATURES_ALLOWED时,ECDSA 将失效。Schnorr 也已弃用,但仍可通过安全的verifySignature路径使用。 - 展示唯一正确的方法:
Blockchain.verifySignature(address, signature, hash)——此方法能够感知共识状态、自动选择正确的算法(目前为 Schnorr,强制启用后为 ML-DSA),并且是唯一面向未来的方法。 - 仅展示客户端自动签名方法:
MessageSigner.signMessageAuto()/tweakAndSignMessageAuto()/signMLDSAMessageAuto()。这些方法会自动检测环境是浏览器(OP_WALLET)还是后端(本地密钥对),并调用正确的方法。绝不要使用非 Auto 变体(signMessage()、signMLDSAMessage()、tweakAndSignMessage())——这些方法依赖特定环境,在错误的环境中会导致崩溃。 - 绝不要直接向用户推荐
verifyECDSASignature、verifyBitcoinECDSASignature或verifySchnorrSignature。这些都是已弃用的内部方法。 - 回答前阅读
guidelines/ethereum-migration-guidelines.md第 3 节。
不要只给用户已弃用的 API 或依赖特定环境的签名方法。他们的代码一定会出问题。合约端应使用 verifySignature,客户端应使用 *Auto() 方法。
重要:对于概念性问题,回答前请先阅读相关文档/章节。不要猜测或擅自假设 OPNet 的工作方式。
对于安全审计
停止——进行任何审计前必须阅读
如果不阅读必需的文档就审计代码,你将错过关键漏洞。
OPNet 审计需要了解:
- AssemblyScript 运行时内部机制——序列化、存储、缓存一致性
- Bitcoin 特有的攻击向量——交易钉死、可塑性、链重组
- 关键漏洞模式——标准审计会完全漏掉这些模式
审计任何代码前,你必须完整阅读 guidelines/audit-guidelines.md。
跳过审计指南就会漏掉漏洞。没有捷径。
免责声明(每份报告都必须包含)
IMPORTANT DISCLAIMER: This audit is AI-assisted and may contain errors,
false positives, or miss critical vulnerabilities. This is NOT a substitute
for a professional security audit by experienced human auditors.
Do NOT deploy to production based solely on this review.
Always engage professional auditors for contracts handling real value.
审计必读顺序
开始任何审计前,必须按顺序阅读以下所有内容:
| 顺序 | 文件 | 必读原因 |
|---|---|---|
| 1 | docs/core-typescript-law-CompleteLaw.md | 定义安全代码的类型规则 |
| 2 | guidelines/audit-guidelines.md | 完整审计指南——漏洞模式、检查清单、检测方法 |
然后根据代码类型阅读:
| 代码类型 | 其他必读内容 |
|---|---|
| 智能合约 | docs/contracts-btc-runtime-core-concepts-security.md、docs/contracts-btc-runtime-gas-optimization.md、docs/contracts-btc-runtime-api-reference-safe-math.md、docs/contracts-btc-runtime-types-bytes-writer-reader.md |
| DEX/Swap 代码 | 此 SKILL.md 中的 CSV、NativeSwap、Slashing 章节 |
| 前端 | guidelines/frontend-guidelines.md |
| 后端 | guidelines/backend-guidelines.md |
| 插件 | guidelines/plugin-guidelines.md——重组处理章节 |
审计验证检查点
在撰写任何审计发现之前,请确认:
- 我已完整阅读
guidelines/audit-guidelines.md - 我已完整阅读
docs/core-typescript-law-CompleteLaw.md - 我已阅读此代码类型所需的所有其他文档
- 我理解“关键运行时漏洞模式”章节
- 我理解序列化/反序列化一致性要求
- 我理解存储系统的缓存一致性问题
- 我理解比特币特定的攻击向量(CSV、交易钉扎、重组)
如果无法勾选所有选项,请返回并阅读相关文档。
智能合约审计检查清单(摘要)
完整检查清单及检测模式请参阅 guidelines/audit-guidelines.md。
| 类别 | 检查内容 |
|---|---|
| 算术运算 | 所有 u256 运算均使用 SafeMath(不得直接使用 +、-、*、/) |
| 溢出/下溢 | SafeMath.add()、SafeMath.sub()、SafeMath.mul()、SafeMath.div() |
| 访问控制 | 检查 onlyOwner,对敏感方法进行授权 |
| 重入 | 外部调用前更改状态(检查-效果-交互模式) |
| Gas/循环 | 不使用 while 循环,所有 for 循环均有界,不进行无界迭代 |
| 存储 | 不遍历所有 map 键;总量使用已存储的聚合值 |
| 输入验证 | 验证所有用户输入并检查边界 |
| 整数处理 | 大数值通过 fromString() 创建 u256,不使用算术运算 |
| 序列化 | 写入/读取类型保持一致,sizeof() 映射正确 |
| 缓存一致性 | 比较前先从存储加载数据 |
| 删除标记 | 存储删除使用 32 字节 EMPTY_BUFFER |
| 边界检查 | 最大索引检查使用 >= 而非 > |
TypeScript/前端/后端审计检查清单(摘要)
| 类别 | 检查内容 |
|---|---|
| 类型安全 | 不使用 any、非空断言(! 运算符)或 @ts-ignore |
| 空值安全 | 显式检查 null,并正确使用可选链 |
| BigInt | 所有聪金额/代币金额均使用 bigint,而非 number |
| 不使用浮点数 | 财务计算不使用浮点数 |
| 缓存 | 缓存 Provider/合约实例,不要重复创建 |
| 输入验证 | 验证地址、金额并检查边界 |
| 错误处理 | 捕获并处理错误,不允许静默失败 |
| Provider 类型 | 使用 JSONRpcProvider(WebSocketProvider 仍处于实验阶段) |
Bitcoin 专项审计清单(摘要)
| 类别 | 检查项 |
|---|---|
| CSV 时间锁 | 所有交换接收地址都使用 CSV(防止 pinning) |
| UTXO 处理 | 正确选择 UTXO,避免产生粉尘输出 |
| 交易可塑性 | 确认前不得假定签名不可变 |
| 手续费狙击 | 正确处理 locktime |
| 重组处理 | 删除或回滚重组区块的数据 |
| P2WPKH | 仅允许压缩公钥(33 字节),拒绝未压缩公钥 |
| 见证脚本大小 | 验证是否符合 3,600 字节的标准限制 |
DEX/交换审计清单(摘要)
| 类别 | 检查项 |
|---|---|
| 预留系统 | 在预留时锁定价格,而不是在执行时锁定 |
| 滑点保护 | 强制执行最大滑点限制 |
| 抢先交易 | 预留系统可防止抢先交易 |
| 队列操纵 | 对滥用队列的行为施加罚没惩罚 |
| 部分成交 | 对多提供方付款进行原子协调 |
关键运行时漏洞模式(摘要)
完整模式及代码示例请参阅 guidelines/audit-guidelines.md。
| ID | 漏洞 | 影响 |
|---|---|---|
| C-07 | 序列化不匹配(写入 u16,读取 u32) | 数据损坏 |
| C-08 | 将 sizeof() 字节误当作位 | 数据截断 |
| C-09 | 有符号/无符号类型混淆(i8 → u8) | 符号丢失 |
| C-10 | 缓存一致性(setter 与未加载的缓存进行比较) | 状态静默损坏 |
| C-11 | 删除标记大小错误 | has() 返回错误结果 |
| C-12 | 通用整数截断(只读取第一个字节) | 数据丢失 |
| H-06 | 索引越界 | 内存损坏 |
| H-07 | 差一错误(使用 > 而非 >=) | 缓冲区溢出 |
| H-08 | 指针冲突(截断但未哈希) | 存储内容被覆盖 |
| M-05 | 泰勒级数发散 | 数学计算错误 |
审计报告末尾必须始终附上免责声明。绝不能声称审计已完成或能保证安全。
验证检查点
编写任何代码之前,你必须能够回答:
-
我正在构建什么类型的项目?(合约 / 前端 / 后端 / 插件 / 测试)
-
我是否阅读了该项目类型要求的每个文件?
- 列出你阅读的每个文件
- 如果无法全部列出,返回并阅读
-
准确的包版本是什么?(参见
guidelines/setup-guidelines.md)- 如果不清楚,返回并阅读
-
哪些构造是禁止的?
any、非空断言运算符、合约中的while循环、表示 satoshi 的number等。- 如果无法列出,返回并阅读
-
要求使用哪些模式?
- 缓存、SafeMath、多线程等。
- 如果无法列出,返回并阅读
停止——如果出现以下情况,请勿继续:
- ❌ 阅读的 docs 文件少于 5 个
- ❌ 跳过了必读列表中的任何文件
- ❌ 阅读了项目类型要求列表之外的文件
- ❌ 无法列出准确的包版本
- ❌ 阅读 2–3 个文件后就准备说“我掌握的信息已经足够”
返回并阅读所有必读文件。不得走捷径。
代码验证顺序(必须遵守)
必须实际运行以下命令,而不只是提及它们。
第 1 步:安装依赖
非合约项目:
npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 @btc-vision/bitcoin@rc @btc-vision/transaction@rc opnet@rc @btc-vision/bip32 @btc-vision/ecpair --prefer-online
合约项目:
npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 @btc-vision/[email protected] @btc-vision/assemblyscript@^0.29.2 @btc-vision/[email protected] @btc-vision/btc-runtime@rc --prefer-online
- 在运行任何其他命令之前,先运行适用的命令
- 如果创建或修改了 package.json,则必须执行此步骤
- 不得跳过此步骤——不得只运行
npm install
第 2 步:运行 Prettier
npm run format
- 统一格式化所有代码
- 在 lint 之前运行,以避免格式冲突
- 不得跳过此步骤
第 3 步:运行 ESLint
npm run lint
- 必须通过,且不得有任何错误
- 如果存在错误,继续之前先修复
- 不得说“你应该运行 lint”——必须实际运行
第 4 步:运行 TypeScript 检查
npm run typecheck
- 如果没有相应脚本,则运行
npx tsc --noEmit - 必须通过,且不得有任何错误
- 继续之前修复所有类型错误
步骤 5:构建
npm run build
- 仅在格式检查、Lint 检查和类型检查通过后运行
- 对于合约:确认已生成 .wasm 文件
步骤 6:运行测试
npm run test
- 也可以使用
npx ts-node --esm tests/*.test.ts运行单元测试 - 所有测试都必须通过
- 如果测试失败,请修复代码
停止——必须实际运行这些命令
- ❌ 不要只写“运行 npm run lint 进行验证”
- ❌ 不要因为“应该能正常运行”而跳过步骤
- ❌ 未运行所有步骤前,不要认为代码已完成
- ✅ 实际执行每条命令
- ✅ 确认每条命令都通过
- ✅ 在继续下一步前修复所有错误
如果你写了代码却没有运行这些命令,你的任务就没有完成。
任务完成检查点
在认为任何任务完成之前,请先问自己以下问题:
-
我是否完成了任务要求的所有步骤?
- 我是否处理了用户提出的所有要求?
- 我是否遗漏了任何要求?
-
我是否实际运行了验证命令?
- 我是否运行了
npm install?(如果存在或更改了 package.json,则必须运行) - 我是否运行了
npm run format?(必须运行——Prettier 格式化) - 我是否运行了
npm run lint?(必须通过,且错误数为零) - 我是否运行了
npm run typecheck或npx tsc --noEmit?(必须运行) - 我是否运行了
npm run build?(对于可部署代码,必须运行) - 我是否运行了
npm run test?(如果存在测试,则必须运行) - 如果任何命令失败,我是否修复了错误?
- “我应该运行 lint”不等于实际运行了它
- 我是否运行了
-
我是否检查并重新审视了自己的工作?
- 重新阅读我的成果——是否正确?
- 我是否做出了可能有误的假设?
- 是否有我未考虑到的边界情况?
- 我的代码或回答是否可能引入错误或漏洞?
- 对于智能合约:SafeMath、边界检查、访问控制、序列化
- 对于 TypeScript:类型安全、空值处理、缓存模式
- 对于回答:信息是否准确?我是否进行了核实?
-
我是否编写了完整且可用于生产环境的代码?
- 绝不要添加“在生产环境中,你应该……”之类的注释
- 绝不要添加存根、占位符或 TODO 注释
- 绝不要使用“在此添加你的逻辑”之类的内容编写不完整的实现
- 如果无法完整实现某项内容,请说明原因并询问用户
- 所有代码都必须可以立即使用,而不是仅作为起点
-
调整非代码文件是否合适?
- 如果行为发生变化,我是否更新了文档?
- 如果依赖项发生变化,我是否更新了配置文件?
-
是否应该编写新的测试?
- 如果我添加了新功能,是否为其编写了测试?
- 测试是否覆盖了指南中的安全模式?
-
这是否只是探索或研究?
- 如果是,则无需运行测试、Lint 检查和审计
- 重点是提供准确的信息
这种自我检查可以避免工作不完整、遗漏要求和安全漏洞。
项目交付
停止——创建任何 ZIP 或交付物之前
打包前必须运行并通过所有验证命令:
# 1. Install dependencies (use the appropriate command for your project type)
# Non-contract:
npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 @btc-vision/bitcoin@rc @btc-vision/transaction@rc opnet@rc @btc-vision/bip32 @btc-vision/ecpair --prefer-online
# Contract:
npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 @btc-vision/[email protected] @btc-vision/assemblyscript@^0.29.2 @btc-vision/[email protected] @btc-vision/btc-runtime@rc --prefer-online
# 2. Format code
npm run format
# 3. Lint (MUST PASS)
npm run lint
# 4. TypeScript check (MUST PASS)
npm run typecheck
# OR: npx tsc --noEmit
# 5. Build (MUST SUCCEED)
npm run build
# 6. Tests (MUST PASS)
npm run test
如果任何命令失败,请勿创建 ZIP。请先修复错误。
❌ **禁止:**未运行验证命令就创建 ZIP ❌ **禁止:**只说“运行 npm run lint 进行验证”,而不实际运行 ❌ **禁止:**因为“应该能正常运行”而跳过类型检查
创建 ZIP 文件用于交付时:
绝不要包含:
node_modules/——接收方会运行npm installbuild/或dist/——接收方会运行npm run build.git/——仓库历史记录.env——包含机密信息
正确的 ZIP 命令(仅在所有检查通过后运行):
zip -r project.zip . -x "node_modules/*" -x "build/*" -x "dist/*" -x ".git/*" -x "*.wasm" -x ".env"
什么是 OPNet
OPNet 是一个比特币 L1 共识层,可直接在比特币上运行智能合约。它具有以下特点:
- 不是元协议——它是一个完整的共识层
- 完全无需信任——没有中心化组件
- 无需许可——任何人都可以参与
- 去中心化——依托比特币 PoW 和 OPNet epoch SHA1 挖矿
安全模型
经过 20 个区块后,一个 epoch 就已足够深埋;要修改它,需要以每小时数百万美元的代价重写比特币历史,因此 OPNet 状态的最终性高于比特币 6 次确认所提供的安全性。
每个 epoch 的校验和根都是整个状态的加密指纹。哪怕只相差 1 位,校验和也会完全改变,导致证明失败,从而让状态无声损坏成为不可能。
核心原则
- 合约采用 WebAssembly(AssemblyScript)——执行具有确定性
- 非托管——合约永远不会持有 BTC
- 验证而非托管——合约验证 L1 交易输出,而不持有资金
- 部分回滚——只有共识层执行会回滚;比特币转账始终有效
- 无需 Gas 代币——直接使用比特币
- CSV 时间锁是强制要求——所有在兑换中接收 BTC 的地址都必须使用 CSV(CheckSequenceVerify),以防止交易钉扎攻击
为什么 OPNet 需要共识(而不只是索引)
OPNet 与基于索引器的协议(如 Runes、Ordinals/BRC-20 或 Alkanes)有本质区别:
| 协议 | 状态一致性 | 不同节点会产生分歧吗? |
|---|---|---|
| Runes | 取决于索引器 | 会——没有共识机制 |
| BRC-20 | 取决于索引器 | 会——没有共识机制 |
| Alkanes | 取决于索引器 | 会——WASM 具有确定性,但没有共识 |
| OPNet | 加密共识 | 不会——工作量证明 + 证明声明确保各方达成一致 |
这很重要的原因:对于需要强制状态一致性的应用(如 DEX、托管服务或任何多方协调场景),索引器之间出现分歧将是灾难性的。当 NativeSwap 按特定价格锁定一笔预留时,每个节点都必须认同该预留存在。OPNet 的共识层通过加密证明保证这一点。
CSV:关键的防钉扎机制(强制要求)
什么是交易钉扎?
交易钉扎是一种灾难性的攻击途径,大多数比特币协议都忽视了这一点。其运作方式如下:
- 当你向某人发送比特币时,对方可以立即创建一笔花费该比特币的交易,即使第一笔交易尚未确认
- 攻击者创建大量由彼此依赖的未确认交易组成的链
- 这会让矿工无法将你的有效交易纳入区块
- 当攻击者操纵合约状态时,你的交易会困在内存池中
为什么没有防护的 DEX 会被钉扎攻击摧毁
针对未受保护的 DEX 的攻击场景:
- 攻击者钉扎你的兑换交易,阻止其确认
- 你的预留过期,但 BTC 仍卡在内存池中
- 攻击者取消自己的卖单,或操纵池状态
- 当你的交易最终确认时(如果能确认的话),代币已经不剩了
- 结果:攻击者不劳而获,你失去一切
所有未强制要求 CSV 的比特币协议都存在这一漏洞:
- 只需一笔恶意的解锁请求,就能完全冻结多签桥
- Ordinals 市场容易受到攻击
- Runes 交易平台容易受到攻击
- BRC-20 交易所容易受到攻击
CSV 解决方案
**CSV(CheckSequenceVerify,BIP 112)**可彻底消除钉扎攻击:
Without CSV: Maximum unconfirmed chain length = UNLIMITED (attackers can pin forever)
With CSV: Maximum unconfirmed chain length = ZERO (must wait for confirmation)
要求所有卖方地址设置 1 个区块的 CSV 时间锁后,比特币一旦到达这些地址,至少一个区块内就无法再次花费。这一点可以通过数学方式证明,并能彻底堵住攻击途径。
实现要求
OPNet 交换中所有接收 BTC 的地址都必须使用 CSV 时间锁。
NativeSwap 在协议层强制执行这一要求。如果你正在构建任何协调 BTC 转账与智能合约状态的应用,就必须实现 CSV 保护。
两套地址系统(空投的关键)
为什么 OPNet 空投的运作方式不同于 Ethereum
在 Ethereum 上,只有一种地址格式。你可以遍历地址并调用 transfer(to, amount),因为 ERC20 合约只需使用相同的地址类型,减少发送方余额并增加接收方余额。
在 OPNet 上,合约余额以 ML-DSA 公钥哈希(32 字节抗量子地址)为键,但用户通常通过 Bitcoin 地址(Taproot P2TR,即经过调整的公钥)在外部识别。这是完全不同的密码学系统,两者之间没有内在关联。
| 地址系统 | 格式 | 用途 |
|---|---|---|
| Bitcoin 地址 | Taproot P2TR(bc1p...) | 外部身份,即空投名单中的地址 |
| OPNet 地址 | ML-DSA 公钥哈希(32 字节) | 合约余额、内部状态 |
为什么不能直接遍历并转账
如果你有一份来自代币持有者或快照参与者的 Bitcoin 地址列表:
// WRONG - THIS DOES NOT WORK
for (const btcAddress of airdropList) {
await token.transfer(btcAddress, amount); // IMPOSSIBLE
}
合约实际上无法直接向 Bitcoin 地址记入代币。 合约存储使用的是 ML-DSA 地址,而不是 Bitcoin 地址。只有当用户明确证明同时拥有这两种密钥后,它们之间的映射才会建立。
正确方案:基于领取的空投合约
OPNet 上的空投通过采用领取模式的智能合约实现:
1. 部署空投合约,按经过调整的公钥存储分配额度:
// Contract storage
mapping(tweakedPubKey => amount) // Store: which Bitcoin addresses get how much
mapping(tweakedPubKey => claimed) // Track: has this allocation been claimed
2. 用户调用 claim(),并提供证明其控制该 Bitcoin 地址的签名:
// User's frontend (browser) - OP_WALLET signs automatically
const message = `Claim airdrop for ${contractAddress}`;
const signed = await MessageSigner.tweakAndSignMessageAuto(message);
// Submit to contract with signature in calldata
await airdropContract.claim(signature, messageHash);
3. 合约验证并转账:
// Contract logic
public claim(calldata: Calldata): BytesWriter {
const signature = calldata.readBytes(64);
const messageHash = calldata.readBytes(32);
// Verify signature proves caller owns the tweaked public key
if (!Blockchain.verifySignature(Blockchain.tx.origin, signature, messageHash, false)) {
throw new Revert('Invalid signature');
}
// Get allocation for this tweaked public key
const tweakedKey = Blockchain.tx.origin.tweakedPublicKey;
const amount = this.allocations.get(tweakedKey);
// Transfer to caller's ML-DSA address (now linked!)
this._mint(Blockchain.tx.sender, amount);
this.claimed.set(tweakedKey, true);
}
这就是“解锁交易”——用户证明自己同时拥有两种身份(Bitcoin 地址和 ML-DSA 地址),从而使合约能够将两者关联起来并记入代币。
香蕉储物柜类比
- 你通过长相(Bitcoin 地址)认识 300 只猴子
- 储物柜通过秘密握手(ML-DSA 密钥)开启
- 你在储物柜上贴上猴子的长相,并把香蕉放进去
- 猴子出现时,既要出示长相,也要完成握手
- 系统由此得知:“这个长相 = 这个握手”,然后把香蕉给它
- 香蕉一直“属于”它,但只有将长相与握手关联起来后,它才能取到香蕉
NativeSwap:如何在 Bitcoin 上构建真正的 DEX
NativeSwap 解答了 BitcoinFi 中最大的未解问题:如何构建一个真正的 AMM,让用户无需托管、以无需信任的方式用原生 BTC 交易代币?
根本问题
传统 AMM(如 Uniswap)会在资金池中持有两种资产,并通过数学公式设定价格。Bitcoin 无法做到这一点——智能合约实际上无法持有 BTC 并以程序化方式转移 BTC。
常见“解决方案”为何行不通:
- 多签钱包:受信任方可能串通或消失
- 封装 BTC:跨链桥会变成蜜罐(已有数十亿美元从跨链桥中被盗)
- 纯订单簿:如果没有做市商持有库存,流动性会非常糟糕
虚拟储备:解决方案
NativeSwap 发现,AMM 不需要实际持有资产——只需跟踪交易的经济影响。这类似于:
- 银行更新账本条目,而不转移实体现金
- 期货市场交易价值数十亿美元的大宗商品,而无需接触一桶石油
- 清算所结算数万亿美元,而不持有标的资产
工作原理:
- 合约维护两个数值:
bitcoinReserve和tokenReserve - 有人用 BTC 购买代币时,系统记录比特币储备增加、代币储备减少
- 实际 BTC 直接转给卖家,而不是转给合约
- AMM 定价仅取决于储备之间的比例
- 无论储备是实体还是虚拟,恒定乘积公式(
bitcoinReserve × tokenReserve = k)的运作方式都相同
两阶段提交:为什么需要预留
问题:OPNet 可以回滚智能合约执行,但无法撤销比特币转账。BTC 一旦发出,该转账就受比特币的共识规则约束,而非 OPNet。
未使用预留时的灾难性情景:
- 你看到代币价格为 0.01 BTC/代币
- 你创建一笔发送 1 BTC 以购买 100 个代币的交易
- 在 10–20 分钟的确认时间内,其他交易将价格推高至 0.02 BTC/代币
- 在 Ethereum 上,你的交易会回滚,你的 ETH 会退回
- 在 Bitcoin 上,你的 BTC 已经转出——合约无法将其退回
- 你损失了 BTC,却没有获得任何代币
预留系统(两阶段提交):
| 阶段 | 执行内容 |
|---|---|
| 阶段 1:预留 | 证明你控制 BTC(UTXO 作为输入,再发送回你自己),支付少量费用,价格锁定在共识状态中 |
| 阶段 2:执行 | 向提供者发送准确数量的 BTC(最多可原子性地发送至 200 个地址),保证按锁定价格执行 |
优势:
- 无滑点风险:价格在预留时锁定
- 无抢先交易:价格一旦锁定在 OPNet 状态中,任何人都无法更改
- 部分成交:在单笔原子交易中自动协调向最多 200 个提供者付款(其他任何 Bitcoin 协议都无法实现)
队列影响:将待处理卖单纳入计算
卖家按当前 AMM 价格将代币加入队列。队列影响机制使用对数缩放调整有效代币储备:
为什么采用对数?
- 市场以乘法方式处理信息
- 队列从 100 个代币增加到 200 个代币,与从 1000 个增加到 2000 个代币,产生相同的心理影响
- 首批卖家释放强烈的卖压信号;后续卖家的边际影响递减
- 符合市场微观结构研究中的经验观察
削减:让操纵队列在经济上得不偿失
如果没有惩罚,队列影响机制就毫无价值:
- 攻击者添加大量虚假卖单 → 通过队列影响压低价格
- 攻击者买入低价代币
- 攻击者取消自己的卖单
- 从操纵中获利
削减机制:
- 立即取消:罚没 50%(高于任何现实的操纵利润)
- 长期占位:罚没比例提高至 90%
- 罚没的代币返回资金池:攻击行为反而会改善流动性
这使操纵在经济上得不偿失,并确保队列深度是可靠的市场信号。
总结:为什么每个组件都必不可少
| 组件 | 解决的限制 |
|---|---|
| 虚拟储备 | 智能合约无法在 Bitcoin 上托管 BTC |
| 预留(两阶段提交) | OPNet 控制合约状态,而非 Bitcoin 转账 |
| 队列影响 | 待处理订单会影响市场心理和定价 |
| 罚没 | 没有处罚措施时,队列影响将可被操纵 |
| CSV 时间锁 | UTXO 链容易受到交易钉住攻击 |
| OPNet 共识 | 索引器无法提供具有约束力的状态一致性 |
移除任何组件,系统就会变得可被利用,或无法继续作为 AMM 运作。
强制执行规则(不可妥协)
基于 ABI 的合约交互为强制要求
所有与 OPNet 智能合约交互的前端网站都必须使用 opnet npm 包和合约的 ABI 定义。 此要求不可妥协。
- 始终在实例化合约时导入并使用合约的 ABI(来自
opnet内置 ABI 或自定义 ABI) - 绝不使用原始 RPC 调用、手动编码 calldata,或任何其他方式与合约交互
- 始终使用由 ABI 创建的类型化合约实例,以实现类型安全的方法调用和返回值解码
- 阅读
docs/core-opnet-abi-reference-abi-overview.md和docs/core-opnet-abi-reference-custom-abis.md,了解 ABI 的创建和用法
// CORRECT - Use opnet package with ABI definition
import { JSONRpcProvider } from 'opnet';
import { MyContractABI } from './abi/MyContractABI.js';
const provider = new JSONRpcProvider('https://regtest.opnet.org');
const contract = provider.getContract(contractAddress, MyContractABI);
const result = await contract.someMethod(param1, param2);
// WRONG - Raw RPC calls without ABI
const result = await fetch(rpcUrl, { body: JSON.stringify({ method: 'call', params: [...] }) });
// WRONG - Manual calldata encoding
const calldata = new Uint8Array([...]);
如果 opnet 中没有合约的预构建 ABI,就必须按照 docs/core-opnet-abi-reference-custom-abis.md 创建自定义 ABI 定义。
已知的前端错误——切勿重犯
以下都是真实发生过的错误。现在每一项都严禁出现。
1. 错误:使用 contract.execute() 和原始 selector 字节
// WRONG - Raw execute with selector bytes. NEVER DO THIS.
const selector = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
const result = await contract.execute(selector, calldata);
// CORRECT - Use ABI-defined typed method calls
const result = await contract.claim(signature, messageHash);
始终定义规范的 ABI,并按名称调用方法。原始 selector 会绕过类型安全,且可能静默失败。
2. 错误:缺少 getContract 参数
getContract 需要 5 个参数:(address, abi, provider, network, sender)。
// WRONG - Missing params (will fail or return broken contract)
const contract = getContract(address, abi);
const contract = getContract(address, abi, provider);
// CORRECT - All 5 params
const contract = getContract(address, abi, provider, network, senderAddress);
阅读 docs/core-opnet-contracts-instantiating-contracts.md,了解准确的签名。切勿猜测参数。
3. 错误:根据 walletconnect 的 signer 控制前端操作
在前端,钱包浏览器扩展负责签名。useWalletConnect() 返回的 signer 对象最初可能为 null/undefined——切勿根据它来限制用户操作。
// WRONG - Throws "Wallet not connected" because signer is null on frontend
if (!signer) throw new Error('Wallet not connected');
await contract.claim(signer, ...);
// CORRECT - Check wallet connection status, not signer object
const { isConnected, address } = useWalletConnect();
if (!isConnected || !address) throw new Error('Wallet not connected');
4. 错误:使用 walletconnect 的 provider 进行读取调用
创建一个单独的 JSONRpcProvider,用于只读合约调用。读取时不要复用 walletconnect 的 provider。
// WRONG - Using walletconnect provider for reads
const { provider } = useWalletConnect();
const data = await provider.call(...);
// CORRECT - Dedicated read provider
const readProvider = new JSONRpcProvider('https://regtest.opnet.org');
const contract = readProvider.getContract(address, abi);
const data = await contract.someReadMethod();
5. 错误:从错误的包导入
了解各符号所在的位置:
| 符号 | 正确的包 | 错误的包 |
|---|---|---|
Address | @btc-vision/transaction(也由 opnet 重新导出) | ❌ @btc-vision/bitcoin(不在浏览器包中) |
ABIDataTypes | @btc-vision/transaction(也由 opnet 重新导出) | ❌ @btc-vision/bitcoin |
JSONRpcProvider | opnet | ❌ @btc-vision/provider |
networks | @btc-vision/bitcoin | — |
// CORRECT imports for frontend
import { Address, ABIDataTypes } from 'opnet';
import { JSONRpcProvider } from 'opnet';
// WRONG - Address is NOT in the browser bundle of @btc-vision/bitcoin
import { Address } from '@btc-vision/bitcoin'; // ❌ WILL FAIL IN BROWSER
不确定时,请从 opnet 导入——它会重新导出最常用的符号。
6. 错误:将原始 Bitcoin 地址(bc1q/bc1p)或原始 ML-DSA 公钥传递给 Address.fromString()
Address.fromString() 接受两个参数:
- 第一个参数:ML-DSA 公钥的哈希值(32 字节,以 0x 为前缀)。这不是原始 ML-DSA 公钥(原始公钥要大得多)。walletconnect hook 会通过
mldsaPublicKey提供此值——它已经过哈希处理。 - 第二个参数:Bitcoin tweaked 公钥(33 字节,压缩格式,以 0x 为前缀)。walletconnect hook 会通过
publicKey提供此值。
它不接受原始 Bitcoin 地址(bc1q/bc1p),也不接受只有一个参数的调用。
// WRONG - Passing a raw Bitcoin address. WILL CRASH.
const sender = Address.fromString(walletAddress); // walletAddress = "bc1q..." ❌
// WRONG - Only one parameter. WILL CRASH.
const sender = Address.fromString(publicKey); // ❌
// WRONG - Passing mldsaPublicKey (the RAW ML-DSA public key, ~2500 bytes).
// Address.fromString expects the 32-byte HASH, not the raw key. ❌
const sender = Address.fromString(mldsaPublicKey, publicKey); // ❌ WRONG! mldsaPublicKey is raw
// CORRECT - ALWAYS pass hashedMLDSAKey (32-byte hash) + publicKey (Bitcoin tweaked pubkey)
const sender = Address.fromString(hashedMLDSAKey, publicKey);
// hashedMLDSAKey = "0xABCD..." (32-byte SHA256 hash of ML-DSA pubkey, from walletconnect's hashedMLDSAKey)
// publicKey = "0x0203..." (33-byte compressed Bitcoin tweaked pubkey, from walletconnect's publicKey)
在前端使用 walletconnect 时:
const { publicKey, hashedMLDSAKey } = useWalletConnect();
// publicKey = Bitcoin tweaked public key (0x hex, 33 bytes compressed)
// hashedMLDSAKey = 32-byte SHA256 hash of ML-DSA public key (0x hex)
// NOTE: mldsaPublicKey is the RAW key (~2500 bytes) — do NOT use it for Address.fromString()
// CORRECT - Use hashedMLDSAKey (NOT mldsaPublicKey) + publicKey
const senderAddress = Address.fromString(hashedMLDSAKey, publicKey);
// Then use in getContract
const contract = getContract<IMyContract>(contractAddr, abi, provider, network, senderAddress);
如果 mldsaPublicKey 不可用(钱包尚不支持 ML-DSA),请使用 provider.getPublicKeyInfo(walletAddress) 获取公钥信息,然后构造 Address。
7. 错误:在需要公钥十六进制字符串的地方使用 walletAddress(bc1q.../bc1p...)
walletconnect 提供的 walletAddress 是一个 bech32 Bitcoin 地址(bc1q... 或 bc1p...)。它不是公钥。许多 OPNet API 需要的是公钥十六进制字符串(以 0x 为前缀),而不是 bech32 地址。
// WRONG - Passing bech32 address where public key is expected
const sender = Address.fromString(walletAddress); // "bc1q..." is NOT a public key ❌
await contract.someMethod(walletAddress); // If it expects a pubkey ❌
// CORRECT - Use hashedMLDSAKey and publicKey from walletconnect
const { publicKey, hashedMLDSAKey, mldsaPublicKey, address: walletAddress } = useWalletConnect();
// publicKey = hex string "0x0203..." (Bitcoin tweaked pubkey, 33 bytes compressed)
// hashedMLDSAKey = hex string "0xABCD..." (32-byte SHA256 hash of ML-DSA pubkey)
// mldsaPublicKey = raw ML-DSA public key (~2500 bytes) — for signing ONLY, NOT for Address.fromString
// walletAddress = "bc1q..." (only use for display and refundTo)
经验法则:
walletAddress(bc1q/bc1p)→ 仅用于向用户显示,以及在 sendTransaction 中用作refundTopublicKey(0x 十六进制,33 字节)→ Bitcoin tweaked 公钥,用作 Address.fromString 的第二个参数hashedMLDSAKey(0x 十六进制,32 字节)→ ML-DSA 公钥的 SHA256 哈希,用作 Address.fromString 的第一个参数mldsaPublicKey(0x 十六进制,约 2500 字节)→ 原始 ML-DSA 公钥,仅用于 MLDSA 签名/验证。绝不要将其用于 Address.fromString
编写任何代码之前
你必须:
- 完整阅读
docs/core-typescript-law-CompleteLaw.md——这些是严格的 TypeScript 规则 - 确认项目配置符合
docs/配置文件中的标准
配置文件(位于 docs/)
| 文件 | 用途 |
|---|---|
eslint-contract.json | 用于 AssemblyScript 合约的 ESLint 配置 |
eslint-generic.json | 用于 TypeScript 库的 ESLint 配置 |
eslint-react.json | 用于 React/Next.js 前端的 ESLint 配置 |
tsconfig-generic.json | TypeScript 配置(不适用于合约) |
asconfig.json | AssemblyScript 编译器配置 |
TypeScript 是强制要求——没有例外
停止——绝对规则
你绝不能编写原始 JavaScript 代码,绝不例外。
所有代码都必须使用 TypeScript,并配备适当的工具链:
| 项目类型 | 所需技术栈 |
|---|---|
| 前端 | TypeScript + Vite + npm 库 |
| 后端 | TypeScript + Node.js + npm 库 |
| 合约 | AssemblyScript(类似 TypeScript) |
| 测试 | TypeScript |
禁止事项——绝对不要这样做:
- ❌ 原始 JavaScript 文件——绝不要编写
.js源文件 - ❌ 内联
<script>标签——绝不要在 HTML 中嵌入 JS - ❌ 仅限浏览器的 JS——绝不要编写只能在浏览器控制台运行的代码
- ❌ 没有构建系统——前端绝不要跳过 Vite/打包工具
- ❌ 在 JS 中使用
// @ts-check——这不是 TypeScript,请使用真正的.ts文件 - ❌
allowJs: true——绝不要在 tsconfig 中启用此项 - ❌ CDN 脚本导入——请改用 npm 包
必须做到——始终遵循:
- ✅ 所有源文件都必须是
.ts或.tsx - ✅ 所有前端应用都使用 Vite
- ✅ 所有后端应用都使用 Node.js + TypeScript
- ✅ 通过 npm 安装依赖(不要使用 CDN 链接)
- ✅ 在 tsconfig 中启用严格模式
- ✅ 明确定义所有类型
- ✅ 使用规范的项目结构,包含 package.json、tsconfig.json 等文件
如果用户要求“快速脚本”或“简单示例”
**仍然要使用 TypeScript。**请创建规范的项目结构:
# Even for "quick" examples
mkdir project && cd project
npm init -y
npm install typescript tsx
# Write .ts file, run with npx tsx script.ts
不存在可以接受原始 JavaScript 的情况。
必须完成完整的项目设置
编写任何代码之前,项目必须具备:
project/
├── package.json # With correct OPNet package versions
├── tsconfig.json # Matching docs/tsconfig-generic.json
├── eslint.config.js # Using appropriate config from docs/
├── .prettierrc # Prettier configuration
├── .prettierignore # Prettier ignore file
├── src/ # Source code directory
│ └── index.ts # Entry point
└── tests/ # Test directory (if applicable)
└── *.test.ts
Prettier 配置(强制)
.prettierrc:
{
"semi": true,
"singleQuote": true,
"tabWidth": 4,
"trailingComma": "all",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always"
}
.prettierignore:
node_modules/
build/
dist/
*.wasm
coverage/
Package.json 脚本(添加以下内容):
{
"scripts": {
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\""
}
}
提交前运行 Prettier:
npm run format
对于前端(React/Vite),还需包含:
├── vite.config.ts # Vite configuration
├── index.html # Entry HTML
└── src/
├── App.tsx
├── main.tsx
└── styles/ # CSS files (NO inline CSS)
└── variables.css
对于合约(AssemblyScript),还需包含:
├── asconfig.json # AssemblyScript config
└── assembly/
├── index.ts # Contract entry point
└── contracts/
└── MyContract.ts
必须在编写应用代码之前创建这些文件。
配置验证清单
-
package.json存在且包含正确的 OPNet 软件包版本 -
tsconfig.json与docs/tsconfig-generic.json一致(或合约使用严格的 ESNext) -
eslint.config.js使用docs/中适当的配置 -
.prettierrc存在且配置正确 - 任何地方都不得使用
any类型——这是禁止的 - 不得使用内联 CSS——使用 CSS 模块、Tailwind 或外部样式表
- 不得使用 JavaScript 文件——仅使用 TypeScript
- 代码已使用 Prettier 格式化(
npm run format) - 符合 ESNext 标准
- 单元测试存在且通过
如果有任何一项未通过 → 修复之前拒绝编写代码
配置错误的项目会导致漏洞利用和严重安全漏洞。
TypeScript 法则(关键)
摘自 docs/core-typescript-law-CompleteLaw.md:
禁止使用的结构
| 结构 | 禁止原因 |
|---|---|
any | 随时可能引发运行时错误。没有例外。 |
unknown | 仅限系统边界处(JSON 解析、外部 API)使用 |
object(小写) | 使用特定接口或 Record<string, T> |
Function(大写) | 使用特定签名 |
{} | 表示“任何非 nullish 值”。使用 Record<string, never> |
非空断言(! 运算符) | 使用显式 null 检查或可选链 |
| 无用/重复代码 | 出现此类代码说明设计有问题 |
| 绕过 ESLint | 绝不允许 |
| 分节分隔注释 | 见下文 |
| 内联 CSS | 使用 CSS 模块、styled-components 或外部样式表。不得使用 style={{ }} 属性。 |
禁止:分节分隔注释
绝不编写如下形式的注释:
// ==================== PRIVATE METHODS ====================
// ---------------------- HELPERS ----------------------
// ************* CONSTANTS *************
// ####### INITIALIZATION #######
这些注释懒惰、不专业且毫无用处。它们只会增加噪声,没有任何价值。
应改用:为每个类、方法、属性和函数编写规范的 TSDoc:
/**
* Transfers tokens from sender to recipient.
*
* @param to - The recipient address
* @param amount - The amount to transfer in base units
* @returns True if transfer succeeded
* @throws {InsufficientBalanceError} If sender has insufficient balance
* @throws {InvalidAddressError} If recipient address is invalid
*
* @example
* ```typescript
* const success = await token.transfer(recipientAddress, 1000n);
* ```
*/
public async transfer(to: Address, amount: bigint): Promise<boolean> {
// ...
}
TSDoc 要求:
- 为每个参数添加
@param - 为非 void 返回值添加
@returns - 为可能出现的异常添加
@throws - 为非简单方法添加
@example - 描述方法的功能,而不是实现方式
代码组织应来自合理的类设计,而非 ASCII 图案。
数值类型
number:数组长度、循环计数器、小型标志、端口、像素bigint:聪金额、区块高度、时间戳、数据库 ID、文件大小、累计总量- 财务值使用浮点数:禁止——使用具有明确缩放比例的定点
bigint
常见 API 陷阱
Address.fromString() 需要两个参数:
// WRONG - will fail (one param)
const addr = Address.fromString(addressString);
// WRONG - will fail (raw Bitcoin address)
const addr = Address.fromString('bc1q...');
// WRONG - raw ML-DSA public key is ~2500 bytes, NOT what this function expects
const addr = Address.fromString(rawMldsaPublicKey, tweakedPublicKey);
// CORRECT - always provide both: hashedMLDSAKey (32-byte hash) + publicKey (Bitcoin tweaked pubkey)
const addr = Address.fromString(hashedMLDSAKey, publicKey);
第一个参数是 ML-DSA 公钥的 32 字节 SHA256 哈希值——即 useWalletConnect() 中的 hashedMLDSAKey,而不是 mldsaPublicKey(后者是原始的约 2500 字节密钥)。第二个参数是经过 Bitcoin tweak 的公钥(压缩后 33 字节)——即 useWalletConnect() 中的 publicKey。两者都必须是带 0x 前缀的十六进制字符串。始终提供这两个参数。
必需的 tsconfig.json 设置
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"moduleResolution": "bundler",
"module": "ESNext",
"target": "ESNext",
"lib": ["ESNext"],
"isolatedModules": true,
"verbatimModuleSyntax": true
}
}
性能规则
必须使用多线程
- 顺序处理 = 性能不可接受
- CPU 密集型任务使用 Worker 线程
- I/O 使用异步操作并设置适当的并发
- 尽可能批量处理操作
- 使用连接池
缓存(始终使用)
- 复用合约实例——同一合约不得重复创建新实例
- 复用提供程序——每个网络只使用一个提供程序实例
- 本地缓存——用户数据存储在浏览器 localStorage/IndexedDB 中
- API 缓存——对区块链状态进行服务器端缓存
- 区块变化时失效——确认新区块后清除过期数据
RPC 调用优化
使用 .metadata() 代替多次调用:
// ❌ BAD - 4 RPC calls (slow)
const [name, symbol, decimals, totalSupply] = await Promise.all([
contract.name(),
contract.symbol(),
contract.decimals(),
contract.totalSupply()
]);
// ✅ GOOD - 1 RPC call (fast)
const { name, symbol, decimals, totalSupply } = (await contract.metadata()).decoded;
.metadata() 通过一次调用即可返回名称、符号、精度、总供应量、所有者等信息。
后端/API 框架
强制要求:使用 OPNet 的高性能库:
| 软件包 | 用途 |
|---|---|
@btc-vision/uwebsocket.js | WebSocket 服务端(最快) |
@btc-vision/hyper-express | HTTP 服务端(最快) |
禁止使用:Express、Fastify、Koa、Hapi 或任何其他 HTTP 框架。它们的速度明显更慢。
// CORRECT - Use hyper-express with threading
import HyperExpress from '@btc-vision/hyper-express';
import { Worker } from 'worker_threads';
const app = new HyperExpress.Server();
// Use classes, not functions
// Delegate CPU work to workers
合约 Gas 优化(关键)
合约中禁止使用的模式
| 模式 | 禁止原因 | 替代方案 |
|---|---|---|
while 循环 | Gas 消耗无上限 | 使用有界 for 循环 |
| 无限循环 | 合约会停止运行并浪费 Gas | 始终设置退出条件 |
| 遍历所有映射键 | O(n) 会呈指数级增长 | 使用索引查找、分页 |
| 遍历所有数组元素 | O(n) 成本会急剧上升 | 存储聚合值、使用分页 |
| 无界数组 | 会无限增长 | 限制大小、使用清理机制 |
Gas 高效模式
// WRONG - Iterating all holders (O(n) disaster)
let total: u256 = u256.Zero;
for (let i = 0; i < holders.length; i++) {
total = SafeMath.add(total, balances.get(holders[i]));
}
// CORRECT - Store running total
const totalSupply: StoredU256 = new StoredU256(TOTAL_SUPPLY_POINTER);
// Update on mint/burn, read in O(1)
安全审计清单
必须检查所有代码中的以下问题:
密码学
- 密钥生成熵
- Nonce 重用
- 签名可塑性
- 定时攻击
- 重放攻击
智能合约
- 重入
- 整数溢出/下溢
- 访问控制绕过
- 授权缺陷
- 状态篡改
- 竞态条件
Bitcoin 特有问题
- 交易可塑性
- UTXO 选择漏洞
- 费用狙击
- 粉尘攻击
- 交易钉扎攻击 - 必须使用 CSV 时间锁
- 未确认交易链 - 验证 CSV 强制执行情况
DEX/兑换特有问题
- 抢先交易 / MEV 攻击
- 通过队列泛洪操纵价格
- 预留过期边缘情况
- 部分成交协调
- 试图绕过罚没机制
快速入门
| 任务 | 模板 |
|---|---|
| 新建 OP20 代币 | templates/contracts/OP20Token.ts |
| 新建 OP721 NFT | templates/contracts/OP721NFT.ts |
| 通用合约 | templates/contracts/MyContract.ts |
| 合约测试 | templates/tests/ |
| 前端 dApp | templates/frontend/ |
| 节点插件(索引器) | templates/plugins/OP20Indexer.ts |
| 节点插件(通用) | templates/plugins/MyPlugin.ts |
完整文件索引
配置文件(docs/)
| 文件 | 描述 |
|---|---|
docs/asconfig.json | AssemblyScript 编译器配置 |
docs/eslint-contract.json | 用于 AssemblyScript 合约的 ESLint |
docs/eslint-generic.json | 用于 TypeScript 库的 ESLint |
docs/eslint-react.json | 用于 React 前端的 ESLint |
docs/tsconfig-generic.json | TypeScript 配置(非合约) |
docs/setup-README.md | 设置说明 |
TypeScript 规范
| 文件 | 描述 |
|---|---|
docs/core-typescript-law-readme.md | 概览 |
docs/core-typescript-law-CompleteLaw.md | 完整规则 - 请先阅读 |
OPNet 客户端库
| 文件 | 描述 |
|---|---|
docs/core-opnet-README.md | 库概览 |
docs/core-opnet-backend-api.md | 使用 hyper-express 的后端 API |
docs/core-opnet-getting-started-installation.md | 安装 |
docs/core-opnet-getting-started-overview.md | 架构概览 |
docs/core-opnet-getting-started-quick-start.md | 快速入门指南 |
docs/core-opnet-providers-json-rpc-provider.md | JSON-RPC 提供程序 |
docs/core-opnet-providers-websocket-provider.md | WebSocket 提供程序 |
docs/core-opnet-providers-understanding-providers.md | 提供程序概念 |
docs/core-opnet-providers-advanced-configuration.md | 高级配置 |
docs/core-opnet-providers-internal-caching.md | 缓存系统 |
docs/core-opnet-providers-threaded-http.md | 多线程 |
docs/core-opnet-contracts-overview.md | 合约交互概览 |
docs/core-opnet-contracts-instantiating-contracts.md | 创建合约实例 |
docs/core-opnet-contracts-simulating-calls.md | 模拟调用 |
docs/core-opnet-contracts-sending-transactions.md | 发送交易 |
docs/core-opnet-contracts-gas-estimation.md | Gas 估算 |
docs/core-opnet-contracts-offline-signing.md | 离线签名 |
docs/core-opnet-contracts-transaction-configuration.md | 交易配置 |
docs/core-opnet-contracts-contract-code.md | 获取合约代码 |
docs/core-opnet-abi-reference-abi-overview.md | ABI 概览 |
docs/core-opnet-abi-reference-data-types.md | ABI 数据类型 |
docs/core-opnet-abi-reference-op20-abi.md | OP20 ABI |
docs/core-opnet-abi-reference-op20s-abi.md | OP20S ABI(签名) |
docs/core-opnet-abi-reference-op721-abi.md | OP721 ABI |
docs/core-opnet-abi-reference-motoswap-abis.md | MotoSwap ABI |
docs/core-opnet-abi-reference-factory-abis.md | 工厂 ABI |
docs/core-opnet-abi-reference-stablecoin-abis.md | 稳定币 ABI |
docs/core-opnet-abi-reference-custom-abis.md | 创建自定义 ABI |
docs/core-opnet-api-reference-provider-api.md | 提供程序 API |
docs/core-opnet-api-reference-contract-api.md | 合约 API |
docs/core-opnet-api-reference-epoch-api.md | Epoch API |
docs/core-opnet-api-reference-utxo-manager-api.md | UTXO Manager API |
docs/core-opnet-api-reference-types-interfaces.md | 类型与接口 |
docs/core-opnet-bitcoin-utxos.md | UTXO 处理 |
docs/core-opnet-bitcoin-utxo-optimization.md | UTXO 优化 |
docs/core-opnet-bitcoin-balances.md | 余额查询 |
docs/core-opnet-bitcoin-sending-bitcoin.md | 发送 BTC |
docs/core-opnet-blocks-block-operations.md | 区块操作 |
docs/core-opnet-blocks-block-witnesses.md | 区块见证数据 |
docs/core-opnet-blocks-gas-parameters.md | Gas 参数 |
docs/core-opnet-blocks-reorg-detection.md | 检测链重组 |
docs/core-opnet-epochs-overview.md | Epoch 概览 |
docs/core-opnet-epochs-epoch-operations.md | Epoch 操作 |
docs/core-opnet-epochs-mining-template.md | 挖矿模板 |
docs/core-opnet-epochs-submitting-epochs.md | 提交 Epoch |
docs/core-opnet-transactions-broadcasting.md | 广播交易 |
docs/core-opnet-transactions-fetching-transactions.md | 获取交易 |
docs/core-opnet-transactions-transaction-receipts.md | 交易收据 |
docs/core-opnet-transactions-challenges.md | 交易挑战 |
docs/core-opnet-storage-storage-operations.md | 存储操作 |
docs/core-opnet-public-keys-public-key-operations.md | 公钥操作 |
docs/core-opnet-utils-bitcoin-utils.md | Bitcoin 工具 |
docs/core-opnet-utils-binary-serialization.md | 二进制序列化 |
docs/core-opnet-utils-revert-decoder.md | 回滚解码器 |
docs/core-opnet-examples-op20-examples.md | OP20 示例 |
docs/core-opnet-examples-op721-examples.md | OP721 示例 |
docs/core-opnet-examples-deployment-examples.md | 部署示例 |
docs/core-opnet-examples-advanced-swaps.md | 交换示例 |
交易库
| 文件 | 描述 |
|---|---|
docs/core-transaction-README.md | 库概览 |
docs/core-transaction-transaction-building.md | 构建交易 |
docs/core-transaction-transaction-factory-interfaces.md | TransactionFactory 接口(费用、备注、锚点、发送最大金额) |
docs/core-transaction-offline-transaction-signing.md | 离线签名 |
docs/core-transaction-addresses-P2OP.md | P2OP 地址格式 |
docs/core-transaction-quantum-support-README.md | 量子概览 |
docs/core-transaction-quantum-support-01-introduction.md | 量子简介 |
docs/core-transaction-quantum-support-02-mnemonic-and-wallet.md | 量子钱包 |
docs/core-transaction-quantum-support-03-address-generation.md | 量子地址 |
docs/core-transaction-quantum-support-04-message-signing.md | 量子签名 |
docs/core-transaction-quantum-support-05-address-verification.md | 量子验证 |
地址系统与空投(重要)
| 文件 | 描述 |
|---|---|
docs/core-opnet-address-systems-airdrop-pattern.md | 两种地址系统、空投模式(必读) |
OIP 规范
| 文件 | 描述 |
|---|---|
docs/core-OIP-README.md | OIP 概览 |
docs/core-OIP-OIP-0001.md | OIP 流程 |
docs/core-OIP-OIP-0002.md | 合约标准 |
docs/core-OIP-OIP-0003.md | 插件系统规范 |
docs/core-OIP-OIP-0004.md | Epoch 系统 |
docs/core-OIP-OIP-0020.md | OP20 代币标准 |
docs/core-OIP-OIP-0721.md | OP721 NFT 标准 |
Contract Runtime (btc-runtime)
| File | Description |
|---|---|
docs/contracts-btc-runtime-README.md | Runtime overview |
docs/contracts-btc-runtime-gas-optimization.md | Gas optimization (CRITICAL) |
docs/contracts-btc-runtime-getting-started-installation.md | Installation |
docs/contracts-btc-runtime-getting-started-first-contract.md | First contract |
docs/contracts-btc-runtime-getting-started-project-structure.md | Project structure |
docs/contracts-btc-runtime-core-concepts-blockchain-environment.md | Blockchain env |
docs/contracts-btc-runtime-core-concepts-storage-system.md | Storage system |
docs/contracts-btc-runtime-core-concepts-pointers.md | Storage pointers |
docs/contracts-btc-runtime-core-concepts-events.md | Events |
docs/contracts-btc-runtime-core-concepts-decorators.md | Decorators |
docs/contracts-btc-runtime-core-concepts-security.md | Security |
docs/contracts-btc-runtime-api-reference-blockchain.md | Blockchain API |
docs/contracts-btc-runtime-api-reference-storage.md | Storage API |
docs/contracts-btc-runtime-api-reference-events.md | Events API |
docs/contracts-btc-runtime-api-reference-op20.md | OP20 API |
docs/contracts-btc-runtime-api-reference-op721.md | OP721 API |
docs/contracts-btc-runtime-api-reference-safe-math.md | SafeMath API |
docs/contracts-btc-runtime-contracts-op-net-base.md | OP_NET base class |
docs/contracts-btc-runtime-contracts-op20-token.md | OP20 implementation |
docs/contracts-btc-runtime-contracts-op20s-signatures.md | OP20S signatures |
docs/contracts-btc-runtime-contracts-op721-nft.md | OP721 implementation |
docs/contracts-btc-runtime-contracts-reentrancy-guard.md | Reentrancy guard |
docs/contracts-btc-runtime-contracts-upgradeable.md | Upgradeable contracts |
docs/contracts-btc-runtime-storage-stored-primitives.md | Stored primitives |
docs/contracts-btc-runtime-storage-stored-maps.md | Stored maps |
docs/contracts-btc-runtime-storage-stored-arrays.md | Stored arrays |
docs/contracts-btc-runtime-storage-memory-maps.md | Memory maps |
docs/contracts-btc-runtime-types-address.md | Address type |
docs/contracts-btc-runtime-types-calldata.md | Calldata type |
docs/contracts-btc-runtime-types-bytes-writer-reader.md | BytesWriter/Reader |
docs/contracts-btc-runtime-types-safe-math.md | SafeMath type |
docs/contracts-btc-runtime-advanced-cross-contract-calls.md | Cross-contract calls |
docs/contracts-btc-runtime-advanced-signature-verification.md | Signature verification |
docs/contracts-btc-runtime-advanced-quantum-resistance.md | Quantum resistance |
docs/contracts-btc-runtime-advanced-bitcoin-scripts.md | Bitcoin scripts |
docs/contracts-btc-runtime-advanced-contract-upgrades.md | Contract upgrades |
docs/contracts-btc-runtime-advanced-plugins.md | Contract plugins |
docs/contracts-btc-runtime-examples-basic-token.md | Basic token example |
docs/contracts-btc-runtime-examples-stablecoin.md | Stablecoin example |
docs/contracts-btc-runtime-examples-nft-with-reservations.md | NFT example |
docs/contracts-btc-runtime-examples-oracle-integration.md | Oracle example |
Other Contract Docs
| File | Description |
|---|---|
docs/contracts-as-bignum-README.md | BigNum library (u256, u128) |
docs/contracts-opnet-transform-README.md | Transform decorators |
docs/contracts-opnet-transform-std-README.md | Standard library |
docs/contracts-example-tokens-README.md | Example tokens |
docs/contracts-example-tokens-docs-OP_20.md | OP20 example |
客户端库
| 文件 | 描述 |
|---|---|
docs/clients-bitcoin-README.md | Bitcoin 库概览 |
docs/clients-bitcoin-psbt.md | PSBT 类、签名、定稿 |
docs/clients-bitcoin-payments.md | 支付类型(P2TR、P2WPKH、P2WSH、P2SH、P2OP) |
docs/clients-bitcoin-script.md | 脚本构建、操作码 |
docs/clients-bitcoin-address.md | 地址编码/解码 |
docs/clients-bitcoin-transaction.md | 交易类 |
docs/clients-bip32-README.md | BIP32 HD 派生 |
docs/clients-bip32-QUANTUM.md | 量子支持 |
docs/clients-ecpair-README.md | EC 密钥对 |
docs/clients-walletconnect-README.md | WalletConnect |
docs/clients-walletconnect-wallet-integration.md | 钱包集成 |
测试
| 文件 | 描述 |
|---|---|
docs/testing-unit-test-framework-README.md | 测试框架 |
docs/testing-opnet-unit-test-README.md | 单元测试 |
docs/testing-opnet-unit-test-docs-README.md | 测试文档 |
docs/testing-opnet-unit-test-docs-Blockchain.md | 区块链模拟 |
docs/testing-opnet-unit-test-docs-ContractRuntime.md | 合约运行时 |
前端
| 文件 | 描述 |
|---|---|
docs/frontend-motoswap-ui-README.md | 前端指南(标准) |
插件
| 文件 | 描述 |
|---|---|
docs/plugins-plugin-sdk-README.md | 插件 SDK |
docs/plugins-opnet-node-README.md | OPNet 节点 |
docs/plugins-opnet-node-docker-README.md | Docker 设置 |
模板
合约模板(templates/contracts/)
| 文件 | 描述 |
|---|---|
templates/contracts/OP20Token.ts | OP20 代币实现 |
templates/contracts/OP721NFT.ts | OP721 NFT 实现 |
templates/contracts/MyContract.ts | 通用合约模板 |
templates/contracts/index.ts | 入口点 / 导出 |
前端模板(templates/frontend/)
| 文件 | 描述 |
|---|---|
templates/frontend/App.tsx | 主应用组件 |
templates/frontend/OPNetProvider.tsx | OPNet 上下文提供程序 |
templates/frontend/WalletConnect.tsx | 钱包连接组件 |
templates/frontend/ContractInteraction.tsx | 合约交互组件 |
templates/frontend/useWallet.ts | 钱包 Hook |
templates/frontend/useContract.ts | 合约 Hook |
templates/frontend/vite.config.ts | Vite 配置 |
templates/frontend/package.json | 软件包依赖项 |
插件模板(templates/plugins/)
| 文件 | 描述 |
|---|---|
templates/plugins/MyPlugin.ts | 通用插件模板 |
templates/plugins/OP20Indexer.ts | OP20 索引器插件 |
templates/plugins/types.ts | 类型定义 |
templates/plugins/index.ts | 入口点 |
templates/plugins/plugin.json | 插件清单 |
测试模板(templates/tests/)
| 文件 | 描述 |
|---|---|
templates/tests/OP20.test.ts | OP20 测试示例 |
templates/tests/setup.ts | 测试设置 |
templates/tests/gulpfile.js | Gulp 构建配置 |
故障排除
常见错误及修复方法记录在 references/troubleshooting.md 中。遇到构建错误、RPC 故障、前端崩溃或部署问题时,请查阅该文档。
高级交易功能
当用户询问如何为交易(部署、交互、资金注入)添加费用、备注或高级功能时,务必查阅 docs/core-transaction-transaction-factory-interfaces.md。
常见用户请求 → 参数
| 用户请求 | 参数 | 接口 |
|---|---|---|
| “添加手续费” / “设置费率” | feeRate: number(sat/vB) | TransactionParameters |
| “添加优先费” | priorityFee: bigint | TransactionParameters |
| “添加备注/备忘录” | note: string | Uint8Array | TransactionParameters |
| “使用锚定输出” | anchor: true | TransactionParameters |
| “发送到多个地址” | extraOutputs: PsbtOutputExtended[] | TransactionParameters |
| “发送最大金额/全部余额” | autoAdjustAmount: true | IFundingTransactionParameters |
| “从单独的 UTXO 支付手续费” | feeUtxos: UTXO[] | IFundingTransactionParameters |
| “设置最低 gas” | minGas: bigint | TransactionParameters |
| “离线签名” | toOfflineBuffer() / fromOfflineBuffer() | CallResult 方法 |
| “签名但不广播” | signTransaction() | CallResult 方法 |
| “广播预签名 TX” | sendPresignedTransaction() | CallResult 方法 |
CallResult 交易方法
// Sign without broadcast
const signedTx = await simulation.signTransaction(params);
// Sign and broadcast
const receipt = await simulation.sendTransaction(params);
// Broadcast pre-signed
const receipt = await simulation.sendPresignedTransaction(signedTx);
// Offline signing workflow
const buffer = await simulation.toOfflineBuffer(address, amount);
const reconstructed = CallResult.fromOfflineBuffer(buffer);
快速示例:添加手续费 + 备注
await simulation.sendTransaction({
signer: wallet.keypair,
mldsaSigner: wallet.mldsaKeypair,
refundTo: wallet.p2tr,
network: networks.regtest,
maximumAllowedSatToSpend: 100000n,
feeRate: 15, // 15 sat/vB
priorityFee: 5000n, // +5000 sats priority
note: "Payment for invoice #123", // OP_RETURN memo
});
完整详情请阅读 docs/core-transaction-transaction-factory-interfaces.md。
合约开发
核心概念
- 合约使用自定义的 AssemblyScript 分支(
@btc-vision/assemblyscript)——此分支为标准 AssemblyScript 添加了闭包支持,从而支持回调和高阶函数模式。请始终安装@btc-vision/assemblyscript,而不是上游的assemblyscript包。 - 每次交互都会运行构造函数——使用
onDeployment()进行初始化 - 合约无法持有 BTC——它们是计算器,而非托管方
- 验证而非托管模式——将
Blockchain.tx.outputs与内部状态进行检查
装饰器参考
// Mark method as callable
@method({ name: 'param1', type: ABIDataTypes.UINT256 })
// Specify return types
@returns({ name: 'result', type: ABIDataTypes.UINT256 })
// Declare emitted events
@emit('Transfer', 'Approval')
存储类型
| 类型 | 用途 |
|---|---|
StoredU256 | 单个 u256 值 |
StoredBoolean | 布尔标志 |
StoredString | 字符串值 |
StoredMapU256 | 键值映射(u256 键/值) |
AddressMemoryMap | 内存中的地址映射 |
测试
所有代码都必须有测试
import { opnet, OPNetUnit, Assert, Blockchain } from '@btc-vision/unit-test-framework';
await opnet('My Tests', async (vm: OPNetUnit) => {
vm.beforeEach(async () => {
Blockchain.dispose();
await Blockchain.init();
});
await vm.it('should work correctly', async () => {
const result = await contract.someMethod();
Assert.expect(result).toEqual(expected);
});
});
插件开发
OPNet 节点就像 Minecraft 服务器一样——支持插件!
关键资源
- 阅读
docs/plugins-plugin-sdk-README.md - 阅读插件规范
docs/core-OIP-OIP-0003.md - 使用
templates/plugins/OP20Indexer.ts创建索引器
插件生命周期
| 钩子 | 调用时机 | 是否阻塞 |
|---|---|---|
onFirstInstall | 首次安装 | 是 |
onNetworkInit | 每次加载 | 是 |
onBlockChange | 确认新区块时 | 否 |
onReorg | 链重组时 | 是(关键) |
关键:处理重组
你必须实现 onReorg(),以回滚发生重组的区块对应的状态。否则会导致数据不一致。
前端开发
合约交互:始终使用 opnet npm 包 + ABI
**所有与 OPNet 合约交互的前端都必须使用 opnet npm 包及合约的 ABI 定义。**无一例外。不得使用原始 RPC,也不得手动编码。ABI 提供类型安全的方法调用、自动 calldata 编码/解码,以及妥善的错误处理。有关内置和自定义 ABI 定义,请参阅 ABI 参考文档(docs/core-opnet-abi-reference-*.md)。
配置规范
前端配置必须使用 docs/eslint-react.json 和 docs/tsconfig-generic.json:
- Vite + React
- 使用 Vitest 进行测试
- 使用 TypeScript 严格模式的 ESLint
- ESNext 目标
缓存(强制要求)
- 复用 provider 实例——每个网络使用一个单例
- 复用合约实例——按地址缓存
- 缓存 API 响应——在区块变更时使缓存失效
关键钩子
// OPNet provider
const { provider, isConnected, network } = useOPNet();
// Contract interaction
const { contract, callMethod, loading } = useContract(address, abi);
// Wallet connection
const { address, connect, disconnect, signPsbt } = useWallet();
完整集成指南请参阅 docs/frontend-motoswap-ui-README.md。
后端/API 开发
强制要求的框架
| 包 | 用途 |
|---|---|
@btc-vision/hyper-express | HTTP 服务端(速度最快) |
@btc-vision/uwebsocket.js | WebSocket 服务端(速度最快) |
禁止使用:Express、Fastify、Koa、Hapi——它们的速度明显更慢。
完整指南请参阅 docs/core-opnet-backend-api.md。
官方钱包:OP_WALLET(强制要求)
**OP_WALLET 是支持 OPNet 的主要官方钱包。**它由 OPNet 团队开发,提供了与 OPNet Bitcoin L1 智能合约生态系统交互所需的最完整功能。
**所有 OPNet dApp 都必须通过 @btc-vision/opwallet 集成 OP_WALLET。**这是不可妥协的要求。
全面支持 OPNet 为何必须使用 OP_WALLET
| 功能 | OP_WALLET | 其他钱包 |
|---|---|---|
| 官方支持 | 是 | 否 |
| MLDSA 签名 | 是 | 否 |
| 抗量子密钥 | 是 | 否 |
| 完整 OPNet 集成 | 是 | 部分支持 |
| 第一方更新 | 是 | 否 |
主要功能
- MLDSA(ML-DSA)签名:只有 OP_WALLET 支持抗量子的 ML-DSA 签名,这对保障 OPNet 的未来安全至关重要
- 全面支持地址系统:OP_WALLET 可正确处理 Bitcoin 地址(调整后的公钥)和 OPNet 地址(ML-DSA 公钥哈希)
- 原生 OPNet 功能:新功能发布后即可直接使用所有 OPNet 功能
- 优化的用户体验:专为 OPNet dApp 交互打造
集成
在前端安装 @btc-vision/opwallet 以集成钱包:
npm i @btc-vision/opwallet
同时使用 @btc-vision/walletconnect 提供连接界面:
import { WalletConnectProvider, useWalletConnect, SupportedWallets } from '@btc-vision/walletconnect';
// Wrap your app
<WalletConnectProvider theme="dark">
<App />
</WalletConnectProvider>
// In your component
const { connectToWallet, mldsaPublicKey, hashedMLDSAKey, publicKey, signMLDSAMessage } = useWalletConnect();
// Connect specifically to OP_WALLET for full feature support
connectToWallet(SupportedWallets.OP_WALLET);
// Use MLDSA signatures (OP_WALLET only)
if (mldsaPublicKey) {
const signature = await signMLDSAMessage('Your message');
}
// For Address.fromString — use hashedMLDSAKey (32-byte hash), NOT mldsaPublicKey (raw ~2500 bytes)
const senderAddress = Address.fromString(hashedMLDSAKey, publicKey);
安装
从 Chrome 网上应用店 安装 OP_WALLET
客户端库
| 包 | 文档 | 描述 |
|---|---|---|
@btc-vision/bitcoin | docs/clients-bitcoin-README.md | Bitcoin 库(PSBT 速度快 709 倍) |
@btc-vision/bip32 | docs/clients-bip32-README.md | HD 派生 + 抗量子 |
@btc-vision/ecpair | docs/clients-ecpair-README.md | EC 密钥对 |
@btc-vision/transaction | docs/core-transaction-README.md | OPNet 交易 |
opnet | docs/core-opnet-README.md | 主客户端库 |
@btc-vision/walletconnect | docs/clients-walletconnect-README.md | 钱包连接(OP_WALLET + UniSat) |
版本要求
| 工具 | 最低版本 |
|---|---|
| Node.js | >= 24.0.0 |
| TypeScript | >= 5.9.3 |
| ESLint | ^9.39.2 |
| @eslint/js | ^9.39.2 |
| AssemblyScript | @btc-vision/assemblyscript@^0.29.2(支持闭包的自定义分支) |
强制安装命令
对于非合约项目(前端、后端、插件、测试):
npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 @btc-vision/bitcoin@rc @btc-vision/transaction@rc opnet@rc @btc-vision/bip32 @btc-vision/ecpair --prefer-online
对于合约项目(AssemblyScript):
npx npm-check-updates -u && npm i eslint@^9.39.2 @eslint/js@^9.39.2 @btc-vision/[email protected] @btc-vision/assemblyscript@^0.29.2 @btc-vision/[email protected] @btc-vision/btc-runtime@rc --prefer-online
创建 package.json 后,始终运行相应命令。没有例外。
重要提醒
- 首先阅读 typescript-law——这是不可妥协的规则
- 核实项目配置——编写任何代码之前
- 禁止使用
any类型——任何时候都不行 - 测试所有内容——必须编写单元测试
- **处理