关于 launch-curve-audit
launch-curve-audit 是一项面向代币发行前销售机制的审查技能,关注点不同于一般代币风险审计。它使用链上证据和对抗性调用顺序,检查定价、准入、托管、发行迁移及代币生成。
主要功能
- 01曲线定价与预算分析
- 02准入及身份门槛审查
- 03托管与退款检查
- 04发行迁移和 LP 保管审查
- 05基于分叉的约束测试
适用场景
- 01审查发行前的代币销售
- 02检查买方能否退出或退款
- 03将曲线定价与预期公式核对
- 04评估发行迁移条件及 LP 保管
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ setyamickala/ bsc-contract-audit-skill --skill launch-curve-audit安装后在 Claude Code 中输入/launch-curve-audit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
roles.example.envreferences/attack-surface.mdeconomics.mdinvariants.mdscripts/curve_math.pyrpc.pysnapshot.sh详细介绍
启动曲线审计
启动曲线会以代币审计不会关注的方式失败:价格路径收费不足、预算消耗顺序错误、毕业时铸币进入无人能退出的池子,或者开盘价格在任何人交易之前就被操纵。资金也不属于持币者——它属于买家,且是通过出售合约而非转账流出的。
因此,这里的审计对象是出售机制:定价、准入、托管、毕业,以及与之后发行代币的机制之间的衔接。
针对 BSC 上由 Agent-ID 控制准入的线性 P→5P 曲线,是本方法建立时参考的形态,但方法并不限于这种形态——从合约读取参数,并对其编码的曲线进行建模。
范围
包含:出售或曲线合约、定价库、准入控制(白名单、注册表、Agent ID、每钱包及每身份预算)、托管账本(如有)、毕业/启动路径和 LP 托管、代币启动前的转账锁定和税费接线,以及启动时交接给的发行合约。
不包含:后端奖励逻辑(信任假设除外)、前端、部署脚本、AMM 本身,以及上市后的市场风险。说明跳过了其中哪些项目。
信息收集
接触链上数据前先收集以下信息;缺少时请提出:
- 链和地址(代币、出售合约、池子、账本、所有者)。对于 SPA,地址位于 JS 包中,而不是页面上——搜索
0x[a-fA-F0-9]{40},并查看周围的配置键。 - 曲线参数:起始价格、结束价格、供应上限、预算上限、每身份上限、持续时间。
- 准入模型:开放、白名单、注册表/Agent-ID、质押门槛。谁可以获准,以及由谁决定。
- 毕业:达到上限和截止时间时会发生什么、谁可以触发、LP 会去哪里。
- 启动后由什么机制发行代币,以及谁可以调用。
- 已验证源码(优先使用 Sourcify,其次使用 Etherscan v2
chainid=56)和已审计的提交。
即使只有地址、没有参数,也仍然可以审计——从构造函数参数和字节码中读取。不要只相信文档中的参数。
证据规则
按优先级从高到低排列;只有上一级证据无法取得时,才降级使用下一级:
- 固定区块上的字节码和链上状态。
- 已验证源码,仅限
exact_match。 - 对准确调用序列进行分叉模拟。
- 构造函数参数和部署交易。
- 文档、网站和 medium 帖子,作为需要比对的声明。
硬性规则:
- 将每项声明都用 BNB 和代币单位量化。“便宜”但没有数字,不算发现。
- 仅通过阅读注释证明的曲线属性,视为未证实;应明确指出。
- 绝不根据出售页面评定出售机制。页面与字节码不一致——这种差距本身就是一项发现。
- 在取得 ABI 之前,应将不存在的函数报告为不存在,而不是缺失。扫描中未发现某个选择器,不代表它无法通过代理或库调用。
- 固定区块。没有区块号的快照只是传闻。
工具
export RPC_URL=https://bsc-rpc.publicnode.com
./scripts/snapshot.sh roles.env # lifecycle, supply, privilege, balances
python3 scripts/curve_math.py --p <wei> --supply <units> --check 1e18,100e18 [--budget <wei>]
scripts/rpc.py 打包用于一次性调用。snapshot.sh 还会输出所有者是 EOA 还是合约——这是决定审计结论的首要因素。curve_math.py 是一个参考积分:它可证明单调性、舍入方向,以及不存在按步骤计算后比闭式计算支付更少的情况。这些工具均为通用工具;roles.example.env 是输入模板。
流程
先对曲线建模,再逐一检查准入关卡。四个红色关卡都是硬性 BLOCK;所有者托管则是经典的 HOLD。下文的工作流步骤与各关卡一一对应。
flowchart TD
A[Intake & model curve<br/>pin block · owner type] --> G1{Gate 1 · exit<br/>buyers can exit / refund?}
G1 -->|no| B1[BLOCK · buyer funds trapped]
G1 -->|yes| G2{Gate 2 · curve<br/>cost = integral, cap safe?}
G2 -->|no| B2[BLOCK · undercharge / cap blowable]
G2 -->|yes| G3{Gate 3 · opening price<br/>not settable?}
G3 -->|no| B3[BLOCK · price settable: donate+sync]
G3 -->|yes| G4{Gate 4 · graduation<br/>anyone triggers, LP burned/locked?}
G4 -->|no| B4[BLOCK · LP recoverable]
G4 -->|yes| G5{Gate 5 · custody<br/>owner Safe, setters bounded?}
G5 -->|no| H1[HOLD · owner EOA / loose setters]
G5 -->|yes| D[Deep review<br/>coupling · admission · invariants]
D --> S[Verdict<br/>SHIP · HOLD · BLOCK]
S --> V[+ DECISION<br/>GO / CAUTION / BLOCK]
工作流
0. 在阅读代码前对经济机制建模
根据参数,先将预期曲线写成公式,不参考实现:price(x)、cost(a,b) = ∫price、上限、准入规则、毕业触发条件。
然后根据模型检查实现。两者不一致,要么是有文档说明的不同设计,要么就是审计发现——判断属于哪一种,并说明原因。
1. 固定部署信息
./scripts/snapshot.sh roles.env
python3 scripts/curve_math.py --p ... --supply ... --check ...
记录区块、所有者类型、上限、已售数量、已筹金额、截止时间和当前状态(tradingOpen、launched)。
三个一票否决项,任意一项都足以决定结论:所有者是 EOA;截止时间已过但尚未启动;达到上限但尚未毕业。
2. 为曲线定价
完整目录见 references/economics.md。以下四种问题会造成实际损失:
- 少收费。 成本向下取整,或将一次购买拆分成多步,实际支付会低于积分值。将
cost(n)与cost(n/2) * 2以及闭式解进行比较。 - 溢出。 在接近
2^96的供应量下,未使用 512 位mulDiv,却先乘后除。 - 上限准入。 达到或超过上限的购买必须按规则回退或截断;最后一个单位必须仍在预算范围内,不能悄然超额。
- 绕过预算。 每个身份的上限按钱包重置,或在转账后才记账。
很多销售没有 cost()/quote() 查询函数——价格是在可支付的购买函数中内联计算的。此时应通过状态变化(购买前后的 raised、sold)或在分叉环境中使用 value 对购买函数执行 eth_call 来定价,并记录所用方法。
3. 运行不变量检查
references/invariants.md 列出了通用检查项;根据步骤 0 编写项目专属检查项。在分叉环境中,通过对抗性调用顺序、已注资的参与者和重复身份来证明这些检查项。只在理想路径下成立的性质不算性质。
4. 模拟攻击者
references/attack-surface.md 按顺序介绍各个阶段。以下启动细节容易被通用审计遗漏:
- 捐赠后调用
sync(),以改变开盘价格或跳过 skim。 - 购买第一个单位,然后触发税费兑换或费用转换,使资金流入价差。
- 销售使用自身余额进行任何兑换时,设置
amountOutMin = 0。 - 托管账本中的退款重入,以及只递减一侧的退款记账。
- 抢跑上限:在最后一个区块狙击,关闭团队希望保持开放的销售。
- Gas 随条目数增长,导致奖励无法领取的领取路径。
5. 毕业与托管
达到上限或截止时间时,代码必须执行一项明确操作,任何人都能触发,且 LP 必须发送至销毁地址或经验证的锁仓合约。检查:启动是否受所有者、暂停状态或外部注册表读取限制?空销售是否也会毕业?LP 是否托管在中间地址?代币的交易锁是否在同一笔交易中解除?
6. 耦合
销售只是系统的一半。如果第二个合约在启动后铸造代币:
- 启动后的铸造是否受预算约束,还是只受
maxSupply约束? - 预算是单调递增地结转,还是每个周期都会重置?
- 铸币者列表能否锁定,所有者是否已从列表中移除?
- 达到上限时会怎样——领取路径会截断数量,还是会导致整个操作回退?
- 销售的所有者之后能否替换代币或排放合约?
7. 准入与身份
白名单和注册表门槛往往隐藏着内部人员分配。衡量以下内容:身份数量、每个身份的分配额、公布时间,以及谁有权添加身份。1,000 个单位的供应量对应 500 个钱包的白名单,意味着内部人员控制。tx.origin 门槛会导致智能钱包无法使用——应将其记录为行为而非漏洞,除非预期代理或多签钱包会参与。
8. 管理员与参数
列出路径上的每个 setter,并检查其值是否有 require 限制。未受限制的参数包括:提交费、税费、每钱包上限、曲线端点、交易对地址、费用接收方。可接受的参数包括:不可变代币、一次性设置的池、已锁定的铸币者列表。然后检查托管方式:带时间锁的 Safe,还是单个 EOA?活跃销售中由 EOA 持有所有权是一个否决项,而非通过项。
9. 结论
| 结论 | 条件 |
|---|---|
| BLOCK | 持有人/买家的资金无法退出、上限可能被突破、开盘价可被设定,或 LP 可被取回 |
| HOLD | 买家可以退出,曲线合理,但仍存在开放的管理权限路径,或有尚未证实的不变量 |
| SHIP | 曲线、不变量、毕业流程和托管均已证明;剩余事项均为 Info |
无论数量多少,以下情况都会限制结论:所有者是 EOA(HOLD);截止时间或启动受所有者权限控制(HOLD);超过一半的供应量分配给内部人员(HOLD);文档承诺退款,但缺少任何退款路径(BLOCK)。
集成与门控决策
此技能是上线前的决策层;它与 bsc-contract-audit 及 Binance Agentic Wallet 技能配合使用:
- 移交资产。 销售毕业后,对已启动的代币/NFT(TOKEN/MINT 路径)以及任何存款退款托管合约(LOCK 路径)运行
bsc-contract-audit。此技能涵盖销售机制;另一项技能涵盖资产和锁定机制。 - 门控钱包。 在买入新项目之前调用时,以钱包可读取的同一机器可读行作为结尾:
DECISION: GO | CAUTION | BLOCK — <one-line reason>
根据结论映射:SHIP → GO、HOLD → CAUTION(仅在用户确认未解决的限制后继续)、BLOCK → BLOCK(拒绝)。如果此审计结果与钱包自己的 query-token-audit 不一致,则采用较严格的结果。
报告格式
# Launch audit <SYMBOL> — curve launch, BSC 56 <ADDR>
Block <n> · source exact_match · owner EOA|Safe · cap <n> BNB · sold <n>/<cap>
Verdict: HOLD (caps: owner EOA, setAmmPair live)
DECISION: CAUTION — curve sound and buyer exits work, but owner is an EOA with a live setAmmPair
## Economics
price 0% <wei> -> 100% <wei> · integral vs stepped: <delta> · max purchase inside budget: yes/no
## Findings
| ID | Sev | Phase | Function | Trigger | Invariant |
|---|---|---|---|---|---|
| F-01 | High | curve | cost | step-split pays 0.4% less | INV-3 |
## Invariants
INV-1 ok · INV-3 FAIL · ...
## Unproven
## Recommendations
## Evidence log
E1 <command> -> <trimmed output>
## Snapshots
block, owner type, balances
每项发现都要注明 attack-surface.md 中的阶段、函数,以及它破坏的不变量。
严重性:Critical = 买家资金退出不安全或价格可被设定;High = 上限或预算可能被突破、LP 可被取回;Medium = 在现实触发条件下发生价值提取、启动活性问题;Low = 行为错误、注释不准确;Info = 卫生性问题。
结尾始终写上 Reference only, not investment advice.。
常见误读
- 售罄 = 健康。 检查最后一笔是否来自团队自己的钱包,以及曲线是否真的到达终点,还是仅耗尽了预算。
- 已放弃所有者权限 = 安全。 未锁定的发行合约中,已锁定的铸币者名单毫无意义。
- “仅 1% 税费。” 要看按什么金额计算、何时收取、由谁收取——阅读
_transfer。 - 捐赠不会造成影响。 捐赠会改变开盘价;捐赠者的损失并不是要报告的问题。
- 没有 getter = 没有该功能。
buyTax()回退通常意味着 getter 使用了不同名称。 - 测试通过 = 曲线合理。 分叉测试只能证明其断言的内容;自行核算价格。
- 毕业看似自动。 如果启动受
onlyOwner限制,销售就可能永远开放。 - 部署后仍沿用快照。 重新读取并记录区块。