mcpField
L
launch-curve-audit

launch-curve-audit

github.com/setyamickala安全59292 次安装1 周前更新
代码仓库

关于 launch-curve-audit

launch-curve-audit 是一项面向代币发行前销售机制的审查技能,关注点不同于一般代币风险审计。它使用链上证据和对抗性调用顺序,检查定价、准入、托管、发行迁移及代币生成。

主要功能

  1. 01曲线定价与预算分析
  2. 02准入及身份门槛审查
  3. 03托管与退款检查
  4. 04发行迁移和 LP 保管审查
  5. 05基于分叉的约束测试

适用场景

  1. 01审查发行前的代币销售
  2. 02检查买方能否退出或退款
  3. 03将曲线定价与预期公式核对
  4. 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.env
references/
attack-surface.md
economics.md
invariants.md
scripts/
curve_math.py
rpc.py
snapshot.sh

详细介绍

启动曲线审计

启动曲线会以代币审计不会关注的方式失败:价格路径收费不足、预算消耗顺序错误、毕业时铸币进入无人能退出的池子,或者开盘价格在任何人交易之前就被操纵。资金也不属于持币者——它属于买家,且是通过出售合约而非转账流出的。

因此,这里的审计对象是出售机制:定价、准入、托管、毕业,以及与之后发行代币的机制之间的衔接。

针对 BSC 上由 Agent-ID 控制准入的线性 P→5P 曲线,是本方法建立时参考的形态,但方法并不限于这种形态——从合约读取参数,并对其编码的曲线进行建模。

范围

包含:出售或曲线合约、定价库、准入控制(白名单、注册表、Agent ID、每钱包及每身份预算)、托管账本(如有)、毕业/启动路径和 LP 托管、代币启动前的转账锁定和税费接线,以及启动时交接给的发行合约。

不包含:后端奖励逻辑(信任假设除外)、前端、部署脚本、AMM 本身,以及上市后的市场风险。说明跳过了其中哪些项目。

信息收集

接触链上数据前先收集以下信息;缺少时请提出:

  1. 链和地址(代币、出售合约、池子、账本、所有者)。对于 SPA,地址位于 JS 包中,而不是页面上——搜索 0x[a-fA-F0-9]{40},并查看周围的配置键。
  2. 曲线参数:起始价格、结束价格、供应上限、预算上限、每身份上限、持续时间。
  3. 准入模型:开放、白名单、注册表/Agent-ID、质押门槛。谁可以获准,以及由谁决定。
  4. 毕业:达到上限和截止时间时会发生什么、谁可以触发、LP 会去哪里。
  5. 启动后由什么机制发行代币,以及谁可以调用。
  6. 已验证源码(优先使用 Sourcify,其次使用 Etherscan v2 chainid=56)和已审计的提交。

即使只有地址、没有参数,也仍然可以审计——从构造函数参数和字节码中读取。不要只相信文档中的参数。

证据规则

按优先级从高到低排列;只有上一级证据无法取得时,才降级使用下一级:

  1. 固定区块上的字节码和链上状态。
  2. 已验证源码,仅限 exact_match。
  3. 对准确调用序列进行分叉模拟。
  4. 构造函数参数和部署交易。
  5. 文档、网站和 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 限制,销售就可能永远开放。
  • 部署后仍沿用快照。 重新读取并记录区块。

评论

支持 Markdown