@masterpeach/launchpad-sdk 0.0.0-experimental-20260818121657
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 +262 -0
- package/README.zh-CN.md +242 -0
- package/dist/abi/index.cjs +2786 -0
- package/dist/abi/index.cjs.map +1 -0
- package/dist/abi/index.d.cts +2121 -0
- package/dist/abi/index.d.ts +2121 -0
- package/dist/abi/index.js +19 -0
- package/dist/abi/index.js.map +1 -0
- package/dist/chunk-PGZCTPUB.js +127 -0
- package/dist/chunk-PGZCTPUB.js.map +1 -0
- package/dist/chunk-SH2Y5F5L.js +2754 -0
- package/dist/chunk-SH2Y5F5L.js.map +1 -0
- package/dist/core.cjs +163 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +48 -0
- package/dist/core.d.ts +48 -0
- package/dist/core.js +27 -0
- package/dist/core.js.map +1 -0
- package/dist/index.cjs +4420 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1273 -0
- package/dist/index.d.ts +1273 -0
- package/dist/index.js +1840 -0
- package/dist/index.js.map +1 -0
- package/dist/transaction-BiLcoXyx.d.cts +13 -0
- package/dist/transaction-BiLcoXyx.d.ts +13 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# MasterPeach Launchpad SDK
|
|
2
|
+
|
|
3
|
+
[简体中文](./README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Typed, Viem-only TypeScript SDK for the ARC Launchpad. It covers deployment resolution and
|
|
6
|
+
verification, generated ABI bindings, on-chain reads and quotes, exact-amount approvals,
|
|
7
|
+
simulate-then-send write pipelines, and receipt event decoding.
|
|
8
|
+
|
|
9
|
+
The SDK's only consumer-provided dependency is `viem`. Shared amount, chain, transaction, and error
|
|
10
|
+
primitives are bundled and re-exported from the root entry point. It ships no React hooks, no Wagmi
|
|
11
|
+
adapter, and no Indexer client — those belong in the application layer.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @masterpeach/launchpad-sdk viem
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Entry points
|
|
20
|
+
|
|
21
|
+
| Import | Contents |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `@masterpeach/launchpad-sdk` | Client, actions, domain types, deployments, errors, pure helpers |
|
|
24
|
+
| `@masterpeach/launchpad-sdk/abi` | Generated ABI bindings and the artifact commit |
|
|
25
|
+
|
|
26
|
+
Frontend integration is covered in depth in
|
|
27
|
+
[docs/integration-guide.en.md](./docs/integration-guide.en.md)
|
|
28
|
+
([简体中文](./docs/integration-guide.md)), including a Wagmi adapter, query keys, and a migration
|
|
29
|
+
checklist.
|
|
30
|
+
|
|
31
|
+
## Deployments and capabilities
|
|
32
|
+
|
|
33
|
+
Arc is **testnet-only** today; no mainnet exists. The built-in production registry is therefore
|
|
34
|
+
empty by design and reserved for a future verified manifest. The Arc Testnet V2 deployment is
|
|
35
|
+
exported by name so it must be opted into explicitly.
|
|
36
|
+
|
|
37
|
+
The typed client is configured from a **chain config**, which carries the verified deployment *and*
|
|
38
|
+
the product capabilities. One object means there is no second source to drift, and the write gate
|
|
39
|
+
cannot be skipped by construction.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import {
|
|
43
|
+
arcTestnetLaunchpadDeployment,
|
|
44
|
+
createLaunchpadPortalClient,
|
|
45
|
+
defineLaunchpadChain,
|
|
46
|
+
verifyLaunchpadDeployment,
|
|
47
|
+
} from "@masterpeach/launchpad-sdk";
|
|
48
|
+
|
|
49
|
+
const chain = defineLaunchpadChain({
|
|
50
|
+
slug: "arc-testnet",
|
|
51
|
+
chainId: arcTestnetLaunchpadDeployment.chainId,
|
|
52
|
+
family: "evm",
|
|
53
|
+
nativeCurrency: { symbol: "USDC", decimals: 18 },
|
|
54
|
+
rpcUrls: ["https://rpc.testnet.arc.io"],
|
|
55
|
+
explorerUrl: "https://testnet.arcscan.app",
|
|
56
|
+
apiBaseUrl: "https://your-indexer.example",
|
|
57
|
+
realtimeUrl: "wss://rpc.testnet.arc.io",
|
|
58
|
+
deployment: arcTestnetLaunchpadDeployment,
|
|
59
|
+
quoteTokens: [
|
|
60
|
+
{ address: arcTestnetLaunchpadDeployment.contracts.quoteToken, decimals: 6, symbol: "tUSDC" },
|
|
61
|
+
],
|
|
62
|
+
capabilities: {
|
|
63
|
+
create: true,
|
|
64
|
+
bondingTrade: true,
|
|
65
|
+
graduatedTrade: false, // no audited router yet
|
|
66
|
+
antiFarmer: true,
|
|
67
|
+
earlySellTax: false,
|
|
68
|
+
rewards: false, // Locker claim off until the product confirms it
|
|
69
|
+
referral: false,
|
|
70
|
+
},
|
|
71
|
+
confirmations: 2,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Confirms chain id, Portal runtime bytecode hash, and every module binding before use.
|
|
75
|
+
await verifyLaunchpadDeployment(publicClient, chain.deployment);
|
|
76
|
+
|
|
77
|
+
const client = createLaunchpadPortalClient({ publicClient, walletClient, chain });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
When Arc mainnet ships, register its manifest the same way — call sites do not change.
|
|
81
|
+
|
|
82
|
+
Write paths are gated by these capabilities and throw `UnsupportedLaunchpadCapabilityError`:
|
|
83
|
+
`prepareCreateToken` needs `create`, `prepareBuy`/`prepareSell` need `bondingTrade`, and
|
|
84
|
+
`prepareClaimReward` needs `rewards` (off by default). `prepareMigration` and `prepareCollectFees`
|
|
85
|
+
are **not** gated — both are permissionless on-chain, and disabling migration would strand
|
|
86
|
+
graduated tokens while disabling collection would freeze fees that belong to creators. Reads are
|
|
87
|
+
never gated, so a page renders without a signer regardless of which writes are enabled.
|
|
88
|
+
|
|
89
|
+
## Reading
|
|
90
|
+
|
|
91
|
+
Every read accepts `blockNumber` / `blockTag`, so state, quotes, balances, and allowances can be
|
|
92
|
+
pinned to one block instead of stitched from different heights.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const blockNumber = await publicClient.getBlockNumber();
|
|
96
|
+
const [state, quote, availability] = await Promise.all([
|
|
97
|
+
client.getTokenState(token, { blockNumber }),
|
|
98
|
+
client.quoteBuy(token, parseUnits("100", 6), { blockNumber }),
|
|
99
|
+
client.getTradeAvailability(token, { blockNumber }),
|
|
100
|
+
]);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`getTradeAvailability` and `getPrice` are composite reads and are **strictly same-block**: they
|
|
104
|
+
resolve one height first, then pin their sub-reads to it, so a fee can never be computed for one
|
|
105
|
+
block from another block's state. `latest`, `safe`, `finalized`, and an explicit height behave
|
|
106
|
+
identically; `pending` has no stable height and is rejected with `UnpinnableBlockTagError`.
|
|
107
|
+
|
|
108
|
+
`getPrice` returns the **bonding-curve reference price**, tagged with its `source` and the `phase`
|
|
109
|
+
observed at read time. It is never an executable quote, it must not be used to derive a `minOut`,
|
|
110
|
+
and after graduation it does not track the V3 pool — read the official pool or your indexer for a
|
|
111
|
+
live DEX price.
|
|
112
|
+
|
|
113
|
+
## Trading
|
|
114
|
+
|
|
115
|
+
Writes follow one pipeline: validate → refresh quote → derive a non-zero minimum → check the exact
|
|
116
|
+
allowance → `simulateContract` → `writeContract`. Nothing is broadcast without a simulation.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { toDeadline } from "@masterpeach/launchpad-sdk";
|
|
120
|
+
|
|
121
|
+
const block = await publicClient.getBlock();
|
|
122
|
+
const prepared = await client.prepareBuy({
|
|
123
|
+
token,
|
|
124
|
+
quoteIn,
|
|
125
|
+
slippage: { bps: 100 }, // capped at 10%
|
|
126
|
+
deadline: toDeadline(block.timestamp),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const hash = await client.send(prepared);
|
|
130
|
+
const result = await client.wait(hash);
|
|
131
|
+
const trade = result.events.trades[0];
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Approvals are **exact-amount** by default and the spender is always the Portal. An unlimited
|
|
135
|
+
allowance requires passing `maxUint256` explicitly. For a buy, approve `quote.quoteSpent` rather
|
|
136
|
+
than the user's typed input — a graduating buy spends less than requested.
|
|
137
|
+
|
|
138
|
+
### Creating a token
|
|
139
|
+
|
|
140
|
+
Pass `slippage` and the SDK prices the creator's atomic first buy against the deployed curve, then
|
|
141
|
+
derives a safe non-zero `minInitialTokenOut` from it:
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const prepared = await client.prepareCreateToken({
|
|
145
|
+
name,
|
|
146
|
+
symbol,
|
|
147
|
+
metadata: JSON.stringify({ description, image, website }),
|
|
148
|
+
userSalt: keccak256(crypto.getRandomValues(new Uint8Array(32))),
|
|
149
|
+
initialQuoteIn, // 0n for no first buy
|
|
150
|
+
slippage: { bps: 100 }, // required when initialQuoteIn > 0
|
|
151
|
+
deadline: toDeadline(block.timestamp),
|
|
152
|
+
protectionBlocks: 6, // 0..6
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
prepared.preview.firstBuy; // the full quote behind the minimum
|
|
156
|
+
prepared.preview.minInitialTokenOut; // what will actually be submitted
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
The allowance is checked against `quoteSpent` rather than `initialQuoteIn`, because a first buy that
|
|
160
|
+
reaches graduation is truncated at the threshold and transfers less than committed — while the
|
|
161
|
+
simulation still submits the raw `initialQuoteIn`, since the Portal performs that truncation itself.
|
|
162
|
+
`minInitialTokenOut` may be supplied instead of `slippage` when you compute it yourself; it must be
|
|
163
|
+
positive and within the fresh quote, so there is no unsafe zero-minimum path.
|
|
164
|
+
|
|
165
|
+
### prepare → send is re-validated
|
|
166
|
+
|
|
167
|
+
A prepared transaction carries the chain, account, and Portal it was simulated under, and `send`
|
|
168
|
+
re-reads the wallet's live chain and account before broadcasting:
|
|
169
|
+
|
|
170
|
+
| Situation | Error |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| Wallet switched network after prepare | `WrongWalletChainError` |
|
|
173
|
+
| Wallet switched account after prepare | `PreparedAccountMismatchError` |
|
|
174
|
+
| Prepared for another deployment, or not minted by this client | `ForgedPreparedTransactionError` |
|
|
175
|
+
|
|
176
|
+
Prepare and send through the same client instance, and re-prepare after any wallet change rather
|
|
177
|
+
than retrying a stale prepared value.
|
|
178
|
+
|
|
179
|
+
## Events
|
|
180
|
+
|
|
181
|
+
`Trade.quoteAmount` is asymmetric on-chain: the Portal emits the **net** quote on a buy and the
|
|
182
|
+
**gross** quote on a sell. The decoder preserves the raw value and adds unambiguous signed deltas,
|
|
183
|
+
so volume is never computed from a field that changes meaning by direction.
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const { trades, tokenCreated, migrationStarted } = result.events;
|
|
187
|
+
trades[0].traderQuoteDelta; // buy: -(raw + fee), sell: +(raw - fee)
|
|
188
|
+
trades[0].reserveQuoteDelta; // buy: +raw, sell: -raw
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The created token address comes from the `TokenCreated` event, never from a predicted address.
|
|
192
|
+
|
|
193
|
+
## Errors
|
|
194
|
+
|
|
195
|
+
Contract failures are classified by decoded custom-error name, not message text:
|
|
196
|
+
|
|
197
|
+
| Contract revert | SDK error |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `InvalidStatus` | `LaunchpadPhaseUnavailableError` |
|
|
200
|
+
| `ProtocolPaused` | `LaunchpadTradingPausedError` |
|
|
201
|
+
| `LaunchBlockTradingDisabled` | `LaunchpadProtectionError` |
|
|
202
|
+
| `SlippageExceeded` | `LaunchpadSlippageError` |
|
|
203
|
+
| `DeadlineExpired` | `LaunchpadDeadlineExpiredError` |
|
|
204
|
+
| insufficient allowance | `LaunchpadApprovalRequiredError` |
|
|
205
|
+
| insufficient balance | `LaunchpadInsufficientBalanceError` |
|
|
206
|
+
| `OfficialPoolLocked` | `LaunchpadOfficialPoolLockedError` |
|
|
207
|
+
| unregistered LP NFT id | `LaunchpadUnknownPositionError` |
|
|
208
|
+
| wallet on the wrong chain | `WrongWalletChainError` |
|
|
209
|
+
| wallet switched account after prepare | `PreparedAccountMismatchError` |
|
|
210
|
+
| prepared value from elsewhere | `ForgedPreparedTransactionError` |
|
|
211
|
+
| capability disabled for this chain | `UnsupportedLaunchpadCapabilityError` |
|
|
212
|
+
| non-pinnable block tag on a composite read | `UnpinnableBlockTagError` |
|
|
213
|
+
|
|
214
|
+
Unrecognized failures fall back to the SDK's shared error codes with the cause preserved.
|
|
215
|
+
|
|
216
|
+
### Using more than one MasterPeach SDK
|
|
217
|
+
|
|
218
|
+
Shared primitives are exported from the `./core` subpath, never from the root entry point, so the
|
|
219
|
+
root entry points of two MasterPeach SDKs never export the same name. Import them from one SDK's
|
|
220
|
+
`./core`:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { isPeachSdkError, parseAmount } from "@masterpeach/launchpad-sdk/core";
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Each SDK still bundles its own copy, so the shared error classes are distinct objects in each
|
|
227
|
+
package. Use the `isPeachSdkError` guard, not `instanceof`:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { isPeachSdkError } from "@masterpeach/launchpad-sdk/core";
|
|
231
|
+
|
|
232
|
+
if (isPeachSdkError(error, "UNSUPPORTED_CHAIN")) {
|
|
233
|
+
// Matches whether launchpad-sdk or lending-sdk raised it.
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The guard brands errors through the global symbol registry, so every copy recognizes the others'.
|
|
238
|
+
`PeachSdkErrorCode` values are the stable contract to branch on. Launchpad-specific errors above
|
|
239
|
+
exist only in this package and are exported from the root entry point, so `instanceof
|
|
240
|
+
LaunchpadError` remains correct for them.
|
|
241
|
+
|
|
242
|
+
## Regenerating the ABI
|
|
243
|
+
|
|
244
|
+
ABI is generated from Foundry artifacts built at a pinned contract revision; it is never
|
|
245
|
+
hand-written or copied from an application.
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# in the contracts repo, at the pinned revision
|
|
249
|
+
forge build
|
|
250
|
+
|
|
251
|
+
pnpm abi:generate \
|
|
252
|
+
--config tooling/abi-codegen/launchpad-artifacts.json \
|
|
253
|
+
--artifacts-root <contracts>/contracts/out \
|
|
254
|
+
--output-dir packages/launchpad/src/abi/generated \
|
|
255
|
+
--artifact-commit <40-char commit>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Scope
|
|
259
|
+
|
|
260
|
+
Not included: Tax tokens, referrals, bridges, DEX aggregator swaps, protocol-owner entry points,
|
|
261
|
+
or V3 LP management. Graduated (post-migration) trading stays quote-only until an audited router
|
|
262
|
+
contract exists.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# MasterPeach Launchpad SDK
|
|
2
|
+
|
|
3
|
+
[English](./README.md)
|
|
4
|
+
|
|
5
|
+
面向 ARC Launchpad 的类型化 TypeScript SDK,仅依赖 Viem。覆盖部署解析与校验、生成式 ABI 绑定、
|
|
6
|
+
链上只读与报价、精确额度授权、先模拟后广播的写入管线,以及回执事件解码。
|
|
7
|
+
|
|
8
|
+
SDK 唯一需要消费端提供的依赖是 `viem`。金额、网络、交易和错误等共享基础能力已内联,并从根入口
|
|
9
|
+
重新导出。SDK 不包含 React hooks、Wagmi adapter 或 Indexer 客户端 —— 这些属于应用层。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @masterpeach/launchpad-sdk viem
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 入口
|
|
18
|
+
|
|
19
|
+
| 导入路径 | 内容 |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `@masterpeach/launchpad-sdk` | 客户端、actions、领域类型、部署、错误、纯函数 |
|
|
22
|
+
| `@masterpeach/launchpad-sdk/abi` | 生成的 ABI 绑定与 artifact commit |
|
|
23
|
+
|
|
24
|
+
前端接入的完整说明见 [docs/integration-guide.md](./docs/integration-guide.md)
|
|
25
|
+
([English](./docs/integration-guide.en.md)),含 Wagmi adapter、query key 与迁移清单。
|
|
26
|
+
|
|
27
|
+
## 部署与 capability
|
|
28
|
+
|
|
29
|
+
Arc 目前**只有测试网**,正式网尚未上线。因此内置的生产 registry 刻意保持为空,作为未来经核验
|
|
30
|
+
manifest 的预留入口。Arc Testnet V2 以具名方式导出,必须显式接入。
|
|
31
|
+
|
|
32
|
+
类型化客户端由 **chain config** 构造,它同时携带经校验的 deployment **和**产品 capabilities。
|
|
33
|
+
配置只有一个真源,不存在第二处可漂移,写入门禁也无法被绕过。
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import {
|
|
37
|
+
arcTestnetLaunchpadDeployment,
|
|
38
|
+
createLaunchpadPortalClient,
|
|
39
|
+
defineLaunchpadChain,
|
|
40
|
+
verifyLaunchpadDeployment,
|
|
41
|
+
} from "@masterpeach/launchpad-sdk";
|
|
42
|
+
|
|
43
|
+
const chain = defineLaunchpadChain({
|
|
44
|
+
slug: "arc-testnet",
|
|
45
|
+
chainId: arcTestnetLaunchpadDeployment.chainId,
|
|
46
|
+
family: "evm",
|
|
47
|
+
nativeCurrency: { symbol: "USDC", decimals: 18 },
|
|
48
|
+
rpcUrls: ["https://rpc.testnet.arc.io"],
|
|
49
|
+
explorerUrl: "https://testnet.arcscan.app",
|
|
50
|
+
apiBaseUrl: "https://your-indexer.example",
|
|
51
|
+
realtimeUrl: "wss://rpc.testnet.arc.io",
|
|
52
|
+
deployment: arcTestnetLaunchpadDeployment,
|
|
53
|
+
quoteTokens: [
|
|
54
|
+
{ address: arcTestnetLaunchpadDeployment.contracts.quoteToken, decimals: 6, symbol: "tUSDC" },
|
|
55
|
+
],
|
|
56
|
+
capabilities: {
|
|
57
|
+
create: true,
|
|
58
|
+
bondingTrade: true,
|
|
59
|
+
graduatedTrade: false, // 尚无经审计的 Router
|
|
60
|
+
antiFarmer: true,
|
|
61
|
+
earlySellTax: false,
|
|
62
|
+
rewards: false, // Locker claim 在产品确认前保持关闭
|
|
63
|
+
referral: false,
|
|
64
|
+
},
|
|
65
|
+
confirmations: 2,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// 校验链 ID、Portal runtime bytecode hash 及全部模块绑定。
|
|
69
|
+
await verifyLaunchpadDeployment(publicClient, chain.deployment);
|
|
70
|
+
|
|
71
|
+
const client = createLaunchpadPortalClient({ publicClient, walletClient, chain });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
正式网上线后按同样方式注册 manifest,调用方代码无需改动。
|
|
75
|
+
|
|
76
|
+
写入路径受上述 capabilities 门控,不满足时抛 `UnsupportedLaunchpadCapabilityError`:
|
|
77
|
+
`prepareCreateToken` 需要 `create`,`prepareBuy`/`prepareSell` 需要 `bondingTrade`,
|
|
78
|
+
`prepareClaimReward` 需要 `rewards`(默认关闭)。`prepareMigration` 与 `prepareCollectFees` **不**受
|
|
79
|
+
门控 —— 两者在链上都是 permissionless;禁用迁移会让已毕业的 Token 卡住,禁用手续费收取则会冻住
|
|
80
|
+
本属于 creator 的收益。读取永不受门控,因此页面在无签名者时也能渲染。
|
|
81
|
+
|
|
82
|
+
## 读取
|
|
83
|
+
|
|
84
|
+
所有 read 都接受 `blockNumber` / `blockTag`,可以把状态、报价、余额和 allowance 固定在同一个区块,
|
|
85
|
+
避免拼接不同高度的数据。
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const blockNumber = await publicClient.getBlockNumber();
|
|
89
|
+
const [state, quote, availability] = await Promise.all([
|
|
90
|
+
client.getTokenState(token, { blockNumber }),
|
|
91
|
+
client.quoteBuy(token, parseUnits("100", 6), { blockNumber }),
|
|
92
|
+
client.getTradeAvailability(token, { blockNumber }),
|
|
93
|
+
]);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`getTradeAvailability` 与 `getPrice` 是组合读取,**严格同块**:先解析出一个确定高度,再把各子读取
|
|
97
|
+
固定到它,因此不会用某个区块的状态去计算另一个区块的费率。`latest`、`safe`、`finalized` 与显式高度
|
|
98
|
+
语义一致;`pending` 没有稳定高度,会以 `UnpinnableBlockTagError` 拒绝。
|
|
99
|
+
|
|
100
|
+
`getPrice` 返回的是**曲线参考价**,并标注 `source` 与读取时的 `phase`。它不是可执行报价,不能用来
|
|
101
|
+
计算 `minOut`;毕业之后它也不会跟随 V3 池价格,实时 DEX 价格请从官方 Pool 或 Indexer 获取。
|
|
102
|
+
|
|
103
|
+
## 交易
|
|
104
|
+
|
|
105
|
+
写入统一遵循:参数校验 → 刷新报价 → 计算非零最小输出 → 检查精确授权 → `simulateContract` →
|
|
106
|
+
`writeContract`。没有经过模拟的交易不会被广播。
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { toDeadline } from "@masterpeach/launchpad-sdk";
|
|
110
|
+
|
|
111
|
+
const block = await publicClient.getBlock();
|
|
112
|
+
const prepared = await client.prepareBuy({
|
|
113
|
+
token,
|
|
114
|
+
quoteIn,
|
|
115
|
+
slippage: { bps: 100 }, // 上限 10%
|
|
116
|
+
deadline: toDeadline(block.timestamp),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const hash = await client.send(prepared);
|
|
120
|
+
const result = await client.wait(hash);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
授权默认是**精确额度**,spender 恒为 Portal。无限授权必须显式传入 `maxUint256`。买入时应按
|
|
124
|
+
`quote.quoteSpent` 授权,而不是用户输入值 —— 触达毕业的买入实际花费会更少。
|
|
125
|
+
|
|
126
|
+
### 创建 Token
|
|
127
|
+
|
|
128
|
+
传入 `slippage`,SDK 会用已部署的 curve 为 creator 原子首购报价,并据此推导安全的非零
|
|
129
|
+
`minInitialTokenOut`:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const prepared = await client.prepareCreateToken({
|
|
133
|
+
name,
|
|
134
|
+
symbol,
|
|
135
|
+
metadata: JSON.stringify({ description, image, website }),
|
|
136
|
+
userSalt: keccak256(crypto.getRandomValues(new Uint8Array(32))),
|
|
137
|
+
initialQuoteIn, // 不首购则传 0n
|
|
138
|
+
slippage: { bps: 100 }, // initialQuoteIn > 0 时必填
|
|
139
|
+
deadline: toDeadline(block.timestamp),
|
|
140
|
+
protectionBlocks: 6, // 0..6
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
prepared.preview.firstBuy; // 推导最小值所依据的完整报价
|
|
144
|
+
prepared.preview.minInitialTokenOut; // 实际提交的值
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
allowance 按 `quoteSpent` 而非 `initialQuoteIn` 检查 —— 触达毕业阈值的首购会被截断,实际转账少于
|
|
148
|
+
承诺额;而 simulation 仍提交原始 `initialQuoteIn`,因为截断由 Portal 自己完成。若你自行计算最小值,
|
|
149
|
+
可用 `minInitialTokenOut` 替代 `slippage`,它必须为正且不超过实时报价,因此不存在零最小值的不安全
|
|
150
|
+
路径。
|
|
151
|
+
|
|
152
|
+
### prepare → send 会二次校验
|
|
153
|
+
|
|
154
|
+
prepared transaction 携带它被模拟时的 chain、account 与 Portal,`send` 在广播前会重新读取钱包的
|
|
155
|
+
实时 chain 与 account:
|
|
156
|
+
|
|
157
|
+
| 场景 | 错误 |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| prepare 后钱包切换了网络 | `WrongWalletChainError` |
|
|
160
|
+
| prepare 后钱包切换了账户 | `PreparedAccountMismatchError` |
|
|
161
|
+
| 属于其他 deployment,或非本 client 产出 | `ForgedPreparedTransactionError` |
|
|
162
|
+
|
|
163
|
+
prepare 与 send 请用同一个 client 实例;钱包发生任何变化后重新 prepare,不要重试过期的 prepared 值。
|
|
164
|
+
|
|
165
|
+
## 事件
|
|
166
|
+
|
|
167
|
+
链上 `Trade.quoteAmount` 语义不对称:买入记录**扣费后**的 net,卖出记录**扣费前**的 gross。
|
|
168
|
+
解码器保留原始值,并额外给出方向明确的有符号增量,避免用一个含义随方向变化的字段统计成交量。
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
trades[0].traderQuoteDelta; // 买: -(raw + fee),卖: +(raw - fee)
|
|
172
|
+
trades[0].reserveQuoteDelta; // 买: +raw, 卖: -raw
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
新建 Token 地址一律取自 `TokenCreated` 事件,不使用预测地址。
|
|
176
|
+
|
|
177
|
+
## 错误
|
|
178
|
+
|
|
179
|
+
合约失败按解码出的 custom error 名称分类,不依赖错误文本:
|
|
180
|
+
|
|
181
|
+
| 合约 revert | SDK 错误 |
|
|
182
|
+
| --- | --- |
|
|
183
|
+
| `InvalidStatus` | `LaunchpadPhaseUnavailableError` |
|
|
184
|
+
| `ProtocolPaused` | `LaunchpadTradingPausedError` |
|
|
185
|
+
| `LaunchBlockTradingDisabled` | `LaunchpadProtectionError` |
|
|
186
|
+
| `SlippageExceeded` | `LaunchpadSlippageError` |
|
|
187
|
+
| `DeadlineExpired` | `LaunchpadDeadlineExpiredError` |
|
|
188
|
+
| allowance 不足 | `LaunchpadApprovalRequiredError` |
|
|
189
|
+
| 余额不足 | `LaunchpadInsufficientBalanceError` |
|
|
190
|
+
| `OfficialPoolLocked` | `LaunchpadOfficialPoolLockedError` |
|
|
191
|
+
| LP NFT 编号未注册 | `LaunchpadUnknownPositionError` |
|
|
192
|
+
| 钱包链不匹配 | `WrongWalletChainError` |
|
|
193
|
+
| prepare 后切换了账户 | `PreparedAccountMismatchError` |
|
|
194
|
+
| prepared 值来源不明 | `ForgedPreparedTransactionError` |
|
|
195
|
+
| 该链 capability 已关闭 | `UnsupportedLaunchpadCapabilityError` |
|
|
196
|
+
| 组合读取传入不可固定的 block tag | `UnpinnableBlockTagError` |
|
|
197
|
+
|
|
198
|
+
未识别的失败回退到 SDK 的共享错误码,并保留 cause。
|
|
199
|
+
|
|
200
|
+
### 同时使用多个 MasterPeach SDK
|
|
201
|
+
|
|
202
|
+
共享基础能力统一从 `./core` 子路径导出,**不会**出现在根入口,因此两个 MasterPeach SDK 的根入口不存在
|
|
203
|
+
同名导出,同时引入不会冲突。需要共享能力时,从任一 SDK 的 `./core` 导入:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { isPeachSdkError, parseAmount } from "@masterpeach/launchpad-sdk/core";
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
每个 SDK 仍会内联各自的副本,因此共享错误类在各 Package 中是**互相独立的对象**。请使用
|
|
210
|
+
`isPeachSdkError` 判别,而不是 `instanceof`:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { isPeachSdkError } from "@masterpeach/launchpad-sdk/core";
|
|
214
|
+
|
|
215
|
+
if (isPeachSdkError(error, "UNSUPPORTED_CHAIN")) {
|
|
216
|
+
// 无论由 launchpad-sdk 还是 lending-sdk 抛出都成立。
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
该 Guard 通过全局 Symbol 注册表为错误打标记,因此每份副本都能识别其他副本抛出的错误。分支判断应以
|
|
221
|
+
`PeachSdkErrorCode` 取值为稳定契约。上表中的 Launchpad 专有错误只存在于本 Package 且从根入口导出,
|
|
222
|
+
仍应使用 `instanceof LaunchpadError` 判别。
|
|
223
|
+
|
|
224
|
+
## 重新生成 ABI
|
|
225
|
+
|
|
226
|
+
ABI 由固定合约 revision 的 Foundry artifact 生成,禁止手写或从应用侧复制。
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# 在合约仓库的固定 revision 上
|
|
230
|
+
forge build
|
|
231
|
+
|
|
232
|
+
pnpm abi:generate \
|
|
233
|
+
--config tooling/abi-codegen/launchpad-artifacts.json \
|
|
234
|
+
--artifacts-root <contracts>/contracts/out \
|
|
235
|
+
--output-dir packages/launchpad/src/abi/generated \
|
|
236
|
+
--artifact-commit <40 位 commit>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## 范围
|
|
240
|
+
|
|
241
|
+
不包含:Tax Token、Referral、跨链桥、聚合器 DEX Swap、协议 Owner 管理入口、V3 LP 管理。
|
|
242
|
+
毕业后的交易在经过审计的 Router 合约发布前保持 quote-only。
|