# 规格 · 合并后的 gang 内盘:V3 税收币 + 打新 - 日期:2026-10-27 Asia/Shanghai - 作者:Claude(产品规则 + 合约对应;**非执行单**) - 性质:把 V3 税收币和打新两套规则写在一处,按合并后的合约(一个内盘 `GangLaunchpad`、一个外盘处理合约 `GangTaxHandler`、一种代币 `GangToken`)。合约结构、旧函数的去向、审计修复的落点见 `docs/AUDIT-BRIEF-2026-10-26.md` §20。 - 已含的用户决定: - V3 内盘不收税,只收 1% 手续费;买卖税只在外盘收(审计说明 §19)。 - 金库删掉内盘分红路径,只保留外盘按交税时记账(审计说明 §19)。 - V3 和打新共用一个内盘地址、一个外盘处理合约、一个路由(审计说明 §20)。 - 取代关系:产品规则以本文为准。`SPEC-V3-TAX-RECIPIENT-2026-10-26.md`、`SPEC-FAIR-MINT-2026-10-26.md` 保留为设计记录(各自的需求来源、取舍和测试清单),其中「两个核心」「TaxHandlerV3 / HolderRewardHandler」的说法按本文 §7 理解。 - 文中【待定】是合并代码还没最终确定的点。 --- ## 0. 一句话 一个内盘合约上有两种发币方式: - **V3 税收币**:联合曲线,Agent 专属,单地址 1% 上限,约 15 BNB 毕业。发币时设买 / 卖税(各 1%–5%,或都不设)、分红比例和接收地址(发币人自己,或白名单里的合作方)。内盘每笔只收 1% 手续费;毕业后池子里每笔交易收税,平台拿成交额的 0.5%,剩下按比例分给持币人和接收地址。 - **打新**:没有曲线,所有人按同一个价格一份一份打,打满 10,000 份就开盘,开盘价等于打新价;打满前随时可以按原价卖回。开盘后每笔池子交易收 1% 税,一半给打新进来、一直没卖到出局线以下的地址,一半给平台。 两种币的外盘税都在卖出时顺带分块卖成报价币,持有人的分红在交税那一刻就按当时的份额记账。 --- ## 1. 两种币共用的规则 | 项 | 规则 | |---|---| | 内盘地址 | 一个 `GangLaunchpad`。`kindOf(token)`:0 = 不是本内盘的币,1 = V3,2 = 打新;创建时写一次,之后不变 | | 总量 | 每个币 10 亿枚,全部铸给内盘 | | 准入 | 发币、内盘买入、打新要持 .ai Agent ID(灵魂绑定 ERC-721)。V3 按当前的全局开关检查;打新按创建时快照的准入检查(之后改开关不影响已有 sale)。卖出、卖回、领取不要求 | | 报价币 | WBNB,或任意 18 位小数的 ERC-20;转账扣费的币、本平台发的币拒收(第一次拉钱时按实际到账校验) | | 换算 | V3 的毕业额、打新的募集额都按 BNB 等值定,创建时换算成报价币并锁定:QuoteRegistry 设了固定换算额就用它;否则用发币人给的参考池(Pancake V2 池读储备,V3 池读 30 分钟 TWAP),必要时经中转池换到 WBNB;参考池深度至少 1 BNB 等值。主网 USDT 默认用 Pancake V3 USDT/WBNB 0.01% 池 | | 毕业 / 发射前 | 代币锁转账,只能经内盘进出;钱包里看得到余额 | | 毕业 / 发射 | 解锁代币,代币和报价币经 SharedMigrator 加进 Pancake V2 池子,LP 打到 `0xdead`;池子里已有 LP 时拒绝。有税币再设一次 pair,之后池子买卖开始收税 | | 平台收入 | 全部进 `SharedPlatformFeeBucket`:80% 回购 GANG,20% 进金库 | | 路由 | 内盘白名单里的路由(`GangRouter`,或经多签列入的聚合器路由)可以替人买 / 打新(`buyFor` / `mintFor`,路由付钱)和卖 / 卖回(`sellFor` / `withdrawFor`,花用户给该路由的代币授权,所得给路由),但**只能替发起这笔交易的钱包**(`trader == tx.origin`,见 §9)。准入、上限按这个钱包算,与直接交易相同;买入的截断退款直接退给这个钱包(报价币 ERC-20)。`GangRouter` 无 owner:用原生 BNB 替调用者买 V3(`v3BuyBNB`)或打新(`fairMintBNB`),没有卖出 / 卖回入口。领取只能本人直接调。准入关闭时 `buyFor` / `mintFor` 直接回滚 | | 外盘税只对官方池子 | 税只在代币和它的 pair 之间的转账上收;钱包之间转账不收 | --- ## 2. V3 税收币 ### 2.1 发币参数(创建时定死,终身不可改) | 参数 | 范围 | 说明 | |---|---|---| | `buyTaxBps` | 100–500(1%–5%) | 池子买入税,只在毕业后收 | | `sellTaxBps` | 100–500(1%–5%) | 池子卖出税,只在毕业后收 | | `dividendBps` | 0–10,000 | 税扣掉平台部分后,分给持币人的比例 | | `recipient` | 发币人自己或白名单地址 | 剩下的税全部给它,用报价币结算;为 0 时 `dividendBps` 必须是 10,000 | | 无税币 | 四个字段全为 0 | 和 V2 无税轨一样,只收内盘 1% 手续费;永远不设 pair,转账从不调用外盘处理合约 | - 买卖税率分开设,可以不对称(例如买 1% / 卖 3%)。两个税率要么都在 1%–5%,要么都为 0。 - 接收地址可以是发币人自己(`create` 的调用者,不需要进白名单),也可以是 `GangTaxHandler` 白名单里的地址(owner = 多签,`list` / `delist`);其他第三方地址一律拒绝(`recipient not listed`)。下架只影响之后发的新币,已有的币照常给它打款。发币人当接收地址时,外盘分账、欠款和 `release` 与白名单地址完全一样(2026-10-27,AUDIT-BRIEF §22)。 - 预检查:`checkTaxConfigFor(creator, tax)` 按 `create` 的规则核对(接收地址等于 `creator` 时不查白名单);原来的 `checkTaxConfig(tax)` 保留(selector `0x68c9f7e0` 不变),它不知道发币人是谁,只认白名单。 ### 2.2 联合曲线(内盘) | 项 | 值 | |---|---| | 曲线上卖的数量 | 8 亿枚(`CURVE_SUPPLY`);毕业时剩下的代币和全部 `realQuote` 进池子 | | 毕业额 | 15 BNB 等值(`TARGET_GRAD_BNB`),创建时换算成报价币锁定;换算后低于下限时回滚 `GradAmtTooSmall` | | 单地址上限 | 1% 总量(1,000 万枚,`MAX_PER_WALLET`),可由 owner 全局关闭;超出部分同笔退回 | | 毕业 | `realQuote` 达到毕业额的那一笔买入截到正好毕业,多付的退回,同一笔交易里毕业 | | 内盘手续费 | **每笔买卖 1%**(`PROTOCOL_FEE_BPS`),当笔进 bucket;进 bucket 失败时整笔回滚。**不收税**:有税币和无税币在曲线上的价格完全一样 | - 买入:`quoteIn` 扣 1% 手续费,剩下 99% 进曲线。卖出:曲线付出的报价币扣 1% 手续费,其余给卖家。 - 内盘买卖时接收地址、金库、外盘处理合约都收不到任何报价币。`Buy` / `Sell` 事件里的 `tax` 字段保留,恒为 0。 - 有分红的币:内盘每笔买卖照常按成交后的余额写金库份额,所以毕业时的持有人从第一笔外盘税起就能分到。 ### 2.3 外盘税怎么分(毕业后,每笔池子交易) ``` 税 = 成交数量 × 买(或卖)税率 (代币,进 GangTaxHandler) 平台 = 成交额 × 0.5%,不超过税额 (按这一笔的方向和税率记账) 剩余 = 税 − 平台 分红 = 剩余 × dividendBps / 10,000 (交税时按当时份额记账,见 §5) 接收地址 = 剩余 − 分红 (卖成报价币后直接转) ``` | 例:买卖各 2%,分红 0 | 占成交额 | |---|---| | 内盘每笔合计 | 1%(只有手续费) | | 外盘:平台 | 0.5% | | 外盘:接收地址 | 1.5% | | 外盘每笔合计 | 2%(只有税) | - 税率不对称时,平台部分仍是每笔成交额的 0.5%:买 1% 时占税的一半,卖 3% 时占六分之一。 - 分红那份没有合格持有人时(没有人持仓超过 1 万枚),并入接收地址;没有接收地址时(`dividendBps = 10,000`)并入平台。 - 接收地址永远收报价币,不收本币。转账失败(报价币黑名单、合约拒收)不影响交易,记入 `owedOf[token]`;任何人都能调 `release(token)` 补转,补转仍失败就回滚、欠款保留。 - 事件:`TaxPaid`(每笔真正到账的打款,合作方统计收入只看它)、`TaxOwed`、`TaxDistributed(token, phase = 1, …)`、`TaxConfigured`(发币时)。 ### 2.4 分红资格(V3) - 份额 = 当前余额;余额 ≤ 1 万枚(金库门槛,严格大于才有份额)、被金库排除的地址、pair 地址的份额为 0。 - 卖出时先按卖出后的余额同步卖家份额,再记这笔税:卖家分不到自己这笔卖出的税;买家的份额在这笔税记账之后才增加:买家分不到自己这笔买入的税。 --- ## 3. 打新 ### 3.1 dev 可设的参数(平台上下限) | 参数 | 范围 | 默认 | 说明 | |---|---|---|---| | 募集额 `raiseBnb` | 5–50 BNB 等值 | 15 BNB | 打满即发射;创建时换算成报价币锁定 | | 单地址上限 `capBps` | 0.25%–1% 总量 | 0.5% | 最少参与地址数 = 50% ÷ 上限(1% → 50 个,0.5% → 100 个,0.25% → 200 个) | | 出局线 `holdBps` | 60%–90% | 60%(CLI) | 见 §3.5;1 份 × 60% = 30,000 枚,高于金库门槛 1 万枚 | | 打新费 `mintFeeBps` | 1%–3% | 1% | 本金之外另收,当笔进平台,不退 | | 撤出费 `exitFeeBps` | 1%–2% | 1% | 从卖回的本金里扣;停滞时为 0(§3.4) | | dev 首笔 `devShares` | 1 份到单地址上限 | 1 | 创建者在创建的同一笔交易里打 | ### 3.2 平台固定的参数 | 项 | 值 | |---|---| | 打新份额 | 5 亿枚(50%),分 10,000 份,每份 50,000 枚 | | 池子份额 | 5 亿枚(50%)+ 全部募集本金 | | 每份价格 | 募集额 ÷ 10,000(报价币计,创建时锁定);价格太小、分不成 10,000 份时回滚 `PriceTooSmall` | | 开盘价 | 等于打新价 | | 外盘税 | 买卖各 1%,固定 | ### 3.3 打新阶段 | 动作 | 规则 | 费用 | |---|---|---| | 打 `mint(token, shares, minShares)` | 累计不超过单地址上限;最后一笔自动截到剩余份数,只按成交份数收钱;成交份数少于 `minShares` 回滚 | 本金 + 打新费;打新费当笔进 bucket,失败整笔回滚(打新可以停) | | 卖回 `withdraw(token, shares, minPayout)` | 打满前随时可以;按打新价退本金,减撤出费;到手低于 `minPayout` 回滚 | 撤出费先试着进 bucket,失败不影响卖回,记为 `owedFee[quote]`,之后任何人 `flushFees(quote)` 补交(退出不能停) | | 打满 | 最后一份打出的那笔交易里直接发射 | — | - 没有截止时间、没有失败状态、没有退款。没打满的 sale 一直开着。 - 代币在打的时候就记到地址上;卖回时由内盘收回(只有打新币能这样,V3 币永远不能被收回)。 - 内盘按打到的代币数量写金库份额(打新时阻塞写);卖回时写份额失败不影响卖回,发 `ShareWriteFailed`,发射后 keeper 给这个地址补 `syncShares`。 ### 3.4 停滞 - sale 有一个计时 `progressAt`,从创建者那一笔开始。 - 只有当已售份数 `sold` 达到「上次重启计时时的份数 `markSold` + 10 份(`STALE_STEP`)」时,打新才重启计时。卖回不重启;只打 1 份、或别人撤出后再打回原来的份数,也不重启。 - 距 `progressAt` 满 7 天(`STALE_AFTER`,`>=`)后,这个 sale 算停滞:卖回不收撤出费,按打新价全额退本金(打新费仍不退)。停滞后任何人仍可以接着打,把它打满。 ### 3.5 发射后的 1% 税和出局线 | 去向 | 比例 | 说明 | |---|---|---| | 合格持有人 | 50%(= 成交额 0.5%) | 交税时按当时的合格份额记账(§5) | | 平台 | 50%(= 成交额 0.5%) | 进 bucket | 合格规则: | 项 | 规则 | |---|---| | 谁有资格 | 打新时打进来的地址;基准 = 发射时的数量(打到的份数 × 50,000 枚) | | 权重 | `min(当前余额, 基准)`,再过金库门槛(≤ 1 万枚为 0) | | 出局 | 余额一旦低于 `基准 × holdBps`,永久出局(`isOut`),之后买回来也不恢复;转到别的钱包、加池子都算 | | 已赚的 | 出局前已经记到名下的照样能领 | | 开盘后才买的、池子、平台合约、被金库排除的地址 | 没有份额 | | 一个合格地址都不剩时 | 持有人那一半也归平台 | - 卖出时先同步卖家份额(可能就此出局),再记这笔税:卖家分不到自己这笔卖出的税。 --- ## 4. 外盘税的卖出(两种币相同) 税以代币形式攒在 `GangTaxHandler`,分块卖成报价币: | 项 | 规则 | |---|---| | 卖出时顺带一块(`swapBack`) | 一笔收了税的卖单进池子之前,代币让 handler 先卖一块攒下的税。这一块 ≤ 这笔卖单的 25%(`SWAP_BACK_SELL_BPS`),最低成交额按同一调用里的储备算(下限 1%) | | 每区块额度 | 每个币每个区块一个额度:池子代币储备的 0.5%(`CHUNK_MAX_BPS`,按这个区块第一块时的储备),卖出顺带和 keeper 共用 | | 参考价 | 价格比这个区块的参考价低 2% 以上(`MAX_PRICE_DROP_BPS`)时这一块不卖 | | keeper 补位 | 没有卖单时 keeper 调 `process(token, amount, minQuoteOut)` 分块卖(数量截到 `maxChunk`,`minQuoteOut` 不能为 0) | | 买入 | 池子锁着,只攒不卖 | | 失败不影响交易 | 这一块自己失败时被吞掉,代币发 `SwapBackFailed`,卖出照常;只有调用方给的 gas 不够让这一块耗尽时整笔回滚(`SwapBackOutOfGas`),这样 gas 估算一定包含这一块 | | 池子有多出余额 | 先 skim 到 handler,卖完这一块原样转回 | | 闪电兑换写法的卖出 | 整笔回滚 `SellDuringFlashSwap` | | 下游拒收 | 金库拒绝兑现时,这批单位和报价币留在 handler,任何人 `flushHolders(token)` 补上;bucket 拒收时平台那份记 `owedPlatformOf`,任何人 `flushPlatform(quote)` 补交 | | 可卖数量 | handler 上这个币的余额减去欠接收地址的部分(报价币可能正是 handler 收税的币) | 报价:`previewSellNextBlock(token, amountIn)`(`previewSell` 是同一区块的版本)返回一笔卖单含这一块之后实际能拿到的报价币。 每卖一块,换回的报价币按这一块里各部分的比例拆: | | 平台 | 持有人 | 接收地址 | |---|---|---|---| | V3 | 交税时按成交额记的那部分 | 分红那部分 → 金库兑现 | 其余 → 直接转(失败记欠款) | | 打新 | 50% | 50% → 金库兑现 | — | --- ## 5. 分红记账(金库,两种币相同) 只有一种来路:外盘税的持有人那份(§19 起没有内盘分红)。 1. **交税时记单位**:每笔外盘税的持有人那份,当场按当时的份额记成税币单位(`accrueDividendUnits`)。当时没有任何份额时这一步回滚,handler 把这一份改给接收地址(V3)或平台(打新 / 没有接收地址的 V3)。 2. **分批**:单位进当前的 `open` 批次。handler 卖出时调 `convertDividend(token, units, quote)`:最老的未卖单位先算。没有在卖的批次时,`open` 批次转为 `selling`,新的单位进一个新的 `open` 批次。所以每个币最多两批未结算:一批在攒、一批在卖。 3. **结算**:一批的单位全部卖完,这一批按它实际换回的报价币结算:每个持有人拿到「自己在这一批的单位 × 这批的报价币 ÷ 这批的总单位」。跨很多已结算批次时用累计指数一步算完。放大系数 1e36,取整一律向下(零头留在金库)。 4. **持有人这边**:每个持有人最多记两批未结算单位。份额变动时先按旧份额结算到当前(`_harvestUnits`),新份额只影响之后的税。 5. **领取**:`claim(token)` 领走已结算的部分(报价币)。 - `pending(token, user)`:现在能领的报价币。 - `pendingUnits(token, user)`:已经记到名下、但这一批还没卖完的税币单位;CLI 按现价估算并标明是估算。 6. **门槛和排除**:门槛在币登记时快照(10,000 枚,严格大于);之后 owner 改门槛只影响新币。owner 可以排除合约地址(池子、锁仓合约等),不能排除普通钱包和 EIP-7702 委托钱包。排除后要在同一批交易里给这个地址 `syncShares(token, addr, 0)`。 7. **operator**:金库里一个内盘只绑一个 operator,就是 `GangTaxHandler`,只能绑一次。 --- ## 6. 内盘的钱和平台收入汇总 | 收入 | V3 | 打新 | |---|---|---| | 内盘 | 每笔买卖 1% 手续费(阻塞) | 打新费 1%–3%(阻塞)、撤出费 1%–2%(不阻塞,可欠) | | 外盘 | 成交额 0.5%(从税里出) | 成交额 0.5%(1% 税的一半) | | 接收地址 | 外盘:税 − 平台 − 分红 | — | | 持有人 | 外盘:分红比例 | 外盘:成交额 0.5% | 内盘托管:同一个报价币下,V3 曲线的 `realQuote` 和打新 sale 的本金都在内盘一个地址里。规则是: ``` 内盘持有的报价币 q = Σ 未发射 sale 的 (已售份数 × 每份价格) + 欠 bucket 的撤出费 owedFee[q] + Σ 未毕业曲线的 realQuote + 零头 ``` 打新卖回或发射动不到 V3 曲线的钱,V3 卖出或毕业动不到打新的本金。 --- ## 7. 合约对应 | 规则 | 合约 | |---|---| | 两种币的创建、内盘买卖 / 打新 / 卖回、毕业 / 发射、`kindOf`、`curves` / `sales` / `positions`、准入、路由白名单 | `GangLaunchpad`;V3 的逻辑在外部库 `CurveLogic`,打新的在 `FairLogic`(内盘 delegatecall 调用) | | 外盘税的分账、接收地址白名单、欠款与 `release`、打新的出局判断、分块卖出 | `GangTaxHandler`(继承 `PostGradHandlerBase`),按登记时的 `kind` 分派 | | 代币 | `GangToken`:三种币一份代码,由不可变量 `buyTaxBps` / `sellTaxBps` / `reclaimable` 区分 | | 分红记账 | `SharedDividendVault` | | 平台收入 | `SharedPlatformFeeBucket` | | 毕业 / 发射加池子 | `SharedMigrator` | | 固定换算额 | `QuoteRegistry` | | 用 BNB 买 / 打新、PancakeSwap 外盘买卖(`poolBuyBNB` / `poolBuy` / `poolSell` / `poolSellForBNB`) | `GangRouter`:透明代理,自身不存钱;升级由 Safe 经 48 小时 `TimelockController` 提出和执行(`ProxyAdmin` 的 owner 是时间锁) | 对外接口: - 两个 `create` 是重载,selector 和合并前一样:V3 `create(name, symbol, logoURI, metaURI, quote, refPool, (buyTaxBps, sellTaxBps, dividendBps, recipient))`,打新 `create(name, symbol, logoURI, metaURI, quote, refPool, (raiseBnb, capBps, holdBps, mintFeeBps, exitFeeBps), devShares)`。 - 事件名、参数、topic0 都和合并前一样;V3 `Created`(8 个参数)和打新 `Created`(13 个参数)都从内盘地址发出。 - 用错种类的币调用(例如对打新币调 `buy`)会回滚:`UnknownToken`(合约没有单独的 `WrongKind` 错误)。 - 代币新错误:`Untaxed`(对无税币设 pair)、`NotReclaimable`(对 V3 币收回)、`NotLocked`(发射后收回,或毕业 / 发射后 `pullFor`)。 - 替人交易(§9):`buyFor` / `mintFor`(原有)、`sellFor(trader, token, tokensIn, minQuoteOut)` `0x9d10cdb1`、`withdrawFor(trader, token, shares, minPayout)` `0x60382b4e`(新增);代币 `pullFor(owner, spender, amount)` `0x7d18ac71` 只有内盘能调;内盘新错误 `TraderNotOrigin`。 --- ## 8. owner 能做什么(多签) | 合约 | 能做 | 不能做 | |---|---|---| | `GangLaunchpad` | 路由白名单;准入开关和 Agent ID 合约;V3 单地址上限开关;V3 池子工厂、TWAP 窗口、中转池;pair 工厂(`setPairFactory`,只用于给之后新建的币的参考池定价);`setMigrator` 只能一次 | 改任何已有币的税率、分红比例、接收地址、毕业额、募集额、每份价格;动内盘托管的钱 | | `GangTaxHandler` | 接收地址白名单;keeper(`setKeeper` 一生一次,之后提议 → 48 小时 → 接受);换币路由(提议 → 48 小时 → 接受);可代替 keeper 调 `process`;`setLaunchpad` 只能一次 | 改已有币的路由;动欠接收地址的钱 | | `SharedDividendVault` | 新币的门槛;排除合约地址 | 给已绑定的内盘换 operator;排除普通钱包 | | `SharedPlatformFeeBucket` | operator、金库、回购合约、keeper(同上的时间锁)、7 天应急出口 | 自定义兑换路径(只有固定路径的 `collectPlatform`) | | `SharedMigrator` | 只能再加内盘 | 移除内盘 | | `QuoteRegistry` | 给报价币设固定换算额(只影响之后创建的币) | | | `GangRouter`(经 `TimelockController`) | 提出并在 48 小时后执行实现升级(提案、执行、取消都只给 Safe,时间锁没有 admin) | 跳过 48 小时;动用户的钱(路由不存钱,只花用户为某笔交易授权的数额) | 除 `GangRouter` 以外的合约都不可升级,也都没有暂停开关。出问题时能用的手段见运行手册 §9。 --- ## 9. 聚合器路由(替发起钱包交易) 钱包聚合器可以经自己的路由合约在 V3 曲线上买卖、在打新里打新 / 卖回。规则(合约细节和威胁模型见审计说明 §23): | 项 | 规则 | |---|---| | 谁能调 | 只有多签 `setRouter` 列入的路由;否则 `OnlyRouter` | | 替谁 | 只能替发起这笔交易的钱包(`trader == tx.origin`);否则 `TraderNotOrigin`。路由应把自己的 `msg.sender` 填作 trader,不要自己读 `tx.origin`。主网 Agent ID 持有者都是 EOA,发起钱包就是 Agent 钱包;合约钱包不能经路由交易,只能直接调内盘 | | 准入 / 上限 | 按 trader 算,与直接交易完全一样(V3 当前准入 + 1% 上限;打新按 sale 快照的准入 + 单地址上限)。卖回不查准入,与直接卖回一样 | | 买(`buyFor`) | 路由付钱,代币直接到 trader;上限 / 毕业截断的退款直接退给 trader(报价币 ERC-20,WBNB 报价时是 WBNB),不经路由。准入关闭时回滚 `RouterNeedsGate` | | 打新(`mintFor`) | 路由付 cost + 打新费,按实际接受份数精确拉取、没有退款,路由按 `previewMint` 付;份额和代币归 trader。sale 没快照准入时回滚 `RouterNeedsGate` | | 卖(`sellFor`)/ 卖回(`withdrawFor`) | 代币从 trader 直接拉进内盘,花的是 **trader 给这个路由的授权**(给内盘的授权不算);所得(扣手续费 / 撤出费后)给路由,由路由继续后面的路径;份额、持仓、事件记在 trader 名下 | | 代币侧 | `GangToken.pullFor` 只有内盘能调,只在毕业 / 发射前(锁定期)可用:那时没有 pair,不收税;之后永远回滚 | 为什么安全:白名单挡住未知合约;「只替发起钱包」挡住借列入的路由替别人买满上限、或把未来的池子当 trader 塞代币;卖出要求用户给这个路由的授权,挡住用户只是交互过的合约经列入路由把币卖掉。 但列入一个路由不再是无害的:以前它只能花自己的钱替人买,锁定期的代币谁也动不了;现在用户对它的授权能触及锁定期的代币。所以: - **聚合器接入**:四个入口的 `trader` 一律填路由自己的 `msg.sender`(内盘检查它等于 `tx.origin`),路由绝不自己读 `tx.origin`;卖出 / 卖回所得只付给这个 `msg.sender`。否则用户被骗调用的恶意合约可以经这个路由卖掉用户的币并拿走所得。 - **多签列入前的清单**(详见审计说明 §23.9):不列入会发起任意 / 调用者给定调用的合约(0x AllowanceHolder / Settler 一类、Multicall、通用执行器、4337 EntryPoint 等);要求 trader = `msg.sender`、所得 → `msg.sender`;源码已验证;可升级的路由看清升级权。发现问题立即 `setRouter(路由, false)`。