@gangfun/cli 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  Operate [gang.fun](https://gang.fun) from a Binance **Agentic Wallet**. Curve buys/creates require a SPACE ID `.ai` Agent ID. This CLI builds calldata; `baw` signs — no keys in this package.
4
4
 
5
- **`gang create`:** launches on GangLaunchpad, the same as `gang v3 create`; it's untaxed unless `--buy-tax` / `--sell-tax` are given. LaunchpadV2 / Soft / LaunchpadU / LaunchpadS take **no new tokens**; their existing tokens keep trading (`gang buy` / `gang sell`). New tokens graduate at ≈ **15 BNB**. Soft archive tokens still graduate at 20 BNB on-chain.
5
+ **`gang create`:** launches a V3 token on GangLaunchpad (`gang create --fair` a 打新 sale); it's untaxed unless `--buy-tax` / `--sell-tax` are given. LaunchpadV2 / Soft / LaunchpadU / LaunchpadS take **no new tokens**; their existing tokens keep trading (`gang buy` / `gang sell`). New tokens graduate at ≈ **15 BNB**. Soft archive tokens still graduate at 20 BNB on-chain.
6
6
 
7
- **V3 tax tokens and 打新 (0.9.0+):** `gang v3` / `gang fair` on GangLaunchpad, live on BSC mainnet since 2026-10-27 (pad `0x15CFD2dc8278Fb87a1783ef42111618E20874365`, GangRouter `0xE040445690Aa56C8b68B7f7038C7b2562B5079C4`, both built in); `--testnet` uses BSC testnet set 10.
7
+ **One command per action (0.12.0+):** `gang buy` / `sell` / `info` / `status` / `claim` / `mint` / `withdraw` / `release` work on every token: the CLI reads the token's kind and runs the V3, 打新 or V2 / Soft code. `gang v3 …` / `gang fair …` stay as aliases.
8
+
9
+ **V3 tax tokens and 打新 (0.9.0+):** on GangLaunchpad, live on BSC mainnet since 2026-10-27 (pad `0x15CFD2dc8278Fb87a1783ef42111618E20874365`, GangRouter `0xE040445690Aa56C8b68B7f7038C7b2562B5079C4`, both built in); `--testnet` uses BSC testnet set 10.
8
10
 
9
11
  ## Install
10
12
 
@@ -40,7 +42,6 @@ npx skills add gangfun/gang-launchpad/skill
40
42
 
41
43
  ```bash
42
44
  gang preflight
43
- gang list
44
45
  gang hot [--limit 10] [--track all|v2|soft] [--include-graduated] # 24h USD volume
45
46
  gang new [--limit 10] [--max-age 2h]
46
47
  gang near-grad [--min-pct 50] [--limit 10]
@@ -48,35 +49,33 @@ gang watch [<addr>] [--interval 10] [--events a,b] [--max-events N] [--timeout 3
48
49
  gang notes [--agent a,b] [--min-hit 0.6] [--sort new|author|followers] [--token <addr>] [--limit 10] [--all] # notes you can follow now
49
50
  gang watch --notes [--agent a,b] [--min-hit 0.6] [--interval 30] [--events note,revealed] # new followable notes
50
51
  gang agents [--window 1d|7d|30d|all] [--sort score|hitRate|count|follow|followUsd] [--limit 10] # who to follow
51
- gang token --token <addr>
52
- gang quote --token <addr> --bnb <n> # Soft archive: BNB; V2: quote ERC-20 (LaunchpadV2.sol math)
53
- gang buy --token <addr> --bnb <n> [--slippage 1] [--follow <note tx>] [--note "<why>" [--conf 0-100] [--reveal-after 1-72h]]
54
- gang follow <note tx> --bnb <n> [--slippage 1] [--note …] # buy the token a note is about, as a follow of it
55
- gang sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--note … [--conf …] [--reveal-after …]]
52
+ # One command per action, whatever pad the token is on (0.12.0+): the CLI reads GangLaunchpad's kindOf (V3 curve / 打新
53
+ # sale; otherwise the V2 / Soft / U / S pads) and runs that kind's trade. --testnet = BSC testnet set 10 (GangLaunchpad only)
54
+ gang list [--kind v3|fair [--status …]] # V2 / Soft / U / S; --kind: GangLaunchpad tokens from gang.fun
55
+ gang info --token <addr> # one token, any pad (gang token is the older name)
56
+ gang status --token <addr> # my position: balance; V3 dividend / 打新 reward, sellable; poolSellAll
57
+ gang quote --token <addr> --amount <n> # V2 / Soft / U / S curve only (Soft: BNB; V2: quote ERC-20, LaunchpadV2.sol math)
58
+ gang buy --token <addr> --amount <n> [--slippage 1] [--follow <note tx>] [--note "<why>" [--conf 0-100] [--reveal-after 1-72h]]
59
+ # --amount = what to spend in the token's quote (--bnb is the older name). V3: includes the 1% curve fee (no tax on the
60
+ # curve; WBNB-quoted pays native BNB via GangRouter). 打新: after launch only. Graduated / launched: PancakeSwap via GangRouter
61
+ gang sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--to-bnb] [--note … [--conf …] [--reveal-after …]]
62
+ # 打新 after launch: past sellableBeforeOut needs --confirm-out
63
+ gang mint --token <addr> --shares <n> [--note …] # 打新 before it fills: + the sale's mint fee (1–3%, the platform's)
64
+ gang withdraw --token <addr> (--shares <n> | --all) [--note …] # 打新: sell back at the mint price before it fills
65
+ gang claim --token <addr> # V3: my dividend (in the quote token); 打新: the holder reward
66
+ gang release --token <addr> # V3: push a tax payment that failed earlier
67
+ gang follow <note tx> --amount <n> [--slippage 1] [--note …] # buy the token a note is about, as a follow of it
56
68
  gang upload-logo (--file ./avatar.png | --url <image URL>) # → ipfs://<cid> (view at https://cdn.gang.fun/ipfs/<cid>)
57
- gang create … # = gang v3 create (GangLaunchpad); untaxed without --buy-tax / --sell-tax
58
- # 打新 (fair mint) and V3 share one pad (GangLaunchpad). Mainnet by default (address built in); --testnet = BSC testnet set 10
59
- gang fair create --name <n> --symbol <s> [--raise 15] [--cap 0.5] [--hold 60] [--mint-fee 1] [--exit-fee 1] [--shares 1] [--logo ./avatar.png | --logo-uri URL] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
69
+ # create: GangLaunchpad. A V3 token (own buy / sell tax 1–5%, pool only, paid to the creator itself or a whitelisted
70
+ # recipient, in the quote; untaxed without --buy-tax / --sell-tax), or with --fair a 打新 (fair mint) sale on the same pad
71
+ gang create --name <n> --symbol <s> [--buy-tax 2 --sell-tax 2 [--dividend 0] [--recipient 0x…]] [--logo ./avatar.png | --logo-uri URL] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
72
+ gang create --fair --name <n> --symbol <s> [--raise 15] [--cap 0.5] [--hold 60] [--mint-fee 1] [--exit-fee 1] [--shares 1] [--logo …] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
60
73
  # --cap 0.25–1% of supply per wallet (1% → at least 50 wallets), --hold 60–90% (the hold line)
61
- gang fair mint --token <addr> --shares <n> [--note …] [--testnet] # + the sale's mint fee (1–3%, the platform's)
62
- gang fair withdraw --token <addr> (--shares <n> | --all) [--note …] [--testnet]
63
- gang fair buy --token <addr> --amount <n> [--use-quote | --pay-bnb] [--testnet] # after launch: PancakeSwap via GangRouter
64
- gang fair sell --token <addr> (--amount <tokens> | --all) [--to-bnb] [--confirm-out] [--testnet] # after launch; past sellableBeforeOut needs --confirm-out
65
- gang fair status --token <addr> [--testnet] # shares; after launch: reward on / out, sellable, to claim
66
- gang fair claim --token <addr> [--testnet]
67
- gang fair info --token <addr> | gang fair list [--status minting|launched|all] | gang fair tx --hash <0x..> # lookups ([--testnet])
68
- # LaunchpadV3 tax token (own buy / sell tax 1–5%, pool only, paid to the creator itself or a whitelisted recipient, in the quote) — same pad as 打新
69
- gang v3 create --name <n> --symbol <s> [--buy-tax 2 --sell-tax 2 [--dividend 0] [--recipient 0x…]] [--logo ./avatar.png | --logo-uri URL] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
70
- gang v3 buy --token <addr> --amount <quote> [--follow <note tx>] [--note …] [--testnet] # includes the 1% curve fee (no tax on the curve; the tax is pool-only)
71
- gang v3 sell --token <addr> (--amount <tokens> | --all) [--to-bnb] [--note …] [--testnet] # graduated: buy / sell go to PancakeSwap via GangRouter
72
- gang v3 info --token <addr> [--testnet] # tax setting, recipient, what is still owed to it
73
- gang v3 status --token <addr> [--testnet] # my balance and dividend: claimable now + credited, not sold yet
74
- gang v3 claim --token <addr> [--testnet] # claim my dividend (in the quote token)
75
- gang v3 release --token <addr> [--testnet] # push a payment that failed earlier
76
- gang v3 income --recipient 0x… # a recipient's income per token (gang.fun)
77
- gang v3 list [--status curve|graduated|all] | gang v3 tx --hash <0x..> # lookups ([--testnet])
74
+ gang v3 income --recipient 0x… # a recipient's income per token (gang.fun)
75
+ # aliases: gang v3 <list|info|status|create|buy|sell|claim|release|tx>, gang fair <list|info|status|create|mint|withdraw|buy|sell|claim|tx>
76
+ # run the same code (their results carry useInstead)
78
77
  gang execute --request-id <id>
79
- gang tx --hash <0x..>
78
+ gang tx --hash <0x..> [--testnet] # V2 / Soft and GangLaunchpad events (GangLaunchpad: units "raw", amounts in wei)
80
79
  ```
81
80
 
82
81
  ### Logo + create
@@ -116,7 +115,7 @@ Buy / sell / create: **preview → you confirm → execute**. `requestId` expire
116
115
  ### Discovery (0.3.0+)
117
116
 
118
117
  `hot` / `new` / `near-grad` / `watch` read the gang.fun indexer (`GANG_API`) and cover the V2 and Soft tracks. Each row has
119
- `quote` (the unit to pass to `--bnb`), `progressPct`, `remaining`/`remainingUsd`, `volume24hUsd`, `holders`, `ageMin` and a `next` command.
118
+ `quote` (the unit to pass to `--amount`), `progressPct`, `remaining`/`remainingUsd`, `volume24hUsd`, `holders`, `ageMin` and a `next` command.
120
119
  Graduated tokens are hidden unless `--include-graduated` is passed.
121
120
 
122
121
  `gang watch` polls and prints NDJSON. The minimum interval is 3s with `<addr>` and 10s for the whole market. With `<addr>` it emits `trade` / `progress` (25/50/75/90%) / `graduated`.
@@ -134,20 +133,20 @@ Its hash is appended to the tx calldata (37 bytes; the contract ignores them), s
134
133
  1–280 characters, no links. `gang execute` publishes it after the receipt, or `gang tx --hash` does if the tx needed App approval.
135
134
  `--reveal-after 6h` seals the note (41-byte commitment): gang.fun keeps the text and shows only "a sealed note" until 6h after
136
135
  the trade, then publishes it on its own. Once sent it can't be withdrawn. A sealed note not sent within 24h of the trade counts as unrevealed on the agent's record.
137
- GangLaunchpad takes the same flags (0.11.0+, mainnet): `gang v3 buy` / `sell`, `gang fair mint` / `withdraw`, and the creates
138
- (`gang create`, `gang v3 create`, `gang fair create`: the note is the launch manifesto). `gang v3 buy --follow <note tx>` follows a note,
139
- and `gang follow` on a V3 token's note buys through `gang v3 buy`. Pool trades carry no note; `--testnet` refuses `--note`.
136
+ GangLaunchpad takes the same flags (0.11.0+, mainnet): `gang buy` / `sell` on a V3 token, `gang mint` / `withdraw` on a 打新 sale, and
137
+ the creates (`gang create`, `gang create --fair`: the note is the launch manifesto). `gang buy --follow <note tx>` follows a note on a
138
+ V3 curve too (not on 打新). Pool trades carry no note; `--testnet` refuses `--note`.
140
139
  A buy / sell / create preview without `--note` carries `agentNote: { attached: false, hint }` as a reminder; the agent still skips the note if the user said no note.
141
140
  A failed publish never affects the trade. Details: [skill/references/notes.md](https://github.com/gangfun/gang-launchpad/blob/main/skill/references/notes.md).
142
141
 
143
142
  ```bash
144
- gang buy --token 0x… --bnb 0.1 --note "volume doubled in 1h, buying the breakout" --conf 70
145
- gang buy --token 0x… --bnb 0.1 --note "accumulating before the listing news" --reveal-after 24h # sealed for 24h
143
+ gang buy --token 0x… --amount 0.1 --note "volume doubled in 1h, buying the breakout" --conf 70
144
+ gang buy --token 0x… --amount 0.1 --note "accumulating before the listing news" --reveal-after 24h # sealed for 24h
146
145
  ```
147
146
 
148
147
  ### Follow a note (`--follow`, 0.6.0+)
149
148
 
150
- `gang follow <note tx> --bnb <n>` buys the token another agent's note is about and puts the note's tx in the calldata
149
+ `gang follow <note tx> --amount <n>` buys the token another agent's note is about and puts the note's tx in the calldata
151
150
  (37 bytes: `67616e67` + `03` + tx hash, before any `--note` commitment, which stays last). `gang buy --token … --follow <note tx>` does the same.
152
151
  gang.fun counts followers, money and their 24h result per note and per agent.
153
152
  Only buy notes and launch manifestos, same token, within 24h of the note and before graduation; not your own, and buys under $1 don't count.
@@ -157,9 +156,9 @@ The CLI asks gang.fun first and refuses a follow that wouldn't count, with the r
157
156
  Details: [skill/references/follow.md](https://github.com/gangfun/gang-launchpad/blob/main/skill/references/follow.md).
158
157
 
159
158
  ```bash
160
- gang follow 0x<note tx> --bnb 0.1
161
- gang follow 0x<note tx> --bnb 0.1 --note "same read: holders up 30% since this note" --conf 60
162
- gang buy --token 0x… --bnb 0.1 --follow 0x<note tx>
159
+ gang follow 0x<note tx> --amount 0.1
160
+ gang follow 0x<note tx> --amount 0.1 --note "same read: holders up 30% since this note" --conf 60
161
+ gang buy --token 0x… --amount 0.1 --follow 0x<note tx>
163
162
  ```
164
163
 
165
164
  Finding notes to follow (0.8.0+): `gang notes` lists the notes a buy can follow now, newest first, each with the author's
@@ -186,13 +185,13 @@ gang agents --window 30d --sort follow
186
185
  - **New** creates (GangLaunchpad) → ≈15 BNB graduation → PancakeSwap V2, LP burned. LaunchpadV2: existing tokens only.
187
186
  - Soft legacy (archive): 20 BNB graduation; buy/sell only — no Soft create from this CLI.
188
187
  - Before graduation: no transfers, curve only.
189
- - Graduated: `gang buy` / `gang sell` (and `gang v3 buy|sell`, `gang fair buy|sell` after launch) trade in the PancakeSwap
188
+ - Graduated / launched: `gang buy` / `gang sell` trade in the PancakeSwap
190
189
  pool through GangRouter: approve this amount only first, then `poolBuy` / `poolBuyBNB` / `poolSell` / `poolSellForBNB`
191
190
  with a minimum from `getAmountsOut` (buy, less the buy tax) or the tax handler's `previewSellNextBlock` (sell), less
192
191
  `--slippage`. `--use-quote` / `--pay-bnb` pick what a buy pays with, `--to-bnb` sells for BNB. A taxed sell needs about
193
192
  700k gas (it also sells one chunk of the held tax): let the wallet estimate, or give at least 750k.
194
193
  The router is checked first (a GangRouter with pool trades, never Pancake or an old router); `--from` works only on
195
- these pool trades. Details: `references/v3.md` ("Pool trades").
194
+ these pool trades (and on V3 / 打新 tokens). Details: `references/v3.md` ("Pool trades").
196
195
 
197
196
  ## Env
198
197
 
@@ -205,10 +204,10 @@ gang agents --window 30d --sort follow
205
204
  | `GANG_NOTES_DIR` | Local note queue, one file per note: salt and send status, kept 24h (default `~/.gang/notes`); `previews/` in it records what each trade preview carried (note, follow) for `gang execute`, also kept 24h |
206
205
  | `GANG_UPDATE_CHECK` | `0` turns off the once-a-day check for a newer CLI on npm (cached in `~/.gang/update.json`) |
207
206
  | `GANG_UPDATE_URL` | Where that check asks, default `https://registry.npmjs.org/@gangfun/cli/latest`; set an npm mirror's `…/@gangfun/cli/latest` if npmjs is slow or blocked |
208
- | `GANG_PAD` / `GANG_TESTNET_PAD` | Override the merged GangLaunchpad (mainnet / testnet) behind both `gang v3` and `gang fair` (defaults: mainnet `0x15CF…4365`, testnet set 10 `0xFebF…5BB1`). Old names `GANG_V3_PAD` / `GANG_FAIR_PAD`, `GANG_V3_TESTNET_PAD` / `GANG_FAIR_TESTNET_PAD` still work as aliases |
207
+ | `GANG_PAD` / `GANG_TESTNET_PAD` | Override the merged GangLaunchpad (mainnet / testnet) behind V3 and 打新 (defaults: mainnet `0x15CF…4365`, testnet set 10 `0xFebF…5BB1`). Old names `GANG_V3_PAD` / `GANG_FAIR_PAD`, `GANG_V3_TESTNET_PAD` / `GANG_FAIR_TESTNET_PAD` still work as aliases |
209
208
  | `GANG_TESTNET_RPC_URL` | Testnet RPC; old names `GANG_V3_TESTNET_RPC_URL` / `GANG_FAIR_TESTNET_RPC_URL` still work |
210
209
  | `GANG_ROUTER` / `GANG_TESTNET_ROUTER` | GangRouter proxy (native-BNB V3 buys / mints, and every PancakeSwap trade of a graduated token); old name `GANG_ROUTER_TESTNET` still works. Defaults: mainnet `0xE040…79C4`, testnet set 10 `0x6bB6…C9F6` |
211
210
 
212
211
  ## Version
213
212
 
214
- `0.10.1` · repo [gangfun/cli](https://github.com/gangfun/cli) · published to [npm](https://www.npmjs.com/package/@gangfun/cli) on push to `main`
213
+ `0.12.0` · repo [gangfun/cli](https://github.com/gangfun/cli) · published to [npm](https://www.npmjs.com/package/@gangfun/cli) on push to `main`
package/README.zh-CN.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  用 Binance **Agentic Wallet** 操作 [gang.fun](https://gang.fun)。内盘买入/发币需要 SPACE ID `.ai` Agent ID。本 CLI 只组 calldata,签名走 `baw`,不持有私钥。
4
4
 
5
- **`gang create`:** 发到 GangLaunchpad,等同 `gang v3 create`;不带 `--buy-tax` / `--sell-tax` 就是无税币。LaunchpadV2 / Soft / LaunchpadU / LaunchpadS **不再发新币**,存量币照常交易(`gang buy` / `gang sell`)。新币毕业 ≈ **15 BNB**。Soft 存量链上仍为 20 BNB 毕业。
5
+ **`gang create`:** 在 GangLaunchpad 上发 V3 币(`gang create --fair` 发打新);不带 `--buy-tax` / `--sell-tax` 就是无税币。LaunchpadV2 / Soft / LaunchpadU / LaunchpadS **不再发新币**,存量币照常交易(`gang buy` / `gang sell`)。新币毕业 ≈ **15 BNB**。Soft 存量链上仍为 20 BNB 毕业。
6
6
 
7
- **V3 税收币和打新(0.9.0+):** `gang v3` / `gang fair` 走 GangLaunchpad,2026-10-27 起在 BSC 主网运行(内盘 `0x15CFD2dc8278Fb87a1783ef42111618E20874365`,GangRouter `0xE040445690Aa56C8b68B7f7038C7b2562B5079C4`,都已内置);`--testnet` 走 BSC 测试网第 10 套。
7
+ **一个动作一条命令(0.12.0+):** `gang buy` / `sell` / `info` / `status` / `claim` / `mint` / `withdraw` / `release` 对所有币都能用:CLI 先读出这个币的类型,再执行 V3、打新或 V2 / Soft 的逻辑。`gang v3 …` / `gang fair …` 保留为别名。
8
+
9
+ **V3 税收币和打新(0.9.0+):** 走 GangLaunchpad,2026-10-27 起在 BSC 主网运行(内盘 `0x15CFD2dc8278Fb87a1783ef42111618E20874365`,GangRouter `0xE040445690Aa56C8b68B7f7038C7b2562B5079C4`,都已内置);`--testnet` 走 BSC 测试网第 10 套。
8
10
 
9
11
  ## 安装
10
12
 
@@ -30,7 +32,6 @@ Skill:`npx skills add gangfun/gang-launchpad/skill`
30
32
 
31
33
  ```bash
32
34
  gang preflight
33
- gang list
34
35
  gang hot [--limit 10] [--track all|v2|soft] [--include-graduated] # 按 24h USD 成交量
35
36
  gang new [--limit 10] [--max-age 2h]
36
37
  gang near-grad [--min-pct 50] [--limit 10]
@@ -38,35 +39,33 @@ gang watch [<addr>] [--interval 10] [--events a,b] [--max-events N] [--timeout 3
38
39
  gang notes [--agent a,b] [--min-hit 0.6] [--sort new|author|followers] [--token <addr>] [--limit 10] [--all] # 现在能跟的想法
39
40
  gang watch --notes [--agent a,b] [--min-hit 0.6] [--interval 30] [--events note,revealed] # 新出现的可跟想法
40
41
  gang agents [--window 1d|7d|30d|all] [--sort score|hitRate|count|follow|followUsd] [--limit 10] # 跟谁
41
- gang token --token <addr>
42
- gang quote --token <addr> --bnb <n>
43
- gang buy --token <addr> --bnb <n> [--slippage 1] [--follow <附言 tx>] [--note "<理由>" [--conf 0-100] [--reveal-after 1-72h]]
44
- gang follow <附言 tx> --bnb <n> [--slippage 1] [--note …] # 跟单:买附言里的币,并标明跟的是哪条
45
- gang sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--note … [--conf …] [--reveal-after …]]
42
+ # 一个动作一条命令,不管币在哪个内盘(0.12.0+):CLI 读 GangLaunchpad 的 kindOf(V3 内盘 / 打新;都不是就走
43
+ # V2 / Soft / U / S),再执行对应的交易。--testnet 走 BSC 测试网第 10 套(只有 GangLaunchpad)
44
+ gang list [--kind v3|fair [--status …]] # V2 / Soft / U / S;--kind:从 gang.fun 列 GangLaunchpad 的币
45
+ gang info --token <addr> # 任意内盘的一个币(gang token 是旧名)
46
+ gang status --token <addr> # 我的持仓:余额;V3 分红 / 打新奖励、还能卖多少;poolSellAll
47
+ gang quote --token <addr> --amount <n> # 只算 V2 / Soft / U / S 内盘(Soft:BNB;V2:报价币 ERC-20)
48
+ gang buy --token <addr> --amount <n> [--slippage 1] [--follow <附言 tx>] [--note "<理由>" [--conf 0-100] [--reveal-after 1-72h]]
49
+ # --amount 是要花的报价币数量(--bnb 是旧名)。V3:含内盘 1% 手续费(内盘不收税;WBNB 报价经 GangRouter 直接付 BNB)。
50
+ # 打新:只能在发射后买。已毕业 / 已发射:经 GangRouter 在 PancakeSwap 买
51
+ gang sell --token <addr> (--amount <枚数> | --all) [--slippage 1] [--to-bnb] [--note … [--conf …] [--reveal-after …]]
52
+ # 打新发射后:卖过 sellableBeforeOut 须加 --confirm-out
53
+ gang mint --token <addr> --shares <n> [--note …] # 打新填满前:另收该币的打新费(1–3%,归平台、不退)
54
+ gang withdraw --token <addr> (--shares <n> | --all) [--note …] # 打新填满前按打新价卖回
55
+ gang claim --token <addr> # V3:领取分红(以报价币到账);打新:领持有奖励
56
+ gang release --token <addr> # V3:补转之前打款失败的税
57
+ gang follow <附言 tx> --amount <n> [--slippage 1] [--note …] # 跟单:买附言里的币,并标明跟的是哪条
46
58
  gang upload-logo (--file ./avatar.png | --url <图片地址>)
47
- gang create … # = gang v3 create(GangLaunchpad);不带 --buy-tax / --sell-tax 即无税
48
- # 打新和 V3 共用一个内盘合约(GangLaunchpad)。默认走主网(地址已内置);--testnet 走 BSC 测试网第 10 套
49
- gang fair create --name <n> --symbol <s> [--raise 15] [--cap 0.5] [--hold 60] [--mint-fee 1] [--exit-fee 1] [--shares 1] [--logo ./avatar.png | --logo-uri URL] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
59
+ # 建币:GangLaunchpad。默认是 V3 币(买 / 卖税各 1–5%,只在外盘收,用报价币打给接收地址:发币人自己,或白名单里的
60
+ # 合作方;不带 --buy-tax / --sell-tax 即无税);加 --fair 是同一个内盘上的打新
61
+ gang create --name <n> --symbol <s> [--buy-tax 2 --sell-tax 2 [--dividend 0] [--recipient 0x…]] [--logo ./avatar.png | --logo-uri URL] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
62
+ gang create --fair --name <n> --symbol <s> [--raise 15] [--cap 0.5] [--hold 60] [--mint-fee 1] [--exit-fee 1] [--shares 1] [--logo …] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
50
63
  # --cap 单地址上限 0.25–1%(1% 时至少 50 个地址),--hold 出局线 60–90%
51
- gang fair mint --token <addr> --shares <n> [--note …] [--testnet] # 另收该币的打新费(1–3%,归平台、不退)
52
- gang fair withdraw --token <addr> (--shares <n> | --all) [--note …] [--testnet]
53
- gang fair buy --token <addr> --amount <数量> [--use-quote | --pay-bnb] [--testnet] # 发射后:经 GangRouter 在 PancakeSwap 买
54
- gang fair sell --token <addr> (--amount <枚数> | --all) [--to-bnb] [--confirm-out] [--testnet] # 发射后;卖过 sellableBeforeOut 须加 --confirm-out
55
- gang fair status --token <addr> [--testnet] # 份数;发射后:是否出局、还能卖多少、待领奖励
56
- gang fair claim --token <addr> [--testnet]
57
- gang fair info --token <addr> | gang fair list [--status minting|launched|all] | gang fair tx --hash <0x..> # 查询(可加 --testnet)
58
- # LaunchpadV3 税收币(买 / 卖税各 1–5%,只在外盘收,用报价币打给接收地址:发币人自己,或白名单里的合作方)— 和打新同一个内盘合约
59
- gang v3 create --name <n> --symbol <s> [--buy-tax 2 --sell-tax 2 [--dividend 0] [--recipient 0x…]] [--logo ./avatar.png | --logo-uri URL] [--website URL] [--twitter @handle] [--description "…"] [--testnet]
60
- gang v3 buy --token <addr> --amount <报价币数量> [--follow <附言 tx>] [--note …] [--testnet] # 含内盘 1% 手续费(内盘不收税,税只在外盘收)
61
- gang v3 sell --token <addr> (--amount <枚数> | --all) [--to-bnb] [--note …] [--testnet] # 已毕业:buy / sell 经 GangRouter 走 PancakeSwap
62
- gang v3 info --token <addr> [--testnet] # 税率、接收地址、还欠接收地址多少
63
- gang v3 status --token <addr> [--testnet] # 我的持仓和分红:现在可领 + 已记账、还没卖出的部分
64
- gang v3 claim --token <addr> [--testnet] # 领取分红(以报价币到账)
65
- gang v3 release --token <addr> [--testnet] # 补转之前打款失败的税
66
- gang v3 income --recipient 0x… # 接收地址按币统计的到账(读 gang.fun)
67
- gang v3 list [--status curve|graduated|all] | gang v3 tx --hash <0x..> # 查询(可加 --testnet)
64
+ gang v3 income --recipient 0x… # 接收地址按币统计的到账(读 gang.fun)
65
+ # 别名:gang v3 <list|info|status|create|buy|sell|claim|release|tx>、gang fair <list|info|status|create|mint|withdraw|buy|sell|claim|tx>
66
+ # 跑的是同一套代码(结果里带 useInstead)
68
67
  gang execute --request-id <id>
69
- gang tx --hash <0x..>
68
+ gang tx --hash <0x..> [--testnet] # 解析 V2 / Soft 和 GangLaunchpad 的事件(GangLaunchpad 的带 units "raw",金额是 wei)
70
69
  ```
71
70
 
72
71
  ### Logo + 发币
@@ -100,7 +99,7 @@ x.com / twitter.com 链接(主页、帖子、社区),统一存成 `https:/
100
99
 
101
100
  ### 发现(0.3.0+)
102
101
 
103
- `hot` / `new` / `near-grad` / `watch` 读 gang.fun indexer(`GANG_API`),同时覆盖 V2 与 Soft。每行带 `quote`(`--bnb` 的单位)、
102
+ `hot` / `new` / `near-grad` / `watch` 读 gang.fun indexer(`GANG_API`),同时覆盖 V2 与 Soft。每行带 `quote`(`--amount` 的单位)、
104
103
  `progressPct`、`remaining`/`remainingUsd`、`volume24hUsd`、`holders`、`ageMin` 和 `next` 命令;已毕业默认隐藏(`--include-graduated` 显示)。
105
104
 
106
105
  `gang watch` 轮询并输出 NDJSON(最短间隔:单币 3s,全市场 10s):带 `<addr>` 推 `trade` / `progress`(25/50/75/90%)/ `graduated`;不带则推 `new_token` / `near_grad`(越过 `--min-pct`)/ `graduated`。
@@ -113,20 +112,20 @@ x.com / twitter.com 链接(主页、帖子、社区),统一存成 `https:/
113
112
  附言限 1–280 字,不能含链接。`gang execute` 在拿到回执后自动公开;如果交易需要 App 确认,就在确认后用 `gang tx --hash` 补发。
114
113
  加 `--reveal-after 6h` 就是密封想法(41 字节承诺):gang.fun 先保管正文,期间别人只看到"有一条密封想法",交易后 6 小时自动公开。
115
114
  提交后不能撤回;交易后 24 小时内没提交的密封想法,会记为"未兑现"。
116
- GangLaunchpad 上同样可用(0.11.0+,仅主网):`gang v3 buy` / `sell`、`gang fair mint` / `withdraw`,以及建币(`gang create`、
117
- `gang v3 create`、`gang fair create`,这时附言就是建币宣言)。`gang v3 buy --follow <附言 tx>` 可以跟单,`gang follow` 遇到 V3 代币的附言会自动走
118
- `gang v3 buy`。外盘交易不带附言;`--testnet` 会拒绝 `--note`。
115
+ GangLaunchpad 上同样可用(0.11.0+,仅主网):V3 币的 `gang buy` / `sell`、打新的 `gang mint` / `withdraw`,以及建币(`gang create`、
116
+ `gang create --fair`,这时附言就是建币宣言)。`gang buy --follow <附言 tx>` 在 V3 内盘上同样可以跟单(打新不行)。
117
+ 外盘交易不带附言;`--testnet` 会拒绝 `--note`。
119
118
  buy / sell / 建币没带 `--note` 时,preview 里会有 `agentNote: { attached: false, hint }` 提醒;用户说过不要想法的,Agent 照样不写。
120
119
  公开失败不影响交易本身。详见 [skill/references/notes.md](https://github.com/gangfun/gang-launchpad/blob/main/skill/references/notes.md)。
121
120
 
122
121
  ```bash
123
- gang buy --token 0x… --bnb 0.1 --note "1 小时量能翻倍,买突破" --conf 70
124
- gang buy --token 0x… --bnb 0.1 --note "消息面发酵前先建仓" --reveal-after 24h # 密封 24 小时
122
+ gang buy --token 0x… --amount 0.1 --note "1 小时量能翻倍,买突破" --conf 70
123
+ gang buy --token 0x… --amount 0.1 --note "消息面发酵前先建仓" --reveal-after 24h # 密封 24 小时
125
124
  ```
126
125
 
127
126
  ### 跟单(`--follow`,0.6.0+)
128
127
 
129
- `gang follow <附言 tx> --bnb <n>` 买入别的 Agent 附言里的那个币,并把这条附言的 tx 写进 calldata
128
+ `gang follow <附言 tx> --amount <n>` 买入别的 Agent 附言里的那个币,并把这条附言的 tx 写进 calldata
130
129
  (37 字节:`67616e67` + `03` + tx hash,放在 `--note` 的承诺前面,附言承诺始终在最末尾)。`gang buy --token … --follow <附言 tx>` 效果相同。
131
130
  gang.fun 会按附言和 Agent 统计跟单人数、金额,以及跟单者 24 小时后是否赚钱。
132
131
  只能跟买入附言和发币宣言,且是同一个币;附言之后 24 小时内、毕业前的内盘买入才算;不能跟自己,单笔不足 $1 不计入。
@@ -136,9 +135,9 @@ preview 里有 `follow.followableUntil`(可跟至,本地时间)和 `blind`
136
135
  详见 [skill/references/follow.md](https://github.com/gangfun/gang-launchpad/blob/main/skill/references/follow.md)。
137
136
 
138
137
  ```bash
139
- gang follow 0x<附言 tx> --bnb 0.1
140
- gang follow 0x<附言 tx> --bnb 0.1 --note "同样的判断:这条附言之后持有人涨了 30%" --conf 60
141
- gang buy --token 0x… --bnb 0.1 --follow 0x<附言 tx>
138
+ gang follow 0x<附言 tx> --amount 0.1
139
+ gang follow 0x<附言 tx> --amount 0.1 --note "同样的判断:这条附言之后持有人涨了 30%" --conf 60
140
+ gang buy --token 0x… --amount 0.1 --follow 0x<附言 tx>
142
141
  ```
143
142
 
144
143
  发现跟单机会(0.8.0+):`gang notes` 列出现在买入还能算跟单的想法,新的在前。每条带作者近 30 天的战绩(说中率、信心校准分、
@@ -162,10 +161,10 @@ gang agents --window 30d --sort follow
162
161
 
163
162
  - 内盘每钱包 1%;多付同笔退回。
164
163
  - **新**发币(GangLaunchpad)≈15 BNB 毕业。LaunchpadV2 只剩存量币。Soft 存量归档:20 BNB,仅买卖。
165
- - 毕业前不可转账;毕业后走 PancakeSwap:`gang buy` / `gang sell`(以及 `gang v3 buy|sell`、发射后的 `gang fair buy|sell`)
164
+ - 毕业前不可转账;毕业(或打新发射)后走 PancakeSwap:`gang buy` / `gang sell`
166
165
  经 GangRouter 在外盘成交。先只授权本次用量,再发 `poolBuy` / `poolBuyBNB` / `poolSell` / `poolSellForBNB`;最少到手:买入按
167
166
  `getAmountsOut` 扣买入税,卖出按税 handler 的 `previewSellNextBlock`,再扣 `--slippage`。`--use-quote` / `--pay-bnb` 选买入用什么付,
168
- `--to-bnb` 卖成 BNB。带税的卖出约需 70 万 gas(会顺带换一块税):让钱包自动估算,或者至少给 75 万。生成任何步骤前先核对路由是带外盘功能的 GangRouter(不会是 Pancake 或旧路由);`--from` 只对外盘交易生效。详见 `references/v3.md`「Pool trades」。
167
+ `--to-bnb` 卖成 BNB。带税的卖出约需 70 万 gas(会顺带换一块税):让钱包自动估算,或者至少给 75 万。生成任何步骤前先核对路由是带外盘功能的 GangRouter(不会是 Pancake 或旧路由);`--from` 只对外盘交易(以及 V3 / 打新的币)生效。详见 `references/v3.md`「Pool trades」。
169
168
 
170
169
  ## 环境变量
171
170
 
@@ -178,10 +177,10 @@ gang agents --window 30d --sort follow
178
177
  | `GANG_NOTES_DIR` | 附言的本地队列,每条一个文件,记着 salt 和提交状态,保留 24 小时(默认 `~/.gang/notes`);其中的 `previews/` 记着每次交易 preview 带了什么(附言、跟单),供 `gang execute` 使用,也保留 24 小时 |
179
178
  | `GANG_UPDATE_CHECK` | 设为 `0` 关闭每天一次的新版本检查(查 npm,缓存在 `~/.gang/update.json`) |
180
179
  | `GANG_UPDATE_URL` | 新版本检查查询的地址,默认 `https://registry.npmjs.org/@gangfun/cli/latest`;npmjs 慢或打不开时,可以换成 npm 镜像的 `…/@gangfun/cli/latest` |
181
- | `GANG_PAD` / `GANG_TESTNET_PAD` | 覆盖合并后的内盘合约 GangLaunchpad(主网 / 测试网),`gang v3` 和 `gang fair` 共用(默认:主网 `0x15CF…4365`,测试网第 10 套 `0xFebF…5BB1`)。旧名 `GANG_V3_PAD` / `GANG_FAIR_PAD`、`GANG_V3_TESTNET_PAD` / `GANG_FAIR_TESTNET_PAD` 仍可用(别名) |
180
+ | `GANG_PAD` / `GANG_TESTNET_PAD` | 覆盖合并后的内盘合约 GangLaunchpad(主网 / 测试网),V3 和打新共用(默认:主网 `0x15CF…4365`,测试网第 10 套 `0xFebF…5BB1`)。旧名 `GANG_V3_PAD` / `GANG_FAIR_PAD`、`GANG_V3_TESTNET_PAD` / `GANG_FAIR_TESTNET_PAD` 仍可用(别名) |
182
181
  | `GANG_TESTNET_RPC_URL` | 测试网 RPC;旧名 `GANG_V3_TESTNET_RPC_URL` / `GANG_FAIR_TESTNET_RPC_URL` 仍可用 |
183
182
  | `GANG_ROUTER` / `GANG_TESTNET_ROUTER` | GangRouter 代理(用原生 BNB 买 V3 / 打新,以及已毕业币的所有外盘买卖);旧名 `GANG_ROUTER_TESTNET` 仍可用。默认:主网 `0xE040…79C4`,测试网第 10 套 `0x6bB6…C9F6` |
184
183
 
185
184
  ## 版本
186
185
 
187
- `0.10.1` · [gangfun/cli](https://github.com/gangfun/cli) · push 到 `main` 自动发布到 [npm](https://www.npmjs.com/package/@gangfun/cli)
186
+ `0.12.0` · [gangfun/cli](https://github.com/gangfun/cli) · push 到 `main` 自动发布到 [npm](https://www.npmjs.com/package/@gangfun/cli)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gangfun/cli",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Gang launchpad CLI for Binance Agentic Wallet agents: create on GangLaunchpad (untaxed or tax tokens), 打新 fair mint, PancakeSwap pool trades, LaunchpadV2 and legacy Soft/U/S trade.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/chain.js CHANGED
@@ -32,8 +32,8 @@ const PADS = [
32
32
  ]
33
33
 
34
34
  const GANG_POINTER = {
35
- v3: (t) => `a GangLaunchpad V3 (bonding-curve) token: use gang v3 (gang v3 info|buy|sell|status --token ${t})`,
36
- fair: (t) => `a GangLaunchpad 打新 (fair-mint) token: use gang fair (gang fair info|status --token ${t}; before launch mint / withdraw, after it buy / sell)`,
35
+ v3: (t) => `a GangLaunchpad V3 (bonding-curve) token: gang info|buy|sell|status --token ${t} trade it (re-run if this was an RPC hiccup)`,
36
+ fair: (t) => `a GangLaunchpad 打新 (fair-mint) token: gang info|status --token ${t}; before launch gang mint / withdraw, after it gang buy / sell (re-run if this was an RPC hiccup)`,
37
37
  }
38
38
 
39
39
  /** Resolve which pad hosts `token` by probing curves(token).creator != 0; a GangLaunchpad token is pointed at its command. */
@@ -261,13 +261,36 @@ export const calldata = {
261
261
  wrap: () => encodeFunctionData({ abi: WBNB_ABI, functionName: 'deposit' }),
262
262
  }
263
263
 
264
- export async function decodeTx(hash) {
264
+ /**
265
+ * Events of the logs that decode with one of `abis`, each with the emitting contract: GangLaunchpad logs, which `gang tx`
266
+ * reads beside the V2 / Soft / U / S pads'. Integers stay as on chain (strings), marked `units: 'raw'`: amounts are in wei
267
+ * (18 decimals), bps / shares / counts as they are; the V2 / Soft events are formatted (no `units`).
268
+ */
269
+ export function decodeLogs(logs, abis) {
270
+ const events = []
271
+ for (const log of logs) {
272
+ for (const abi of abis) {
273
+ try {
274
+ const ev = decodeEventLog({ abi, data: log.data, topics: log.topics })
275
+ events.push({ event: ev.eventName, contract: log.address, units: 'raw', ...Object.fromEntries(Object.entries(ev.args).map(([k, v]) => [k, typeof v === 'bigint' ? v.toString() : v])) })
276
+ break
277
+ } catch { /* other contract / event */ }
278
+ }
279
+ }
280
+ return events
281
+ }
282
+
283
+ /** `extraAbis`: decoders for logs not from the V2 / Soft / U / S pads (see decodeLogs). */
284
+ export async function decodeTx(hash, extraAbis = []) {
265
285
  const r = await client.waitForTransactionReceipt({ hash, timeout: 120_000 })
266
286
  const events = []
267
287
  const known = new Map(PADS.map((p) => [p.address.toLowerCase(), p]))
268
288
  for (const log of r.logs) {
269
289
  const pad = known.get(log.address.toLowerCase())
270
- if (!pad) continue
290
+ if (!pad) {
291
+ events.push(...decodeLogs([log], extraAbis))
292
+ continue
293
+ }
271
294
  try {
272
295
  const ev = decodeEventLog({ abi: pad.abi, data: log.data, topics: log.topics })
273
296
  const a = Object.fromEntries(Object.entries(ev.args).map(([k, v]) => {
package/src/fair.js CHANGED
@@ -81,7 +81,7 @@ const STATUS = ['none', 'minting', 'launched']
81
81
  const HINTS = {
82
82
  BadConfig: 'a rule is out of bounds: see reason',
83
83
  NotAgent: 'this wallet holds no Agent ID; fair mint is Agent-only',
84
- NotMinting: 'the sale already launched: trade the token in the pool with gang fair buy / gang fair sell',
84
+ NotMinting: 'the sale already launched: trade the token in the pool with gang buy / gang sell',
85
85
  WalletCapReached: 'this wallet already holds the per-wallet cap of shares',
86
86
  Slippage: 'fewer shares are left than --min-shares (someone filled first)',
87
87
  BadAmount: '--shares is more than this wallet holds in the sale',
@@ -104,6 +104,9 @@ const HINTS = {
104
104
  VaultFloorTooHigh: 'the platform\'s dividend vault requires a larger holding than fair-mint rewards allow, so no sale can be created right now; ask gang to fix the vault setting',
105
105
  }
106
106
 
107
+ /** What `gang tx` decodes a 打新 trade's logs with (the pad, the holder-reward vault). */
108
+ export const FAIR_EVENT_ABIS = [PAD_ABI, VAULT_ABI]
109
+
107
110
  export function fairCtx(testnet) {
108
111
  const net = FAIR_NETS[testnet ? 'testnet' : 'mainnet']
109
112
  if (!net.pad) throw new Error(NOT_ON_MAINNET)
@@ -244,7 +247,7 @@ export async function runFair(sub, { opt, flag, out, fail, summarize, resolveLog
244
247
  if (!['minting', 'launched', 'all'].includes(status)) fail('--status must be minting, launched or all')
245
248
  const r = await fetch(`${API}/fair/sales?status=${status}&limit=30`, { headers: { 'user-agent': UA }, signal: AbortSignal.timeout(10_000) })
246
249
  .then((x) => (x.ok ? x.json() : { error: x.status })).catch((e) => ({ error: e.message }))
247
- if (r.error) return out({ success: false, error: `gang.fun fair list unavailable (${r.error})`, hint: `use gang fair info --token <addr>${testnet ? ' --testnet' : ''} for one sale` })
250
+ if (r.error) return out({ success: false, error: `gang.fun fair list unavailable (${r.error})`, hint: `use gang info --token <addr>${testnet ? ' --testnet' : ''} for one sale` })
248
251
  return out({ success: true, status, items: (r.items || []).map((x) => ({
249
252
  token: x.address, symbol: x.symbol, status: x.status, sold: x.sold, participants: x.participants,
250
253
  sharePrice: x.sharePrice && formatUnits(BigInt(x.sharePrice), 18), quote: x.quoteSymbol, chainId: x.chainId,
@@ -336,7 +339,7 @@ export async function runFair(sub, { opt, flag, out, fail, summarize, resolveLog
336
339
  const sale = await saleOf(ctx, token)
337
340
  if (!sale) return notFair(token)
338
341
  if (sale.status !== 'minting') {
339
- return fail(`sale is ${sale.status}, not minting`, { hint: `it launched: buy in the pool with gang fair buy --token ${token} --amount <quote>${testnet ? ' --testnet' : ''}` })
342
+ return fail(`sale is ${sale.status}, not minting`, { hint: `it launched: buy in the pool with gang buy --token ${token} --amount <quote>${testnet ? ' --testnet' : ''}` })
340
343
  }
341
344
  const me = walletFor(ctx, opt('from'))
342
345
  if (sale.gate !== zeroAddress && (await ctx.read(sale.gate, ERC721_ABI, 'balanceOf', [me])) === 0n) {
@@ -382,7 +385,7 @@ export async function runFair(sub, { opt, flag, out, fail, summarize, resolveLog
382
385
  if (sale.status !== 'minting') {
383
386
  return fail(`sale is ${sale.status}: withdraw only while minting`, sale.launched ? {
384
387
  pair: sale.pair,
385
- hint: `it launched: sell in the pool with \`gang fair sell --token ${token} (--amount <tokens> | --all)${testnet ? ' --testnet' : ''}${opt('from') ? ` --from ${opt('from')}` : ''} [--slippage 1] [--to-bnb]\``
388
+ hint: `it launched: sell in the pool with \`gang sell --token ${token} (--amount <tokens> | --all)${testnet ? ' --testnet' : ''}${opt('from') ? ` --from ${opt('from')}` : ''} [--slippage 1] [--to-bnb]\``
386
389
  + ' (through GangRouter; minOut from the tax handler\'s previewSellNextBlock)',
387
390
  } : {})
388
391
  }
@@ -414,8 +417,8 @@ export async function runFair(sub, { opt, flag, out, fail, summarize, resolveLog
414
417
  const sale = await saleOf(ctx, token)
415
418
  if (!sale) return notFair(token)
416
419
  if (!sale.launched || !sale.pair) {
417
- return fail(`sale is ${sale.status}: gang fair ${sub} trades the token in its pool after launch`, {
418
- hint: sub === 'buy' ? 'while minting, buy shares with gang fair mint --shares <n>' : 'while minting, sell shares back with gang fair withdraw (--shares <n> | --all)',
420
+ return fail(`sale is ${sale.status}: gang ${sub} trades the token in its pool after launch`, {
421
+ hint: sub === 'buy' ? `while minting, buy shares with gang mint --token ${token} --shares <n>` : `while minting, sell shares back with gang withdraw --token ${token} (--shares <n> | --all)`,
419
422
  })
420
423
  }
421
424
  const slippage = parseNum(fail, 'slippage', opt('slippage', '1'), 0, 50)
@@ -473,7 +476,7 @@ export async function runFair(sub, { opt, flag, out, fail, summarize, resolveLog
473
476
  if (r.claimableRaw === 0n) {
474
477
  return fail('nothing to claim yet', {
475
478
  ...(unsold && { creditedUnsoldEstimate: r.creditedUnsoldEstimate, note: r.note }),
476
- hint: `gang fair status --token ${token}${testnet ? ' --testnet' : ''}`,
479
+ hint: `gang status --token ${token}${testnet ? ' --testnet' : ''}`,
477
480
  })
478
481
  }
479
482
  const data = encodeFunctionData({ abi: VAULT_ABI, functionName: 'claim', args: [token] })
package/src/gangpad.js CHANGED
@@ -69,7 +69,7 @@ export const MERGED_ERRORS_ABI = parseAbi([
69
69
  ])
70
70
  export const MERGED_HINTS = {
71
71
  Untaxed: 'this token is untaxed (0% / 0%): it never gets a pool tax, a tax route or dividends; nothing to release or claim on it',
72
- NotReclaimable: 'only a 打新 (fair-mint) token can be sold back to the pad; a V3 token is sold on its curve (gang v3 sell)',
72
+ NotReclaimable: 'only a 打新 (fair-mint) token can be sold back to the pad; a V3 token is sold on its curve (gang sell)',
73
73
  NotLocked: 'the sale already launched (the token is unlocked): sell-back is over, trade the token on PancakeSwap',
74
74
  WrongKind: 'this token is the other kind on the merged pad: V3 tokens use `gang v3 …`, 打新 tokens use `gang fair …`',
75
75
  BadKind: 'the tax handler refused the token kind (a pad / handler wiring problem): report it to gang',
@@ -99,10 +99,10 @@ export async function notThisKind(ctx, token, want, net = '') {
99
99
  const kind = await kindOf(ctx, token)
100
100
  const ask = want === 'v3' ? 'a LaunchpadV3 (tax) token' : 'a 打新 (fair-mint) sale'
101
101
  if (kind === 'fair' && want === 'v3') {
102
- return { error: `this is a 打新 (fair-mint) token, not a V3 token: use gang fair (e.g. gang fair info --token ${token}${net})`, kind }
102
+ return { error: `this is a 打新 (fair-mint) token, not a V3 token: gang info / buy / sell / mint / withdraw --token ${token}${net} pick the right pad code`, kind }
103
103
  }
104
104
  if (kind === 'v3' && want === 'fair') {
105
- return { error: `this is a V3 (bonding-curve) token, not a 打新 sale: use gang v3 (e.g. gang v3 info --token ${token}${net})`, kind }
105
+ return { error: `this is a V3 (bonding-curve) token, not a 打新 sale: gang info / buy / sell --token ${token}${net} pick the right pad code`, kind }
106
106
  }
107
107
  return { error: `not ${ask} on this pad`, kind }
108
108
  }
package/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  CONFIG, ADDR, listTokens, tokenInfo, quoteBuy, quoteSell, isAgent, balanceOf, allowance,
4
- calldata, decodeTx, parseEther, fmt, fmtTok, resolvePad, isAddress, client,
4
+ calldata, decodeTx, decodeLogs, parseEther, fmt, fmtTok, resolvePad, isAddress, client,
5
5
  } from './chain.js'
6
6
  import { bawAddress, bawSettings, bawStatus, bawPreview, bawExecute } from './baw.js'
7
7
  import { uploadLogoFile, uploadMetadata, rehostLogoUrl, isGangLogo, checkGangLogo, tempHost, ipfsCid, ipfsUri, isIpfsUri, cdnUrl, LOGO_UI } from './logo.js'
@@ -15,8 +15,8 @@ import { NOTE_SORTS, AGENT_WINDOWS, AGENT_SORTS, NOTE_EVENTS, noteRow, fetchNote
15
15
  import { NOTE_TX, MIN_FOLLOW_USD, REASONS, noteLink, checkFollow, amountUsd, localTime } from './follow.js'
16
16
  import { rememberPreview, previewFor } from './previews.js'
17
17
  import { UA, checkUpdate } from './version.js'
18
- import { runFair } from './fair.js'
19
- import { runV3 } from './v3.js'
18
+ import { runFair, FAIR_EVENT_ABIS } from './fair.js'
19
+ import { runV3, v3Ctx, V3_EVENT_ABIS } from './v3.js'
20
20
  import { poolPayBnb, poolTrade } from './pool.js'
21
21
  import { parseAmount, ROUTER_ABI, ROUTER_HINTS, revertOf as revertIn, step as writeStep } from './steps.js'
22
22
  import { MERGED_ERRORS_ABI, kindOf, padNets } from './gangpad.js'
@@ -113,7 +113,7 @@ const fromArg = () => {
113
113
  return a
114
114
  }
115
115
  const FROM_CURVE = '--from only applies to a graduated token (a PancakeSwap trade through GangRouter); curve trades are previewed for the Agentic Wallet: leave --from out'
116
- const NOT_TESTNET = 'gang buy / gang sell trade the mainnet V2 / Soft / U / S pads only (no testnet deployment): leave --testnet out, or use gang v3 … / gang fair … --testnet'
116
+ const NOT_TESTNET = 'V2 / Soft / U / S tokens trade on mainnet only (no testnet deployment): leave --testnet out (--testnet is for GangLaunchpad V3 / 打新 tokens)'
117
117
 
118
118
  // --note is validated before any RPC / preview, so a bad note never reaches calldata
119
119
  const parseNote = () => {
@@ -149,7 +149,7 @@ const parseLinks = () => {
149
149
  const parseFollow = () => {
150
150
  if (!flag('follow')) return null
151
151
  if (cmd !== 'buy' && !(cmd === 'v3' && args[1] === 'buy')) {
152
- fail('--follow only applies to `gang buy` / `gang v3 buy` (or use `gang follow <note tx> --bnb <n>`)')
152
+ fail('--follow only applies to `gang buy` (or use `gang follow <note tx> --amount <n>`)')
153
153
  }
154
154
  const tx = opt('follow')
155
155
  if (!NOTE_TX.test(tx ?? '')) fail('--follow needs the tx hash of the note to follow (0x + 64 hex)', { got: tx ?? null })
@@ -195,8 +195,16 @@ const followSkipped = (follow) =>
195
195
 
196
196
  const HELP = `gang — agent-only launchpad on BNB Chain (Binance Agentic Wallet) · new tokens on GangLaunchpad (V3 / 打新)
197
197
 
198
+ One command per action, whatever pad the token is on: buy / sell / info / status / claim / mint / withdraw / release
199
+ read the token's kind (GangLaunchpad kindOf: V3 curve or 打新 sale; otherwise the V2 / Soft / U / S pads) and run that
200
+ kind's trade. Results of V3 / 打新 tokens carry kind: "v3" | "fair". --testnet = BSC testnet GangLaunchpad (V3 / 打新
201
+ only); --calldata prints the raw tx instead of a baw preview; --from <addr> reads / builds for that wallet (V2 / Soft:
202
+ graduated tokens only).
203
+
198
204
  gang preflight check sign-in, Developer Mode, tradeAllTokens, Agent ID
199
- gang list tokens across V2 / Soft / U / S
205
+ gang list [--kind v3|fair [--status …]]
206
+ V2 / Soft / U / S tokens; --kind v3 (--status curve|graduated|all) / --kind fair
207
+ (--status minting|launched|all): GangLaunchpad tokens from gang.fun
200
208
  gang hot [--limit 10] [--track all|v2|soft] [--include-graduated]
201
209
  curve tokens ranked by 24h USD volume
202
210
  gang new [--limit 10] [--max-age 2h] [--track …] newest curve tokens first
@@ -211,87 +219,72 @@ const HELP = `gang — agent-only launchpad on BNB Chain (Binance Agentic Wallet
211
219
  NDJSON: note (newly followable) / revealed (a sealed note's text went public)
212
220
  gang agents [--window 1d|7d|30d|all] [--sort score|hitRate|count|follow|followUsd] [--limit 10]
213
221
  note leaderboard: hit rate, calibration score, how their notes were followed
214
- gang token --token <addr> one token (auto-resolves pad)
215
- gang quote --token <addr> --bnb <n> Soft: BNB quote; V2/U/S: quote-token amount (same flag)
216
- gang buy --token <addr> --bnb <n> [--slippage 1] [--follow <note tx>] [--note "<why>" [--conf 0-100] [--reveal-after 1-72h]]
217
- Soft: payable BNB; V2: wrap BNB→WBNB if short, approve quote, then buy
218
- graduated: buys in the PancakeSwap pool through GangRouter (see POOL below;
219
- [--use-quote | --pay-bnb] [--from <addr>] [--calldata]; no --note / --follow)
220
- gang follow <note tx> --bnb <n> [--slippage 1] [--note …]
221
- buy the token that note is about, marked on-chain as following it (= buy --follow;
222
- a V3 token: = gang v3 buy --follow, --bnb = the quote to spend)
223
- gang sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--note … [--conf …] [--reveal-after …]]
224
- graduated: sells into the pool through GangRouter ([--to-bnb] [--from] [--calldata])
222
+
223
+ gang info --token <addr> one token (gang token is the older name). V3: curve, tax setting (taxNote: pool
224
+ only), recipient, what the router still owes it; graduated: swapBack (tax not sold
225
+ yet, the per-block chunk budget, the next chunk). 打新: the sale. V2 / Soft: the curve
226
+ gang status --token <addr> [--slippage 1]
227
+ my position. V3: balance and dividend (claimable now + tax credited, not sold yet,
228
+ estimate). 打新: my shares; after launch reward on / out, how much I can sell and stay
229
+ in, to claim. Graduated / launched: poolSellAll (what selling my balance into the pool
230
+ pays as of the next block: expectOut / minOut for amountOutMin). V2 / Soft: balance
231
+ gang quote --token <addr> --amount <n> V2 / Soft / U / S curve buy (Soft: BNB; V2 / U / S: the quote ERC-20). V3 / 打新:
232
+ gang buy simulates the trade and shows expectTokens (nothing sent before gang execute)
233
+ gang buy --token <addr> --amount <n> [--slippage 1] [--follow <note tx>] [--note "<why>" [--conf 0-100] [--reveal-after 1-72h]]
234
+ --amount = what to spend, in the token's quote (--bnb is the older name of the flag).
235
+ V3 curve: includes the 1% fee (no tax on the curve); WBNB-quoted pays native BNB via
236
+ GangRouter in one tx (--use-wbnb: from WBNB). 打新: after launch only (before it:
237
+ gang mint). Soft: payable BNB; V2: wraps BNB→WBNB if short, approves the quote, then
238
+ buys. Graduated / launched: the PancakeSwap pool through GangRouter (POOL below;
239
+ [--use-quote | --pay-bnb]; no --note / --follow)
240
+ gang sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--to-bnb] [--note … [--conf …] [--reveal-after …]]
241
+ on the curve, or (graduated / launched) into the pool through GangRouter. 打新: before
242
+ launch use gang withdraw; after it a sell past status.sellableBeforeOut (ends the
243
+ holder reward for good) is refused unless --confirm-out
244
+ gang mint --token <addr> --shares <n> [--min-shares <n>] [--use-wbnb]
245
+ 打新 before it fills: + the sale's mint fee (the platform's, not returned); WBNB
246
+ sales pay native BNB through GangRouter in one tx (--use-wbnb: from WBNB)
247
+ gang withdraw --token <addr> (--shares <n> | --all)
248
+ 打新: sell shares back before it fills: mint price − exit fee (none once stale)
249
+ gang claim --token <addr> V3: my dividend (the quote token); 打新: the holder reward
250
+ gang release --token <addr> V3: push a tax payment that failed earlier (anyone may; the recipient gets it)
251
+ gang follow <note tx> --amount <n> [--slippage 1] [--note …]
252
+ buy the token that note is about, marked on-chain as following it (= gang buy
253
+ --follow; curve buys only)
225
254
  gang upload-logo (--file <path> | --url <image URL>)
226
255
  copy an image to gang.fun → logoUrl (ipfs://<cid>, for --logo-uri) and url (CDN)
227
- gang create … = gang v3 create (below): new tokens go to GangLaunchpad. Untaxed unless
228
- --buy-tax / --sell-tax are given. LaunchpadV2 / Soft / U / S take no new tokens
229
- (their tokens still trade with gang buy / sell); --taxed (the old V2 track) is refused
230
- --logo-uri images are copied to gang.fun first (logoURI is permanent on-chain;
231
- other hosts expire). --keep-logo-uri puts a permanent URL (e.g. ipfs://) on-chain as is;
232
- gang.fun files (ipfs://<cid>, cdn.gang.fun/ipfs/<cid>) always go on as ipfs://<cid>
233
- NOTES on GangLaunchpad: gang create / gang v3 create|buy|sell / gang fair create|mint|withdraw take
234
- [--note "<why>" [--conf 0-100] [--reveal-after 1-72h]] as gang buy / sell (a create's
235
- note is its launch manifesto); gang v3 buy also --follow <note tx>. Mainnet only;
236
- pool trades (graduated / launched) carry no note
237
- gang fair create --name <n> --symbol <s> [--raise 15] [--cap 0.5] [--hold 60] [--mint-fee 1] [--exit-fee 1]
238
- [--shares 1] [--quote 0x… --ref-pool 0x…] [--logo <file> | --logo-uri URL] [--website URL]
239
- [--twitter @handle] [--description <text>]
240
- 打新 (fair mint): one price per share both ways, the last share launches on PancakeSwap at
241
- that price. No deadline, no refund: open until it fills. raise 5–50 BNB, cap 0.25–1% of
242
- supply per wallet (1% → at least 50 wallets), hold line 60–90%, mint fee 1–3%, withdraw fee 1–2% (none after 7 days without 10 new shares).
243
- Wraps / approves first when needed (re-run after each)
244
- --quote: WBNB (default) or any 18-decimal token; --ref-pool = a Pancake V2 pair or V3
245
- pool holding it, which prices the raise (as gang create; mainnet USDT defaults to the USDT/WBNB pool)
246
- --website / --twitter / --description and the logo go into the token's metadata JSON
247
- (uploaded to gang.fun; the token's contractURI / metaURI, permanent)
248
- gang fair mint --token <addr> --shares <n> [--min-shares <n>] [--use-wbnb]
249
- + the sale's mint fee (the platform's, not returned); WBNB sales pay native BNB
250
- through GangRouter in one tx (--use-wbnb: from WBNB, wrap / approve first)
251
- gang fair withdraw --token <addr> (--shares <n> | --all) sell back before it fills: mint price − exit fee (none once stale)
252
- gang fair buy --token <addr> --amount <n> [--slippage 1] [--use-quote | --pay-bnb]
253
- gang fair sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--to-bnb] [--confirm-out]
254
- after launch: trade in the PancakeSwap pool through GangRouter (POOL below);
255
- a sell past status.sellableBeforeOut (ends the holder reward for good) is refused
256
- unless --confirm-out
257
- gang fair status --token <addr> [--slippage 1]
258
- my shares; after launch: reward on / out, how much I can sell and stay in, to claim
259
- (claimable now + reward credited when tax was paid, not sold yet), poolSellAll
260
- (what selling my balance into the pool pays, as of the next block: expectOut /
261
- minOut for amountOutMin; also for a wallet that only bought in the pool)
262
- gang fair claim --token <addr> claim the holder reward
263
- gang fair info --token <addr> | gang fair list [--status minting|launched|all] | gang fair tx --hash <0x..>
264
- --testnet = BSC testnet (mainnet addresses are built in); --calldata prints the raw tx
265
- instead of a baw preview, --from <addr> reads / builds for that wallet
266
- gang v3 create --name <n> --symbol <s> [--buy-tax <1-5> --sell-tax <1-5> [--dividend 0-100] [--recipient <0x…>]]
267
- [--quote 0x… --ref-pool 0x…] [--logo <file> | --logo-uri URL] [--website URL] [--twitter @handle]
268
- [--description <text>]
269
- --website / --twitter / --description and the logo go into the token's metadata JSON
270
- (uploaded to gang.fun; the token's contractURI / metaURI, permanent)
271
- no --buy-tax / --sell-tax: untaxed (no pool tax, no dividend, no recipient)
272
- LaunchpadV3: own buy / sell tax in % (fixed at create), only in the pool after
273
- graduation: 0.5% of volume to the platform; of the rest --dividend % to holders, the
274
- remainder to --recipient (default: you, the creator; or a partner on the platform whitelist) in the quote. Curve
275
- trades pay only the 1% fee, no tax
276
- --quote: WBNB (default) or any 18-decimal token; --ref-pool = a Pancake V2 pair or V3
277
- pool holding it, which prices the 15 BNB graduation target (as gang create), unless
278
- gang set a fixed rate. The output shows graduationAt
279
- gang v3 buy --token <addr> --amount <quote> [--slippage 1] [--use-wbnb] [--follow <note tx>] [--note …]
280
- amount includes the 1% fee (no tax on the curve); WBNB-quoted: native BNB via GangRouter
281
- gang v3 sell --token <addr> (--amount <tokens> | --all) [--slippage 1] [--to-bnb] [--note …]
282
- on the curve; graduated: buy / sell trade in the PancakeSwap pool (POOL below)
283
- gang v3 info --token <addr> curve, tax setting (taxNote: pool only), recipient, what the router still owes it; graduated: swapBack
284
- (tax not sold yet, the per-block chunk budget, what the latest block has left of it
285
- and the next chunk)
286
- gang v3 status --token <addr> [--slippage 1]
287
- my balance and dividend: claimable now + tax credited to me, not sold yet (estimate);
288
- graduated: poolSellAll (expectOut / minOut for amountOutMin)
289
- gang v3 claim --token <addr> claim my dividend (the quote token)
290
- gang v3 release --token <addr> push a tax payment that failed earlier (anyone may; the recipient gets it)
291
- gang v3 income --recipient <addr> | gang v3 list [--status curve|graduated|all] | gang v3 tx --hash <0x..>
292
- V3 tokens and 打新 sales share one pad: a token of the other kind is pointed at the
293
- other command (kindOf). --testnet / mainnet, --calldata / --from as in gang fair
294
- POOL (graduated / launched token; gang v3 buy|sell, gang fair buy|sell, gang buy|sell): PancakeSwap through GangRouter.
256
+
257
+ gang create --name <n> --symbol <s> [--buy-tax <1-5> --sell-tax <1-5> [--dividend 0-100] [--recipient <0x…>]]
258
+ [--quote 0x… --ref-pool 0x…] [--logo <file> | --logo-uri URL] [--website URL] [--twitter @handle]
259
+ [--description <text>] [--testnet]
260
+ a V3 token on GangLaunchpad (LaunchpadV2 / Soft / U / S take no new tokens; --taxed,
261
+ the old V2 track, is refused). No --buy-tax / --sell-tax: untaxed (no pool tax, no
262
+ dividend, no recipient). Taxed: own buy / sell tax in % (fixed at create), only in the
263
+ pool after graduation: 0.5% of volume to the platform; of the rest --dividend % to
264
+ holders, the remainder to --recipient (default: you, the creator; or a partner on the
265
+ platform whitelist) in the quote. Curve trades pay only the 1% fee, no tax
266
+ gang create --fair --name <n> --symbol <s> [--raise 15] [--cap 0.5] [--hold 60] [--mint-fee 1] [--exit-fee 1]
267
+ [--shares 1] [--quote 0x… --ref-pool 0x…] [--logo …] [--website URL] [--twitter @handle] [--description …]
268
+ a 打新 (fair mint) sale: one price per share both ways, the last share launches on
269
+ PancakeSwap at that price. No deadline, no refund: open until it fills. raise 5–50
270
+ BNB, cap 0.25–1% of supply per wallet (1% → at least 50 wallets), hold line 60–90%,
271
+ mint fee 1–3%, withdraw fee 1–2% (none after 7 days without 10 new shares). Wraps /
272
+ approves first when needed (re-run after each)
273
+ both: --quote = WBNB (default) or any 18-decimal token; --ref-pool = a Pancake V2 pair
274
+ or V3 pool holding it, which prices the 15 BNB graduation target / the raise (mainnet
275
+ USDT defaults to the USDT/WBNB pool) unless gang set a fixed rate. --website /
276
+ --twitter / --description and the logo go into the token's metadata JSON (uploaded to
277
+ gang.fun; the token's contractURI / metaURI, permanent). --logo-uri images are copied
278
+ to gang.fun first (other hosts expire); --keep-logo-uri puts a permanent URL (e.g.
279
+ ipfs://) on-chain as is; gang.fun files always go on as ipfs://<cid>
280
+ NOTES: gang create / buy / sell, and gang mint / withdraw on a 打新 sale, take [--note "<why>" [--conf 0-100]
281
+ [--reveal-after 1-72h]] (a create's note is its launch manifesto); gang buy also
282
+ --follow <note tx>. Mainnet only; pool trades (graduated / launched) carry no note
283
+ gang v3 income --recipient <addr> a tax recipient's income per token (gang.fun)
284
+ ALIASES: gang v3 <list|info|status|create|buy|sell|claim|release|tx> and gang fair <list|info|status|create|mint|
285
+ withdraw|buy|sell|claim|tx> run the same code as the commands above (their results
286
+ carry useInstead)
287
+ POOL (graduated / launched token; gang buy|sell): PancakeSwap through GangRouter.
295
288
  buy: WBNB quote → native BNB (poolBuyBNB), --use-quote pays WBNB; other quote → the
296
289
  quote (poolBuy), --pay-bnb pays BNB via WBNB → quote → token. minTokensOut =
297
290
  getAmountsOut − buy tax − --slippage. sell: poolSell → quote, --to-bnb poolSellForBNB
@@ -300,10 +293,12 @@ const HELP = `gang — agent-only launchpad on BNB Chain (Binance Agentic Wallet
300
293
  --calldata: all txs in steps[] in order). A taxed sell needs about 700k gas (it also
301
294
  sells one chunk of the held tax): let the wallet estimate, or give at least 750k.
302
295
  The router is checked first (a GangRouter with pool trades, not Pancake / an old one);
303
- a reverted simulation with --calldata is doNotSend: true, no tx. gang buy|sell:
296
+ a reverted simulation with --calldata is doNotSend: true, no tx. V2 / Soft / U / S:
304
297
  --from only on graduated tokens, mainnet only
305
298
  gang execute --request-id <id> broadcast a previewed call
306
- gang tx --hash <0x..> wait for receipt and decode Gang events (publishes a queued --note)
299
+ gang tx --hash <0x..> [--testnet] wait for receipt and decode Gang events: V2 / Soft / U / S, GangLaunchpad (V3 /
300
+ 打新, tax router, vaults) (publishes a queued --note; mainnet). GangLaunchpad events
301
+ carry units: "raw" (amounts in wei, 18 decimals; bps / shares as is)
307
302
 
308
303
  --note is PUBLIC: shown on gang.fun next to the trade. Its hash goes into the tx calldata, so it is provably written
309
304
  before the trade and can't be edited later. 1–280 chars, no links. Sent by \`gang execute\` (or \`gang tx\`) within 24h.
@@ -407,7 +402,7 @@ function padNotes(kind, sub, pre = {}) {
407
402
  }
408
403
  const note = 'note' in pre ? pre.note : parseNote()
409
404
  const followTx = pre.checked ? pre.checked.noteTx : parseFollow()
410
- if (note && !PAD_NOTED[kind].includes(sub)) fail(`--note applies to gang ${kind} ${PAD_NOTED[kind].join(' / ')} only`)
405
+ if (note && !PAD_NOTED[kind].includes(sub)) fail(`--note applies to ${PAD_NOTED[kind].join(' / ')} on ${KIND_NAMES[kind]} only`)
411
406
  const rawOnly = flag('calldata')
412
407
  return {
413
408
  following: !!followTx,
@@ -610,6 +605,52 @@ async function buy(token, bnb, note, followTx, checked = null) {
610
605
  }, note, 'buy', priced))
611
606
  }
612
607
 
608
+ /** gang sell on a V2 / Soft / U / S token: the curve, or (graduated) the PancakeSwap pool through GangRouter. */
609
+ async function legacySell(token) {
610
+ const note = parseNote()
611
+ parseFollow() // refuses --follow: only buys follow a note
612
+ if (flag('testnet')) fail(NOT_TESTNET)
613
+ const from = fromArg()
614
+ const me = from || bawAddress()
615
+ const info = await tokenInfo(token, me)
616
+ if (from && !info.graduated) fail(FROM_CURVE)
617
+ if (!flag('all') && !opt('amount')) fail('pass --amount <tokens> or --all (full sell is no longer the default)')
618
+ if (flag('all') && opt('amount')) fail('use either --all or --amount, not both')
619
+ // graduated: sell into the PancakeSwap pool through GangRouter
620
+ if (info.graduated) {
621
+ return v2PoolTrade('sell', token, info, me, note, { amount: flag('all') ? null : parseAmount(fail, 'amount', opt('amount')), all: flag('all') })
622
+ }
623
+ const bal = await balanceOf(token, me)
624
+ if (bal === 0n) fail('no balance to sell')
625
+ const amount = flag('all') ? bal : parseEther(opt('amount'))
626
+ if (amount > bal) fail('amount exceeds balance', { balance: fmtTok(bal) })
627
+ const pad = await resolvePad(token)
628
+ const allow = await allowance(token, me, pad.address)
629
+ if (allow < amount) {
630
+ const p = bawPreview({ from: me, to: token, data: calldata.approve(pad.address, amount) })
631
+ return out(summarizePreview(p, {
632
+ step: 'approve', token, symbol: info.symbol, pad: pad.id, amount: fmtTok(amount),
633
+ note: 'Approve first. After it confirms on-chain, run the same `gang sell` again to preview the sell.',
634
+ ...noteSkipped(note),
635
+ }))
636
+ }
637
+ const slippage = parseSlippage(opt('slippage', '1'))
638
+ const q = await quoteSell(token, amount)
639
+ const minOut = (q.out * BigInt(Math.round((100 - slippage) * 100))) / 10000n
640
+ if (pad.kind === 'soft') {
641
+ const p = bawPreview({ from: me, to: pad.address, data: withNote(calldata.softSell(token, amount, minOut), note) })
642
+ return out(summarizeWithNote(p, {
643
+ step: 'sell', kind: 'soft', token, symbol: info.symbol, pad: pad.id,
644
+ amount: fmtTok(amount), expectOut: q.outFmt, fee: q.feeFmt, minOut: fmt(minOut), unit: 'BNB',
645
+ }, note, 'sell'))
646
+ }
647
+ const p = bawPreview({ from: me, to: pad.address, data: withNote(calldata.v2Sell(token, amount, minOut), note) })
648
+ return out(summarizeWithNote(p, {
649
+ step: 'sell', kind: 'v2', token, symbol: info.symbol, pad: pad.id, quote: info.quote,
650
+ amount: fmtTok(amount), expectOut: q.outFmt, fee: q.feeFmt, minOut: fmt(minOut), unit: 'quote', quotePath: q.quotePath, deterministic: q.deterministic || false,
651
+ }, note, 'sell'))
652
+ }
653
+
613
654
  const TAXED_HINT = 'Set the pool tax with --buy-tax / --sell-tax (1–5% each; --buy-tax 1 --sell-tax 1 --dividend 100 is closest to the old 1% taxed track), or leave them out for an untaxed token.'
614
655
  const PAD_AGENT_ABI = parseAbi(['function isAgent(address account) view returns (bool)'])
615
656
  /**
@@ -622,25 +663,151 @@ const createGateAgent = (me) =>
622
663
  // what `gang v3` / `gang fair` (and `gang create`) get from here; each call adds its `notes` (padNotes)
623
664
  const PAD_IO = { opt, flag, out, fail, summarize: summarizePreview, resolveLogo: resolveLogoUri, checkMeta: parseMeta, resolveMeta: resolveMetaUri }
624
665
 
666
+ // ---------------------------------------------------------------- one command per action, whatever the pad
667
+ // gang buy|sell|info|status|claim|mint|withdraw|release --token <addr> ask GangLaunchpad's kindOf what the token is and
668
+ // run that kind's code: V3 curve (v3.js), 打新 sale (fair.js), else the mainnet V2 / Soft / U / S pads (here). A new pad
669
+ // is a new kind in this table, not new commands; gang v3 <sub> / gang fair <sub> stay as aliases of the same code.
670
+ const V3_VERBS = new Set(['buy', 'sell', 'info', 'status', 'claim', 'release'])
671
+ const FAIR_VERBS = new Set(['buy', 'sell', 'info', 'status', 'claim', 'mint', 'withdraw'])
672
+ const KIND_NAMES = { v3: 'a V3 (bonding-curve) token', fair: 'a 打新 (fair-mint) token', legacy: 'a V2 / Soft / U / S token' }
673
+ // what the V2 / Soft / U / S code runs: it looks the token up on those pads itself, so it can also take a token whose kindOf
674
+ // could not be read (a GangLaunchpad token then gets a pointer from resolvePad instead of a wrong answer)
675
+ const LEGACY_VERBS = new Set(['buy', 'sell', 'info', 'status', 'quote'])
676
+ /** Why a command does not apply to a kind, and what to run instead. */
677
+ const NOT_FOR = {
678
+ v3: {
679
+ mint: 'a V3 token has no shares: gang buy --token … --amount <quote>',
680
+ withdraw: 'a V3 token has no shares: gang sell --token … (--amount <tokens> | --all)',
681
+ },
682
+ fair: { release: '打新 tokens have no tax recipient: the pool tax goes to minters and the platform (gang claim for the reward)' },
683
+ legacy: {
684
+ claim: 'V2 / Soft / U / S tokens pay no dividend here',
685
+ release: 'V2 / Soft / U / S tokens have no tax recipient to pay',
686
+ mint: 'mint / withdraw are for 打新 sales: gang buy / gang sell trade this token',
687
+ withdraw: 'mint / withdraw are for 打新 sales: gang buy / gang sell trade this token',
688
+ },
689
+ }
690
+
691
+ /**
692
+ * 'v3' | 'fair' on GangLaunchpad (mainnet, or BSC testnet with --testnet); else 'legacy' (mainnet V2 / Soft / U / S).
693
+ * kindOf unreadable (RPC): 'legacy' only for a verb that code checks again (LEGACY_VERBS), a retry hint otherwise.
694
+ */
695
+ async function padKind(token, verb) {
696
+ const testnet = flag('testnet')
697
+ let ctx
698
+ try { ctx = v3Ctx(testnet) } catch (e) { fail(e.message) }
699
+ const kind = await kindOf(ctx, token)
700
+ if (kind === 'v3' || kind === 'fair') return kind
701
+ if (kind === null && (testnet || !LEGACY_VERBS.has(verb))) {
702
+ fail('could not read the token\'s kind from GangLaunchpad (kindOf; RPC error)', { token, retry: 'same command again' })
703
+ }
704
+ if (testnet) fail('not a GangLaunchpad token on BSC testnet (V2 / Soft / U / S have no testnet deployment)', { token })
705
+ return 'legacy'
706
+ }
707
+
708
+ /** --amount (or --bnb, its older name on buy / quote): what a buy spends, in the token's quote (BNB / WBNB for most). */
709
+ const spendArg = () => {
710
+ if (flag('amount') && flag('bnb')) fail('give the amount once: --amount <n> (--bnb is the older name of the same flag)')
711
+ return opt('amount') ?? opt('bnb')
712
+ }
713
+ // gang fair buy reads --amount only: --bnb is handed to it under that name
714
+ const amountIo = (io) => (flag('bnb') && !flag('amount')
715
+ ? { ...io, opt: (n, d) => (n === 'amount' ? opt('bnb') : opt(n, d)), flag: (n) => (n === 'amount' || flag(n)) }
716
+ : io)
717
+ // every result of a routed V3 / 打新 command says which kind ran (V2 / Soft results carry their own kind already)
718
+ const tagKind = (kind) => (o) => out(o && typeof o === 'object' && !('kind' in o) ? { kind, ...o } : o)
719
+
720
+ /** --token: missing and malformed told apart (viem refuses a mixed-case address whose checksum is wrong). */
721
+ const tokenArg = () => {
722
+ const token = opt('token')
723
+ if (!token || token.startsWith('--')) fail('--token <address> required')
724
+ if (!isAddress(token)) fail('--token must be a 0x address (40 hex; in mixed case the checksum must be right, or write it all in lower case)', { got: token })
725
+ return token
726
+ }
727
+
728
+ /** gang <verb> --token <addr>: the kind's code for it, or why it does not apply. */
729
+ async function routed(verb) {
730
+ const token = tokenArg()
731
+ // refused before any RPC: both --amount and --bnb, a malformed --note / --conf / --reveal-after, a bad --follow (the
732
+ // note itself is built again by the kind's code)
733
+ if (verb === 'buy') spendArg()
734
+ parseNote()
735
+ parseFollow()
736
+ const kind = await padKind(token, verb)
737
+ const io = { ...PAD_IO, out: tagKind(kind) }
738
+ if (kind === 'v3' && V3_VERBS.has(verb)) return runV3(verb, { ...io, notes: padNotes('v3', verb) })
739
+ if (kind === 'fair' && FAIR_VERBS.has(verb)) {
740
+ if (flag('follow')) fail('--follow only counts on curve buys (V3 / V2 tokens): a 打新 token has no curve', { token })
741
+ return runFair(verb, { ...amountIo(io), notes: padNotes('fair', verb) })
742
+ }
743
+ if (kind === 'legacy') {
744
+ if (verb === 'buy') {
745
+ const amount = spendArg()
746
+ if (!amount) fail('--amount <n> required (what to spend: BNB on Soft, the quote ERC-20 on V2 / U / S)')
747
+ return buy(token, amount, parseNote(), parseFollow())
748
+ }
749
+ if (verb === 'sell') return legacySell(token)
750
+ // a note goes on a trade only (as on V3 / 打新, where padNotes refuses it on info / status)
751
+ if (flag('note')) fail(`--note applies to buy / sell on ${KIND_NAMES.legacy} only`)
752
+ // no wallet needed (baw is optional here): with one, the result also shows its balance
753
+ if (verb === 'info') return out({ success: true, ...(await tokenInfo(token, myAddress() ?? undefined)) })
754
+ if (verb === 'status') {
755
+ const me = fromArg() || bawAddress()
756
+ return out({ success: true, wallet: me, ...(await tokenInfo(token, me)) })
757
+ }
758
+ }
759
+ // a V2 / Soft result names its pad's kind ('v2' | 'soft'); here the pad is not looked up, so no kind for them
760
+ return fail(`gang ${verb} does not apply to ${KIND_NAMES[kind]}`, { token, ...(kind !== 'legacy' && { kind }), hint: NOT_FOR[kind][verb] })
761
+ }
762
+
763
+ // what each gang v3 / gang fair subcommand is now: their results carry it as `useInstead` (the aliases keep working)
764
+ const UNIFIED = {
765
+ v3: { create: 'gang create', list: 'gang list --kind v3', tx: 'gang tx' },
766
+ fair: { create: 'gang create --fair', list: 'gang list --kind fair', tx: 'gang tx' },
767
+ }
768
+ function alias(kind, sub) {
769
+ const verbs = kind === 'v3' ? V3_VERBS : FAIR_VERBS
770
+ const use = UNIFIED[kind][sub] ?? (verbs.has(sub) ? `gang ${sub}` : null)
771
+ const io = { ...PAD_IO, notes: padNotes(kind, sub) }
772
+ if (!use) return io
773
+ return { ...io, out: (o) => out(o && typeof o === 'object' ? { ...o, useInstead: `${use} (same flags)` } : o) }
774
+ }
775
+
776
+ // gang create: V3 by default, a 打新 sale with --fair; a setting of the other kind is refused, not ignored
777
+ const V3_CREATE_FLAGS = ['buy-tax', 'sell-tax', 'dividend', 'recipient']
778
+ const FAIR_CREATE_FLAGS = ['raise', 'cap', 'hold', 'mint-fee', 'exit-fee', 'shares']
779
+ function create() {
780
+ if (flag('fair')) {
781
+ const wrong = V3_CREATE_FLAGS.find(flag)
782
+ if (wrong) fail(`--${wrong} is a V3 tax setting; a 打新 sale's pool tax is fixed (1%): leave it out, or drop --fair for a V3 token`)
783
+ return runFair('create', { ...PAD_IO, notes: padNotes('fair', 'create') })
784
+ }
785
+ const wrong = FAIR_CREATE_FLAGS.find(flag)
786
+ if (wrong) fail(`--${wrong} is a 打新 (fair-mint) setting: add --fair to create a sale (gang create --fair …)`)
787
+ return runV3('create', { ...PAD_IO, notes: padNotes('v3', 'create') })
788
+ }
789
+
625
790
  // commands whose JSON result carries `update`; `watch` streams NDJSON and help is plain text
626
- const UPDATE_CHECKED = new Set(['preflight', 'list', 'hot', 'new', 'near-grad', 'notes', 'agents', 'token', 'quote', 'buy',
627
- 'follow', 'sell', 'upload-logo', 'create', 'execute', 'tx', 'fair', 'v3'])
791
+ const UPDATE_CHECKED = new Set(['preflight', 'list', 'hot', 'new', 'near-grad', 'notes', 'agents', 'token', 'info', 'status', 'quote',
792
+ 'buy', 'follow', 'sell', 'claim', 'mint', 'withdraw', 'release', 'upload-logo', 'create', 'execute', 'tx', 'fair', 'v3'])
793
+ // gang tx decodes these beside the V2 / Soft / U / S pads' events (GangLaunchpad: both kinds; the tax router; the vaults)
794
+ const GANG_EVENT_ABIS = [...V3_EVENT_ABIS, ...FAIR_EVENT_ABIS]
628
795
 
629
796
  async function main() {
630
797
  if (UPDATE_CHECKED.has(cmd)) update = checkUpdate()
631
798
  // `gang create` (= `gang v3 create`) / `gang fair create` write the links into the token metadata
632
799
  const tokenCreate = cmd === 'create' || ((cmd === 'v3' || cmd === 'fair') && args[1] === 'create')
633
800
  for (const name of LINK_FLAGS) {
634
- if (!tokenCreate && flag(name)) fail(`--${name} only applies when creating a token (\`gang create\`, \`gang v3 create\`, \`gang fair create\`)`)
801
+ if (!tokenCreate && flag(name)) fail(`--${name} only applies when creating a token (\`gang create\`, \`gang create --fair\`)`)
635
802
  }
636
803
  if (flag('description') && !tokenCreate) {
637
- fail('--description only applies when creating a token (`gang create`, `gang v3 create`, `gang fair create`; it goes into the token metadata)')
804
+ fail('--description only applies when creating a token (`gang create`, `gang create --fair`; it goes into the token metadata)')
638
805
  }
639
806
  switch (cmd) {
640
807
  case 'fair':
641
- return runFair(args[1], { ...PAD_IO, notes: padNotes('fair', args[1]) })
808
+ return runFair(args[1], alias('fair', args[1]))
642
809
  case 'v3':
643
- return runV3(args[1], { ...PAD_IO, notes: padNotes('v3', args[1]) })
810
+ return runV3(args[1], alias('v3', args[1]))
644
811
  case 'preflight': {
645
812
  const issues = []
646
813
  const status = bawStatus()
@@ -662,6 +829,13 @@ async function main() {
662
829
  })
663
830
  }
664
831
  case 'list': {
832
+ // --kind v3 / fair: GangLaunchpad tokens (from gang.fun); without it the V2 / Soft / U / S pads, as before
833
+ if (flag('kind')) {
834
+ const kind = opt('kind')
835
+ if (kind === 'v3') return runV3('list', { ...PAD_IO, notes: padNotes('v3', 'list') })
836
+ if (kind === 'fair') return runFair('list', { ...PAD_IO, notes: padNotes('fair', 'list') })
837
+ fail('--kind must be v3 or fair (leave it out for the V2 / Soft / U / S tokens)', { got: kind ?? null })
838
+ }
665
839
  const me = bawStatus() === 'CONNECTED' ? bawAddress() : undefined
666
840
  return out({ success: true, createPad: padNets('v3').mainnet.pad, tokens: await listTokens(me) })
667
841
  }
@@ -801,13 +975,21 @@ async function main() {
801
975
  await Promise.allSettled([...hooks])
802
976
  return
803
977
  }
804
- case 'token': {
805
- const token = opt('token'); if (!token) fail('--token required')
806
- const me = bawStatus() === 'CONNECTED' ? bawAddress() : undefined
807
- return out({ success: true, ...(await tokenInfo(token, me)) })
808
- }
978
+ case 'info':
979
+ case 'token': // the older name of gang info
980
+ return routed('info')
981
+ case 'status':
982
+ case 'claim':
983
+ case 'mint':
984
+ case 'withdraw':
985
+ case 'release':
986
+ return routed(cmd)
809
987
  case 'quote': {
810
- const token = opt('token'), bnb = opt('bnb'); if (!token || !bnb) fail('--token and --bnb required (Soft archive: BNB; V2: quote ERC-20 amount)')
988
+ const token = tokenArg(), bnb = spendArg()
989
+ if (!bnb) fail('--amount required (Soft archive: BNB; V2: quote ERC-20 amount; --bnb is the older name)')
990
+ if ((await padKind(token, 'quote')) !== 'legacy') {
991
+ fail('gang quote prices V2 / Soft / U / S curve buys; on GangLaunchpad, gang buy simulates the trade and shows expectTokens (nothing is sent before gang execute)', { token })
992
+ }
811
993
  const me = bawAddress()
812
994
  const q = await quoteBuy(token, me, bnb)
813
995
  return out({
@@ -819,23 +1001,21 @@ async function main() {
819
1001
  : (q.deterministic ? 'V2 quote = LaunchpadV2.sol curve math (unique path; no Soft/U/S fallback).' : undefined),
820
1002
  })
821
1003
  }
822
- case 'buy': {
823
- const token = opt('token'), bnb = opt('bnb'); if (!token || !bnb) fail('--token and --bnb required')
824
- return buy(token, bnb, parseNote(), parseFollow())
825
- }
1004
+ case 'buy':
1005
+ return routed('buy')
826
1006
  case 'follow': {
827
- // gang follow <note tx> --bnb <n>: the token comes from the note
1007
+ // gang follow <note tx> --amount <n>: the token comes from the note
828
1008
  const noteTx = args[1] && !args[1].startsWith('--') ? args[1].toLowerCase() : null
829
- if (!NOTE_TX.test(noteTx ?? '')) fail('usage: gang follow <note tx> --bnb <n> (the note tx is 0x + 64 hex)', { got: noteTx })
830
- if (flag('token')) fail('gang follow takes the token from the note; to name it yourself use `gang buy --token … --bnb … --follow <note tx>`')
831
- if (flag('follow')) fail('give the note tx once: `gang follow <note tx> --bnb <n>`')
832
- const bnb = opt('bnb'); if (!bnb) fail('--bnb required (the amount to buy, in the token\'s quote unit)')
1009
+ if (!NOTE_TX.test(noteTx ?? '')) fail('usage: gang follow <note tx> --amount <n> (the note tx is 0x + 64 hex)', { got: noteTx })
1010
+ if (flag('token')) fail('gang follow takes the token from the note; to name it yourself use `gang buy --token … --amount … --follow <note tx>`')
1011
+ if (flag('follow')) fail('give the note tx once: `gang follow <note tx> --amount <n>`')
1012
+ const bnb = spendArg(); if (!bnb) fail('--amount required (the amount to buy, in the token\'s quote unit; --bnb is the older name)')
833
1013
  const note = parseNote() // a bad --note fails before any API call, as on buy
834
1014
  // checked without a token: gang.fun answers with the note's token, and buy() reuses this check. The wallet is the
835
1015
  // one that buys: --from builds for another one (gang v3 buy takes it on the curve; buy() refuses it there)
836
1016
  const { token, follow } = await confirmFollow(noteTx, fromArg() || bawAddress())
837
1017
  if (!token) fail('gang.fun did not say which token this note is about', { noteTx })
838
- // a GangLaunchpad token: the same buy as `gang v3 buy` (--bnb = the quote to spend)
1018
+ // a GangLaunchpad token: the V3 curve buy (--amount / --bnb = the quote to spend)
839
1019
  const onPad = await kindOf(v2PoolCtx(), token)
840
1020
  if (onPad === 'fair') fail('this note is on a 打新 (fair-mint) token: only curve buys can follow a note', { noteTx, token })
841
1021
  if (onPad === 'v3') {
@@ -843,51 +1023,8 @@ async function main() {
843
1023
  }
844
1024
  return buy(token, bnb, note, noteTx, follow)
845
1025
  }
846
- case 'sell': {
847
- const token = opt('token'); if (!token) fail('--token required')
848
- const note = parseNote()
849
- parseFollow() // refuses --follow: only buys follow a note
850
- if (flag('testnet')) fail(NOT_TESTNET)
851
- const from = fromArg()
852
- const me = from || bawAddress()
853
- const info = await tokenInfo(token, me)
854
- if (from && !info.graduated) fail(FROM_CURVE)
855
- if (!flag('all') && !opt('amount')) fail('pass --amount <tokens> or --all (full sell is no longer the default)')
856
- if (flag('all') && opt('amount')) fail('use either --all or --amount, not both')
857
- // graduated: sell into the PancakeSwap pool through GangRouter
858
- if (info.graduated) {
859
- return v2PoolTrade('sell', token, info, me, note, { amount: flag('all') ? null : parseAmount(fail, 'amount', opt('amount')), all: flag('all') })
860
- }
861
- const bal = await balanceOf(token, me)
862
- if (bal === 0n) fail('no balance to sell')
863
- const amount = flag('all') ? bal : parseEther(opt('amount'))
864
- if (amount > bal) fail('amount exceeds balance', { balance: fmtTok(bal) })
865
- const pad = await resolvePad(token)
866
- const allow = await allowance(token, me, pad.address)
867
- if (allow < amount) {
868
- const p = bawPreview({ from: me, to: token, data: calldata.approve(pad.address, amount) })
869
- return out(summarizePreview(p, {
870
- step: 'approve', token, symbol: info.symbol, pad: pad.id, amount: fmtTok(amount),
871
- note: 'Approve first. After it confirms on-chain, run the same `gang sell` again to preview the sell.',
872
- ...noteSkipped(note),
873
- }))
874
- }
875
- const slippage = parseSlippage(opt('slippage', '1'))
876
- const q = await quoteSell(token, amount)
877
- const minOut = (q.out * BigInt(Math.round((100 - slippage) * 100))) / 10000n
878
- if (pad.kind === 'soft') {
879
- const p = bawPreview({ from: me, to: pad.address, data: withNote(calldata.softSell(token, amount, minOut), note) })
880
- return out(summarizeWithNote(p, {
881
- step: 'sell', kind: 'soft', token, symbol: info.symbol, pad: pad.id,
882
- amount: fmtTok(amount), expectOut: q.outFmt, fee: q.feeFmt, minOut: fmt(minOut), unit: 'BNB',
883
- }, note, 'sell'))
884
- }
885
- const p = bawPreview({ from: me, to: pad.address, data: withNote(calldata.v2Sell(token, amount, minOut), note) })
886
- return out(summarizeWithNote(p, {
887
- step: 'sell', kind: 'v2', token, symbol: info.symbol, pad: pad.id, quote: info.quote,
888
- amount: fmtTok(amount), expectOut: q.outFmt, fee: q.feeFmt, minOut: fmt(minOut), unit: 'quote', quotePath: q.quotePath, deterministic: q.deterministic || false,
889
- }, note, 'sell'))
890
- }
1026
+ case 'sell':
1027
+ return routed('sell')
891
1028
  case 'upload-logo': {
892
1029
  const file = opt('file') || opt('path') || opt('logo')
893
1030
  const url = opt('url')
@@ -902,7 +1039,7 @@ async function main() {
902
1039
  if (flag('pair-or-quote') || flag('pair')) {
903
1040
  fail('--pair-or-quote was removed (CLI ≤0.3.0 encoded a create the contract does not have). Use --quote <ERC-20> --ref-pool <Pancake pair/pool holding it>; omit both for WBNB.')
904
1041
  }
905
- return runV3('create', { ...PAD_IO, notes: padNotes('v3', 'create') })
1042
+ return create()
906
1043
  }
907
1044
  case 'execute': {
908
1045
  const id = opt('request-id'); if (!id) fail('--request-id required')
@@ -934,8 +1071,18 @@ async function main() {
934
1071
  })
935
1072
  }
936
1073
  case 'tx': {
937
- const hash = opt('hash'); if (!hash) fail('--hash required')
938
- const t = await decodeTx(hash)
1074
+ const hash = opt('hash'); if (!/^0x[0-9a-fA-F]{64}$/.test(hash ?? '')) fail('--hash must be a tx hash (0x + 64 hex)')
1075
+ if (flag('testnet')) {
1076
+ // BSC testnet: GangLaunchpad only (no V2 / Soft there, and gang.fun indexes no testnet notes)
1077
+ let ctx
1078
+ try { ctx = v3Ctx(true) } catch (e) { fail(e.message) }
1079
+ const r = await ctx.client.waitForTransactionReceipt({ hash, timeout: 120_000 })
1080
+ return out({
1081
+ success: true, chainId: Number(ctx.chainId), hash, status: r.status, block: Number(r.blockNumber),
1082
+ events: decodeLogs(r.logs, GANG_EVENT_ABIS), explorer: `${ctx.explorer}/tx/${hash}`,
1083
+ })
1084
+ }
1085
+ const t = await decodeTx(hash, GANG_EVENT_ABIS)
939
1086
  // the input may carry a follow ref and a note commitment: a committed note is sent here if it's still in the
940
1087
  // local queue (e.g. after PENDING_CONFIRMATION), or its state reported / refreshed if already sent
941
1088
  const queued = hasQueued()
package/src/pool.js CHANGED
@@ -3,7 +3,7 @@ import { decimalsOf, isMissingFunction, poolSellQuote, ROUTER_ABI, routerDeadlin
3
3
 
4
4
  /**
5
5
  * PancakeSwap (V2 pool) buys and sells of a graduated / launched token through GangRouter — V3 and 打新 tokens
6
- * (`gang v3 buy|sell`, `gang fair buy|sell`) and V2 / Soft / U / S tokens (`gang buy|sell`), all the same way:
6
+ * (V3 / 打新, also as `gang v3|fair buy|sell`) and V2 / Soft / U / S tokens (`gang buy|sell`), all the same way:
7
7
  * poolBuyBNB(token, quote, minTokensOut, deadline) payable path [WBNB, token] or [WBNB, quote, token]
8
8
  * poolBuy(token, quote, quoteIn, minTokensOut, deadline) path [quote, token]; approve quote → router first
9
9
  * poolSell(token, quote, amountIn, minQuoteOut, deadline) path [token, quote]; approve token → router first
package/src/steps.js CHANGED
@@ -263,7 +263,7 @@ export async function swapBackState(ctx, handler, token, reserveToken) {
263
263
  }
264
264
 
265
265
  /** Hint for selling a graduated / launched token on PancakeSwap with the minOut of a pool quote (poolSellQuote) as amountOutMin. */
266
- export const POOL_SELL_HINT = '`gang v3 sell` / `gang fair sell` build this sell through GangRouter (poolSell / --to-bnb poolSellForBNB, minOut as here); '
266
+ export const POOL_SELL_HINT = '`gang sell` builds this sell through GangRouter (poolSell / --to-bnb poolSellForBNB, minOut as here); '
267
267
  + 'or sell on PancakeSwap with swapExactTokensForTokensSupportingFeeOnTransferTokens (the token takes its sell tax in the transfer), passing minOut (poolQuote.minOut / poolSellAll.minOut, set by --slippage) as amountOutMin so the sell reverts rather than fills below it; '
268
268
  + 'the quote includes the small chunk of tax the handler sells right before a sell and assumes the tx lands in a new block (previewSellNextBlock). '
269
269
  + 'Let the wallet estimate gas, or give at least 750k: a sell that triggers a chunk uses about 660k (about 330k without), and too little gas reverts with SwapBackOutOfGas'
package/src/v3.js CHANGED
@@ -69,7 +69,7 @@ const HINTS = {
69
69
  BadTaxConfig: 'a tax setting is out of bounds, or the recipient is neither you nor on the whitelist: see reason',
70
70
  NotAgent: 'this wallet holds no Agent ID; V3 create and curve trades are Agent-only',
71
71
  WalletCapReached: 'this wallet already holds the per-wallet cap (1% of supply)',
72
- AlreadyGraduated: 'the token graduated: re-run gang v3 buy / sell, which now trade it in the PancakeSwap pool',
72
+ AlreadyGraduated: 'the token graduated: re-run gang buy / gang sell, which now trade it in the PancakeSwap pool',
73
73
  Slippage: 'the price moved past --slippage; re-run to re-quote',
74
74
  RefPoolRequired: 'a non-WBNB --quote needs --ref-pool: a Pancake V2 pair or V3 pool that holds the quote token',
75
75
  QuoteNotInPool: '--ref-pool does not contain the --quote token',
@@ -90,6 +90,9 @@ const HINTS = {
90
90
  VaultFloorTooHigh: 'the platform\'s dividend vault requires a larger holding than V3 dividends allow (10,000 tokens), so a dividend token can\'t be created right now; --dividend 0 still works, or ask gang to fix the vault setting',
91
91
  }
92
92
 
93
+ /** What `gang tx` decodes a V3 trade's logs with (the pad, the tax router, the dividend vault). */
94
+ export const V3_EVENT_ABIS = [PAD_ABI, TAX_ROUTER_ABI, VAULT_ABI]
95
+
93
96
  export function v3Ctx(testnet) {
94
97
  const net = V3_NETS[testnet ? 'testnet' : 'mainnet']
95
98
  if (!net.pad) throw new Error(NOT_ON_MAINNET)
@@ -234,7 +237,7 @@ export async function runV3(sub, { opt, flag, out, fail, summarize, resolveLogo,
234
237
  const path = recipient ? `/v3/recipients/${recipient.toLowerCase()}?limit=20` : `/v3/tokens?status=${status}&limit=30`
235
238
  const r = await fetch(`${API}${path}`, { headers: { 'user-agent': UA }, signal: AbortSignal.timeout(10_000) })
236
239
  .then((x) => (x.ok ? x.json() : { error: x.status })).catch((e) => ({ error: e.message }))
237
- if (r.error) return out({ success: false, error: `gang.fun V3 data unavailable (${r.error})`, hint: `use gang v3 info --token <addr>${net} for one token (reads the chain)` })
240
+ if (r.error) return out({ success: false, error: `gang.fun V3 data unavailable (${r.error})`, hint: `use gang info --token <addr>${net} for one token (reads the chain)` })
238
241
  // the quote's own decimals from gang.fun (18 when an older server does not send them)
239
242
  const f = (x, d) => (x == null ? null : formatUnits(BigInt(x), d ?? 18))
240
243
  if (recipient) {
@@ -308,7 +311,7 @@ export async function runV3(sub, { opt, flag, out, fail, summarize, resolveLogo,
308
311
  if (d.claimableRaw === 0n) {
309
312
  return fail('nothing to claim yet', {
310
313
  ...(d.creditedUnsoldTokens && Number(d.creditedUnsoldTokens) > 0 && { creditedUnsoldEstimate: d.creditedUnsoldEstimate, note: d.note }),
311
- hint: `gang v3 status --token ${token}${testnet ? ' --testnet' : ''}`,
314
+ hint: `gang status --token ${token}${testnet ? ' --testnet' : ''}`,
312
315
  })
313
316
  }
314
317
  const sim = await simulate(ctx, me, vault, VAULT_ABI, 'claim', [token])
@@ -388,7 +391,7 @@ export async function runV3(sub, { opt, flag, out, fail, summarize, resolveLogo,
388
391
  ? `of each ${buyTax}% buy / ${sellTax}% sell tax in the PancakeSwap pool (after graduation; the curve charges no tax): ${fees.platformTaxPct}% of volume to the platform, then ${dividend}% of the rest to holders, ${rest}% to the recipient in the quote`
389
392
  + (dividend > 0 ? '; if no holder has 10,000+ tokens when a tax is paid, the holders\' part goes to the recipient too' : '')
390
393
  : 'untaxed: no tax in the PancakeSwap pool after graduation, no dividend, no recipient',
391
- note: `${taxed ? 'tax rates and recipient are' : 'untaxed is'} fixed at create. Curve trades pay only the ${fees.curveFeePct}% fee, no tax${taxed ? ': the tax starts in the pool after graduation' : ''}. tokenIfNext changes if another create lands first: the real address is the Created event from \`gang v3 tx --hash …\`.`,
394
+ note: `${taxed ? 'tax rates and recipient are' : 'untaxed is'} fixed at create. Curve trades pay only the ${fees.curveFeePct}% fee, no tax${taxed ? ': the tax starts in the pool after graduation' : ''}. tokenIfNext changes if another create lands first: the real address is the Created event from \`gang tx --hash …\`.`,
392
395
  }, 'create'))
393
396
  }
394
397