@gala-chain/launchpad-sdk 9.0.0-rc.0 → 9.0.0-rc.2
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/CHANGELOG.md +500 -10
- package/EXAMPLES.md +1 -1
- package/MIGRATING-TO-9.0.md +43 -1
- package/README.md +1 -1
- package/dist/LaunchpadSDK.d.ts +414 -219
- package/dist/LaunchpadSDK.d.ts.map +1 -1
- package/dist/api/LaunchpadAPI.d.ts +2 -140
- package/dist/api/LaunchpadAPI.d.ts.map +1 -1
- package/dist/auth/SessionAuthService.d.ts +32 -2
- package/dist/auth/SessionAuthService.d.ts.map +1 -1
- package/dist/auth/session.d.ts +59 -0
- package/dist/auth/session.d.ts.map +1 -0
- package/dist/auth/types.d.ts +6 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/constants/endpoints.d.ts +19 -1
- package/dist/constants/endpoints.d.ts.map +1 -1
- package/dist/constants/enums.d.ts +19 -0
- package/dist/constants/enums.d.ts.map +1 -1
- package/dist/constants/version.generated.d.ts +1 -1
- package/dist/errors/LaunchpadSDKError.d.ts +18 -0
- package/dist/errors/LaunchpadSDKError.d.ts.map +1 -1
- package/dist/index.browser.d.ts +36 -27
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.esm.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/launchpad/bundle-submission.d.ts +57 -2
- package/dist/launchpad/bundle-submission.d.ts.map +1 -1
- package/dist/launchpad/create-sale-validation.d.ts +19 -0
- package/dist/launchpad/create-sale-validation.d.ts.map +1 -0
- package/dist/launchpad/transport.d.ts +31 -1
- package/dist/launchpad/transport.d.ts.map +1 -1
- package/dist/launchpad/writes.d.ts +37 -11
- package/dist/launchpad/writes.d.ts.map +1 -1
- package/dist/native.cjs +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/native.d.ts.map +1 -1
- package/dist/native.esm.js +1 -1
- package/dist/schemas/launchpad.d.ts +3 -2
- package/dist/schemas/launchpad.d.ts.map +1 -1
- package/dist/schemas/primitives.d.ts +12 -2
- package/dist/schemas/primitives.d.ts.map +1 -1
- package/dist/schemas/user.d.ts +3 -3
- package/dist/schemas/validators.d.ts +3 -3
- package/dist/services/ApiKeyService.d.ts +1 -1
- package/dist/services/BanService.d.ts +1 -1
- package/dist/services/ChartService.d.ts.map +1 -1
- package/dist/services/ContentReactionService.d.ts +6 -18
- package/dist/services/ContentReactionService.d.ts.map +1 -1
- package/dist/services/EventsQueryService.d.ts +47 -0
- package/dist/services/EventsQueryService.d.ts.map +1 -0
- package/dist/services/GalaChainLockService.d.ts.map +1 -1
- package/dist/services/GalaChainTransferService.d.ts.map +1 -1
- package/dist/services/HealthService.d.ts +33 -0
- package/dist/services/HealthService.d.ts.map +1 -0
- package/dist/services/ImageService.d.ts +3 -3
- package/dist/services/ImageService.d.ts.map +1 -1
- package/dist/services/LaunchpadPriceService.d.ts.map +1 -1
- package/dist/services/LaunchpadService.d.ts +34 -42
- package/dist/services/LaunchpadService.d.ts.map +1 -1
- package/dist/services/MessagesService.d.ts +12 -44
- package/dist/services/MessagesService.d.ts.map +1 -1
- package/dist/services/ModeratorService.d.ts +2 -2
- package/dist/services/ModeratorService.d.ts.map +1 -1
- package/dist/services/OverseerService.d.ts +3 -3
- package/dist/services/OverseerService.d.ts.map +1 -1
- package/dist/services/PlatformStatsService.d.ts +1 -2
- package/dist/services/PlatformStatsService.d.ts.map +1 -1
- package/dist/services/PoolService.d.ts +31 -65
- package/dist/services/PoolService.d.ts.map +1 -1
- package/dist/services/RestrictedNamesService.d.ts +1 -1
- package/dist/services/RestrictedNamesService.d.ts.map +1 -1
- package/dist/services/StreamChatService.d.ts +8 -19
- package/dist/services/StreamChatService.d.ts.map +1 -1
- package/dist/services/StreamTokenServiceBase.d.ts +2 -1
- package/dist/services/StreamTokenServiceBase.d.ts.map +1 -1
- package/dist/services/StreamWebSocketService.d.ts +423 -451
- package/dist/services/StreamWebSocketService.d.ts.map +1 -1
- package/dist/services/StreamingService.d.ts +14 -15
- package/dist/services/StreamingService.d.ts.map +1 -1
- package/dist/services/TokenBanService.d.ts +1 -1
- package/dist/services/TradeService.d.ts +13 -9
- package/dist/services/TradeService.d.ts.map +1 -1
- package/dist/services/TradingQuotesService.d.ts.map +1 -1
- package/dist/services/UserService.d.ts +64 -20
- package/dist/services/UserService.d.ts.map +1 -1
- package/dist/services/realtime/event-catalog.d.ts +456 -0
- package/dist/services/realtime/event-catalog.d.ts.map +1 -0
- package/dist/services/shared/pagination-helpers.d.ts +22 -5
- package/dist/services/shared/pagination-helpers.d.ts.map +1 -1
- package/dist/services/shared/service-validators.d.ts.map +1 -1
- package/dist/signing/signers.d.ts +57 -1
- package/dist/signing/signers.d.ts.map +1 -1
- package/dist/types/api-key.dto.d.ts +1 -1
- package/dist/types/ban.dto.d.ts +21 -16
- package/dist/types/ban.dto.d.ts.map +1 -1
- package/dist/types/chat-messages.dto.d.ts +10 -72
- package/dist/types/chat-messages.dto.d.ts.map +1 -1
- package/dist/types/comments.dto.d.ts +8 -78
- package/dist/types/comments.dto.d.ts.map +1 -1
- package/dist/types/common.d.ts +40 -62
- package/dist/types/common.d.ts.map +1 -1
- package/dist/types/constraints.d.ts +47 -23
- package/dist/types/constraints.d.ts.map +1 -1
- package/dist/types/content-flag.dto.d.ts +5 -1
- package/dist/types/content-flag.dto.d.ts.map +1 -1
- package/dist/types/content-reactions.dto.d.ts +0 -4
- package/dist/types/content-reactions.dto.d.ts.map +1 -1
- package/dist/types/engagement-stats.dto.d.ts +16 -27
- package/dist/types/engagement-stats.dto.d.ts.map +1 -1
- package/dist/types/events.dto.d.ts +89 -0
- package/dist/types/events.dto.d.ts.map +1 -1
- package/dist/types/global-feed.dto.d.ts +23 -164
- package/dist/types/global-feed.dto.d.ts.map +1 -1
- package/dist/types/health.dto.d.ts +32 -0
- package/dist/types/health.dto.d.ts.map +1 -0
- package/dist/types/launchpad.dto.d.ts +99 -66
- package/dist/types/launchpad.dto.d.ts.map +1 -1
- package/dist/types/messages.dto.d.ts +168 -118
- package/dist/types/messages.dto.d.ts.map +1 -1
- package/dist/types/moderator.dto.d.ts +1 -1
- package/dist/types/options.dto.d.ts +1 -3
- package/dist/types/options.dto.d.ts.map +1 -1
- package/dist/types/overseer.dto.d.ts +2 -1
- package/dist/types/overseer.dto.d.ts.map +1 -1
- package/dist/types/platform-stats.dto.d.ts +3 -3
- package/dist/types/realtime-events.dto.d.ts +340 -0
- package/dist/types/realtime-events.dto.d.ts.map +1 -0
- package/dist/types/restricted-names.dto.d.ts +2 -2
- package/dist/types/restricted-names.dto.d.ts.map +1 -1
- package/dist/types/stream-chat.dto.d.ts +15 -14
- package/dist/types/stream-chat.dto.d.ts.map +1 -1
- package/dist/types/streaming.dto.d.ts +73 -104
- package/dist/types/streaming.dto.d.ts.map +1 -1
- package/dist/types/token-ban.dto.d.ts.map +1 -1
- package/dist/types/token-config.dto.d.ts +34 -19
- package/dist/types/token-config.dto.d.ts.map +1 -1
- package/dist/types/trades-query.dto.d.ts +8 -24
- package/dist/types/trades-query.dto.d.ts.map +1 -1
- package/dist/types/user.dto.d.ts +126 -50
- package/dist/types/user.dto.d.ts.map +1 -1
- package/dist/types/websocket-events.dto.d.ts +5 -5
- package/dist/types/websocket-events.dto.d.ts.map +1 -1
- package/dist/utils/address-formatter.d.ts +15 -0
- package/dist/utils/address-formatter.d.ts.map +1 -1
- package/dist/utils/auto-pagination.d.ts +1 -1
- package/dist/utils/auto-pagination.d.ts.map +1 -1
- package/dist/utils/error-factories.d.ts +8 -7
- package/dist/utils/error-factories.d.ts.map +1 -1
- package/dist/utils/errors.d.ts +54 -11
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/http.d.ts +24 -3
- package/dist/utils/http.d.ts.map +1 -1
- package/dist/utils/message-validators.d.ts.map +1 -1
- package/dist/utils/pagination-helpers.d.ts +17 -0
- package/dist/utils/pagination-helpers.d.ts.map +1 -1
- package/dist/utils/pagination-validation.d.ts +2 -2
- package/dist/utils/response-handlers.d.ts.map +1 -1
- package/dist/utils/service-validators.d.ts +6 -6
- package/dist/utils/service-validators.d.ts.map +1 -1
- package/dist/utils/validation-helpers.d.ts +18 -59
- package/dist/utils/validation-helpers.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +4 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/services/StreamingEventService.d.ts +0 -441
- package/dist/services/StreamingEventService.d.ts.map +0 -1
- package/dist/types/comment.dto.d.ts +0 -176
- package/dist/types/comment.dto.d.ts.map +0 -1
- package/dist/types/streaming-events.dto.d.ts +0 -605
- package/dist/types/streaming-events.dto.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 9.0.0-rc.
|
|
3
|
+
## 9.0.0-rc.2
|
|
4
|
+
|
|
5
|
+
Since 9.0.0-rc.1 (published). Matches launchpad-backend-service-v3 MR !50 (merge and deploy it before
|
|
6
|
+
the new filters work live).
|
|
7
|
+
|
|
8
|
+
- **Token names on DTO routes are 1-25 letters and digits** (the backend raised `@TokenNameField()` from
|
|
9
|
+
20 to 25): `TOKEN_NAME_DTO_CONSTRAINT` (`MAX_LENGTH` 25, pattern `{1,25}`, description
|
|
10
|
+
`1-25 letters and digits`) and `dtoTokenNameSchema` (`.max(25, 'Token name must be at most 25
|
|
11
|
+
characters')`). A 21-25 character name is sent now; 26 is a `ValidationError`.
|
|
12
|
+
- **`fetchPools` / `fetchAllPools` take `isGraduated` and `creatorAddress`.** `isGraduated` is a boolean
|
|
13
|
+
and BOTH values are sent (`"true"` / `"false"`: unlike `hasUpcomingShows` / `hasRecordings`, the
|
|
14
|
+
backend reads this one strictly); leave it out for no filter. `creatorAddress` is `eth|<40 hex>` or
|
|
15
|
+
`client|<24 hex>` (a `0x`-prefixed or bare 40-hex address is sent as `eth|...`); an empty string, an
|
|
16
|
+
array or any other text is a `ValidationError` and is never sent.
|
|
17
|
+
|
|
18
|
+
## 9.0.0-rc.1
|
|
19
|
+
|
|
20
|
+
Since 9.0.0-rc.0 (published): the sections "App-owned session" through "The stream socket" below
|
|
21
|
+
(the app-session, browser-wallet, bundle-confirmation, error-answer, path-parameter, `PageInfo`,
|
|
22
|
+
`fetchPools`, admin/overseer, trades/tokens/users/streaming and stream-socket contracts) and the
|
|
23
|
+
later entries of "BREAKING CHANGES" and "Fixes" are new in rc.1; everything else in this section is
|
|
24
|
+
rc.0's. Core peer floor is `>=1.0.0-rc.6 <2`.
|
|
4
25
|
|
|
5
26
|
launchpad-sdk is launchpad-only. Everything that speaks to the GalaChain gateway generically is
|
|
6
27
|
`@gala-chain/gala-core-sdk`'s (a peer dependency: install it beside launchpad-sdk); everything that
|
|
@@ -51,15 +72,23 @@ built declarations of both versions (`pnpm docs:migration`).
|
|
|
51
72
|
schemas are zod 4, and it is no longer also a dependency) and the shared libraries. No longer
|
|
52
73
|
dependencies: `@solana/web3.js`, `@solana/spl-token`, `dotenv`, `@modelcontextprotocol/sdk` (and,
|
|
53
74
|
since the bridge move, `bs58` and `json-stringify-deterministic`).
|
|
54
|
-
- **
|
|
55
|
-
|
|
56
|
-
`@solana/web3.js` and `@solana/spl-token`, core's optional peers)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
- **Bridging is explicit: the `bridge` loader.** The bridge, balance-on-chain, token-list and wrap
|
|
76
|
+
methods run on `@gala-chain/gala-core-sdk/bridge` (the one core entry that loads `ethers`,
|
|
77
|
+
`@solana/web3.js` and `@solana/spl-token`, core's optional peers), which the app hands over:
|
|
78
|
+
`new LaunchpadSDK({ ..., bridge: () => import('@gala-chain/gala-core-sdk/bridge') })` (type
|
|
79
|
+
`CoreBridgeLoader`; a page without a bundler passes a function resolving to the module it loaded
|
|
80
|
+
itself). The UMD bundle no longer looks for a `GalaCoreSDKBridge` global. launchpad-sdk
|
|
81
|
+
names neither that module nor Solana in any built bundle: a bundler (Vite, webpack, Metro)
|
|
82
|
+
resolves every load it sees, a lazy `import()` included, so the earlier `import()` inside the SDK
|
|
83
|
+
made every app's build need Solana (an app's Vite build failed with 13 `MISSING_EXPORT` errors on
|
|
84
|
+
`@solana/web3.js` / `@solana/spl-token`). Without the loader a bridge or wrap call is a
|
|
85
|
+
`ConfigurationError` (`field: 'bridge'`) naming it; with it, the module is loaded once per SDK at
|
|
86
|
+
the first such call, and a failed load (the peers are not installed) is a `ConfigurationError`
|
|
87
|
+
naming them, tried again at the next call. `check:pack` scans every built bundle of the packed
|
|
88
|
+
package for a `@gala-chain/gala-core-sdk/bridge` or `@solana/*` load (`scripts/runtime-specifiers.ts`).
|
|
89
|
+
A bridge or wrap call made with one wallet is refused (`ConfigurationError`, nothing sent) if
|
|
90
|
+
`setWallet` / `setWalletProvider` runs while `/bridge` loads, rather than running as the next
|
|
91
|
+
wallet. `getSupportedBridgeTokens()` is `async`.
|
|
63
92
|
- **The public surface re-exports nothing** (`tests/public-surface.test.ts` pins both entries' export
|
|
64
93
|
lists and fails on any export declared outside `src/`). Removed from the main and `/native`
|
|
65
94
|
entries: `generateEIP712Types` and `GalaChainSigner` (core's), the duplicate EIP-712 types
|
|
@@ -68,8 +97,469 @@ built declarations of both versions (`pnpm docs:migration`).
|
|
|
68
97
|
(core's), `generateUniqueKey` (core's `createUniqueKey`), and the demo `.env` loaders `getEnv`,
|
|
69
98
|
`getEnvOrThrow`, `loadEnvWithFallback`.
|
|
70
99
|
|
|
100
|
+
### App-owned session (a browser app's httpOnly-cookie JWT session)
|
|
101
|
+
|
|
102
|
+
- **`session` in `LaunchpadSDKConfig`.** A browser app (launchpad-frontend-v3) keeps its own JWT
|
|
103
|
+
session: the refresh token is an httpOnly cookie the backend sets for `cookieSession: true`
|
|
104
|
+
logins, and the app is the one token authority. With `session: { accessToken, onUnauthorized,
|
|
105
|
+
credentials? }` every launchpad-backend request takes its bearer from `accessToken()` (awaited per
|
|
106
|
+
request), a 401 goes to `onUnauthorized(rejectedToken)` and is retried once with the token it
|
|
107
|
+
returns (`null`: the 401 stands), the launchpad-backend client sends credentials (unless
|
|
108
|
+
`credentials: 'omit'`), and the SDK stores no token (services that attach "the JWT" attach
|
|
109
|
+
nothing; the client adds the session's bearer). No other host (gateway, bundler, dex-api, swap
|
|
110
|
+
backend) ever sees the session or credentials. Exclusive with `accessToken`. Pair it with
|
|
111
|
+
`signer` + `walletAddress`: `wallet` / `walletProvider` sign every launchpad request (`Sign`).
|
|
112
|
+
- **`loginWithSignature({ address, message, signature, publicKey? })`**: `POST /auth/login` with a
|
|
113
|
+
signature the caller made (a browser wallet signs `Sign in to Launchpad t:<ms>` itself); with a
|
|
114
|
+
session it asks for the cookie session and stores nothing. **`loginWithDevBypass(address)`**: the
|
|
115
|
+
backend's non-production signature bypass.
|
|
116
|
+
- With a session, `refreshToken()` is `POST /auth/refresh` with `{}`, no bearer and the cookie;
|
|
117
|
+
`logout({ allDevices, accessToken })` always revokes on the server (bearer as handed over, never
|
|
118
|
+
refreshed); `ensureValidToken()` is the session's current token. Auth-endpoint failures are an
|
|
119
|
+
`AuthError` with the backend's `statusCode` (401/403: the session was rejected; 429/5xx/none:
|
|
120
|
+
transient). `tests/app-session.test.ts` drives each through a real axios instance.
|
|
121
|
+
|
|
122
|
+
### Browser-app wallet signing
|
|
123
|
+
|
|
124
|
+
- **`BrowserWalletSigner.fromEip1193(provider, keyAddress, options?)`**: a browser app holds the
|
|
125
|
+
wallet's raw EIP-1193 provider, not an ethers one. The factory builds the ethers `BrowserProvider`
|
|
126
|
+
the typed-data path reads the network and the selected account from, keeps the raw `request` for
|
|
127
|
+
`signObject`, and takes the key as `0x` or `eth|` (a `client|` alias is refused: it names no key).
|
|
128
|
+
The bytes are those of the constructor path (bundler-typed-data and wallet-providers goldens
|
|
129
|
+
unchanged; `tests/wallet-signers-app.test.ts` compares both paths over a real `BrowserProvider`).
|
|
130
|
+
- **`WALLET_ACCOUNT_CHANGED`** (`LaunchpadSDKError`, `details.walletAccount` / `signerAccount`):
|
|
131
|
+
`BrowserWalletSigner` refuses to sign when the wallet's selected account is not its key. Same
|
|
132
|
+
message as before; it was a plain `Error`, which the bundler transport rewrapped as
|
|
133
|
+
`LAUNCHPAD_SIGNING_FAILED`.
|
|
134
|
+
- **`WALLET_USER_REJECTED`**: the bundler transport tells a decline in the wallet (gala-core-sdk's
|
|
135
|
+
`isWalletUserRejection`: `4001`, `ACTION_REJECTED`, Gala Wallet's decline, core's
|
|
136
|
+
`SIGNING_FAILED`/`user_rejected`) apart from other signing failures, with the wallet's code in
|
|
137
|
+
`details.walletCode`. Other failures stay `LAUNCHPAD_SIGNING_FAILED`; an SDK error passes through.
|
|
138
|
+
- **Gala Wallet typed data selects the account.** `GalaWalletSigner.signTypedData` switches the
|
|
139
|
+
extension (`setAddress`, where it offers it) to `selectAddress`, else the Ethereum account the
|
|
140
|
+
request names, inside gala-core-sdk's per-wallet select-then-sign queue
|
|
141
|
+
(`withGalaWalletIdentity`), and signs through the `wallet` option when one is given (it read
|
|
142
|
+
`window.gala` even then, while `signObject` used the option).
|
|
143
|
+
- **`GalaWalletSigner` option `verifyActiveAccount` (default `false`)**: the app's check before a Gala
|
|
144
|
+
Wallet signature. On, the signer reads the wallet's active account (`wallet.address`, else
|
|
145
|
+
`eth_accounts` when the wallet has not reported one since the page loaded) and refuses with
|
|
146
|
+
`WALLET_ACCOUNT_CHANGED` (`details.walletAccount` null when none was reported) unless it is the
|
|
147
|
+
signer's key; the RPC order is that check, `setAddress`, `eth_signTypedData`. Off by default so the
|
|
148
|
+
six recorded `galaWalletEthAddress` vectors of `bundler-typed-data.golden.json` are unchanged (the
|
|
149
|
+
check adds an `eth_accounts` request); the opt-in sequences are pinned by the new
|
|
150
|
+
`tests/golden/gala-wallet-verify-account.golden.json`. A signer that names no Ethereum key
|
|
151
|
+
(`client|` alias without an Ethereum `selectAddress`) is refused at construction
|
|
152
|
+
(`INVALID_SIGNER_OPTIONS`). `BrowserWalletSigner` needs no option: it always compares the wallet's
|
|
153
|
+
selected account with its key.
|
|
154
|
+
|
|
155
|
+
### Bundle confirmation for a browser app
|
|
156
|
+
|
|
157
|
+
- **`bundleSocketFactory` in `LaunchpadSDKConfig`** (passed to `BundlerTransport`): the app's own
|
|
158
|
+
bundler socket, with its reconnect settings. Each `wait()` adds its listener for the bundle's
|
|
159
|
+
event on the socket the factory returns and removes it when the wait ends; that socket is never
|
|
160
|
+
disconnected (a socket the transport opens itself still is).
|
|
161
|
+
- **`sdk.launchpad.attach({ bundleId, method, uniqueKey, tokenName? })`** (and
|
|
162
|
+
`BundlerTransport.attach`): a `BundleSubmission` for a bundle sent before a reload, whose `wait()`
|
|
163
|
+
confirms exactly as the original's (socket event raced against the REST confirmation, which also
|
|
164
|
+
answers for a bundle that settled while nobody listened). Nothing is signed or sent until
|
|
165
|
+
`wait()`. Refused with `INVALID_BUNDLE_ATTACHMENT` (`details.field`): empty `bundleId` /
|
|
166
|
+
`uniqueKey`, a method the bundler does not accept, a `CreateSale` without `tokenName`. New exports:
|
|
167
|
+
`LAUNCHPAD_WRITE_METHODS` (`LaunchpadWriteMethod` is now derived from it), types
|
|
168
|
+
`BundleAttachment`, `BundleSocket`, `SocketFactory`. `LaunchpadWriteTransport` gains `attach`.
|
|
169
|
+
- The bundler (bundler-backend origin/main, `src/socket/socket.gateway.ts`) emits only
|
|
170
|
+
`{ status, data }` (lowercase; `PENDING` / `PROCESSED` / `FAILED`), never `{ Status, Data }`, so
|
|
171
|
+
`parseBundleResult` is unchanged.
|
|
172
|
+
|
|
173
|
+
### The backend's error answer on `NetworkError`
|
|
174
|
+
|
|
175
|
+
- **`serverMessage`, `serverError`, `details`, `body`** (type `NetworkErrorServerAnswer`) beside
|
|
176
|
+
`statusCode`. Before, an HTTP failure was `NetworkError("HTTP <M> <url>: Request failed with status
|
|
177
|
+
code N")` and the backend's body survived only on `originalError.launchpadError`. The launchpad
|
|
178
|
+
backend (origin/main `GlobalExceptionFilter`) answers `{ status, error: true, message }`
|
|
179
|
+
(HttpException: validation pipe, `sendResult`, guards) or `{ statusCode, message }` (CustomError,
|
|
180
|
+
413, unhandled 500): `serverMessage` is that `message` (an array is joined with `,`, as
|
|
181
|
+
`String(array)` shows it), `serverError` is a string `error` only (`true` is not one), `body` the
|
|
182
|
+
whole body (or the text of a non-JSON answer). Messages are unchanged.
|
|
183
|
+
- **A wrapped NetworkError keeps them**: `new NetworkError(msg, status?, originalError?, answer?)`
|
|
184
|
+
takes the answer of `originalError` (an axios error's response, or another NetworkError) when
|
|
185
|
+
not given, and `handleNetworkError` (the `error-wrapper` `withErrorHandling` wrap services put
|
|
186
|
+
around the client's error) also takes the wrapped NetworkError's status: it was `undefined`
|
|
187
|
+
there. (A bare `createNetworkError(msg, undefined, inner)` does not take the status: the
|
|
188
|
+
gateway-reads golden pins `statusCode` absent on the GalaChain balance read's wrapped 400.)
|
|
189
|
+
`createNetworkError` takes the answer as an optional fourth argument;
|
|
190
|
+
`response-handlers.assertResponseSuccess` puts a 2xx error envelope's body on it.
|
|
191
|
+
- Paths that turned the client's NetworkError into another error now keep it a NetworkError, same
|
|
192
|
+
message: `PoolService.fetchToken`'s 404 "Token not found: <name>" (was a plain `Error`),
|
|
193
|
+
`UserService.uploadProfileImage`'s "Profile image upload failed: ..." for an HTTP refusal (was a
|
|
194
|
+
`ValidationError` `UPLOAD_FAILED`; other failures still are), and `LaunchpadPriceService`'s spot
|
|
195
|
+
price failure now wraps the original.
|
|
196
|
+
- The response interceptor's `launchpadError.error` is set only when the body's `error` is a string
|
|
197
|
+
(it cast the backend's `true` to a string).
|
|
198
|
+
|
|
199
|
+
### Path parameters are spelled as the backend compares them
|
|
200
|
+
|
|
201
|
+
- `buildEndpoint` (every `StreamTokenServiceBase` service) lowercased EVERY path value. It now
|
|
202
|
+
follows `PATH_PARAM_CASING`, decided per parameter from launchpad-backend origin/main's routes:
|
|
203
|
+
`tokenName` (the pool lookup's `normalizeTokenName`), `address` and `userAddress`
|
|
204
|
+
(`normalizeAddress`) stay lowercased; `assetId` and `targetId` (Mux ids, mixed-case, compared
|
|
205
|
+
exactly), `id` (numeric invite ids) and `code` (invite codes, matched exactly) are sent as given.
|
|
206
|
+
A parameter the table does not name is refused. Changed on the wire: `getRecordingDownload` /
|
|
207
|
+
`deleteRecording` (`/v1/tokens/:tokenName/recordings/:assetId[/download]`) and
|
|
208
|
+
`removeSimulcastTarget` (`/v1/tokens/:tokenName/simulcasts/:targetId`): a mixed-case Mux id was
|
|
209
|
+
lowercased into one the backend does not have; the moderator and overseer `getInviteByCode`
|
|
210
|
+
(`/v1/{moderator,overseer}-invites/code/:code`) send the code as given (generated codes are
|
|
211
|
+
lowercase hex, so only a code typed in another case differs); the invite `:id` routes are
|
|
212
|
+
unchanged in effect (digits).
|
|
213
|
+
|
|
214
|
+
### `PageInfo` is the backend's `pageInfo`
|
|
215
|
+
|
|
216
|
+
- **`PageInfo` = `{ hasNextPage, endCursor?, hasPreviousPage?, startCursor?, totalCount? }`**, the
|
|
217
|
+
backend's own keys (launchpad-backend origin/main `buildPageInfo`: `endCursor` / `startCursor`
|
|
218
|
+
are `string | null`). It declared `nextCursor` / `prevCursor` / `hasPrevPage`, while every list
|
|
219
|
+
but two passed the backend's `pageInfo` through unchanged (so at run time it was `endCursor`), and
|
|
220
|
+
`PoolService` / `UserService` renamed the backend's keys to the declared ones. Now no list renames:
|
|
221
|
+
pools and user token lists return the backend's `pageInfo` (`pageInfoOf`, which drops a field of
|
|
222
|
+
the wrong type), and the cursor walkers (`autoPaginateWithCursor`, `fetchAllPools`, the vault
|
|
223
|
+
token-name lookup) follow `endCursor` (`nextCursorOf`): `autoPaginateWithCursor` stopped after
|
|
224
|
+
the first page of every passed-through list. They also stop when the backend repeats the cursor
|
|
225
|
+
just used (`fetchAllPools` went back to page 1 on a missing cursor).
|
|
226
|
+
- `totalCount` is set only on results the SDK assembles from every page (`fetchAll...`): the
|
|
227
|
+
backend sends no count. Removed with it: `UserTokenListResult.total` (always undefined), the
|
|
228
|
+
dead `InternalFetchUserBalancesResponse` type, and the top-level `count` / `total` / `nextCursor`
|
|
229
|
+
/ `prevCursor` / `hasNextPage` / `hasPrevPage` fields of the internal pool and user-list response
|
|
230
|
+
types (the backend answers `{ tokens | token | launches, pageInfo }`). Migration: read
|
|
231
|
+
`pageInfo.endCursor` (`?? undefined` for a `cursor` option) where you read `nextCursor`.
|
|
232
|
+
|
|
233
|
+
### `fetchPools` matches `GET /v1/tokens` (launchpad-backend origin/main `FetchPoolDto`)
|
|
234
|
+
|
|
235
|
+
- **`type` and `streamStatus` in the backend's spelling, any case.** `type` RECENT / POPULAR /
|
|
236
|
+
RECORDINGS / SECONDS_STREAMED (`POOL_FETCH_TYPES`; the backend compares case-sensitively, so it is
|
|
237
|
+
sent uppercase), `streamStatus` IDLE / ACTIVE / DISABLED (`POOL_STREAM_STATUSES`). The lookup
|
|
238
|
+
tables were lowercase-keyed and DROPPED the app's `'POPULAR'` / `'ACTIVE'` without a word; lowercase
|
|
239
|
+
still works. A value the backend does not accept is a `ValidationError`, never dropped.
|
|
240
|
+
- **`pageSize` 1 to 100** (`POOL_LIST_MAX_PAGE_SIZE`, the backend's `MediumListQueryDto` maximum):
|
|
241
|
+
it was silently clamped to 20. Larger, zero or fractional is a `ValidationError`. `fetchAllPools`
|
|
242
|
+
reads pages of 100 (was 20). Default still 10.
|
|
243
|
+
- `recentlyStreamed` (integer 1 to 720), `language` (ISO 639-1) and `search` (at most 255) are
|
|
244
|
+
checked before sending, as the backend would refuse them.
|
|
245
|
+
- **`hasUpcomingShows` / `hasRecordings`: only `true` is sent.** The backend's boolean conversion
|
|
246
|
+
(`@Type(() => Boolean)` with implicit conversion) reads `"false"` as true, so `false` used to turn
|
|
247
|
+
the filter ON; it now sends nothing (no filter).
|
|
248
|
+
- `isGraduated` / `creatorAddress` are NOT added: origin/main's `FetchPoolDto` has neither and the
|
|
249
|
+
pipe forbids unknown properties (a 400).
|
|
250
|
+
- One pool-list reader: the private `LaunchpadAPI.fetchPools` / `fetchPoolsFromAPI` copy (used to
|
|
251
|
+
find a vault's token name for a sell-fee quote) is removed; that lookup reads `PoolService`. The
|
|
252
|
+
copy read `hasNextPage` / `nextCursor` at the top of `data`, where the backend never puts them,
|
|
253
|
+
so it never got past page 1. `LaunchpadService` / `fetchAllPools` take `FetchPoolsOptions` (their
|
|
254
|
+
inline option types still named `offset` / `limit`). The zod `poolFetchTypeSchema` takes all four
|
|
255
|
+
types; the public `PoolFetchType` is the one type (`'RECENT' | 'POPULAR' | 'RECORDINGS' |
|
|
256
|
+
'SECONDS_STREAMED'`, it was the schema's two).
|
|
257
|
+
|
|
258
|
+
### Admin and overseer contracts (launchpad-backend origin/main)
|
|
259
|
+
|
|
260
|
+
- **`revokeOverseerInvite` and `revokeOverseer` sent no Authorization.** The auth headers were passed
|
|
261
|
+
in the query-params slot of `HttpClient.delete`: the request went out unauthenticated and the
|
|
262
|
+
header names became query parameters. They are headers now (`tests/g3-admin-wire.test.ts`).
|
|
263
|
+
- **Restricted token names carry the session bearer.** `getRestrictedNames` / `updateRestrictedNames`
|
|
264
|
+
(Overseer-only routes) sent no Authorization. An empty `content` is accepted (the backend takes any
|
|
265
|
+
string; it clears the list); `UpdateRestrictedNamesResponse` is `{ content }` (the backend echoes the
|
|
266
|
+
stored content), it was typed `{ success: true }`.
|
|
267
|
+
- **Invite codes are validated like the backend**: any non-empty string up to 64 characters
|
|
268
|
+
(`INVITE_CODE_CONSTRAINT.MAX_LENGTH`, was 100 for overseer codes and "64 hex" for moderator codes).
|
|
269
|
+
An unknown code is the backend's 404, which a claim page relies on to fall through from the overseer
|
|
270
|
+
lookup to the moderator one; it used to be a client-side `ValidationError`.
|
|
271
|
+
- **`sdk.getHealth()`** (`GET /health`, public): `HealthCheckResponse` (`status` plus database, redis,
|
|
272
|
+
mux, rabbitmq, galachain_query, galachain_launchpad). A critical dependency down is HTTP 503 and
|
|
273
|
+
throws a `NetworkError`.
|
|
274
|
+
- **`sdk.getEventStats()`, `sdk.queryEvents(options)`** (`GET /v1/events/stats`, `GET /v1/events`,
|
|
275
|
+
Admin API key or Overseer): the routes answer BARE bodies, which the service reads and shape-checks.
|
|
276
|
+
`queryEvents` pages by `page` / `limit` (1..100). `eventTypes` / `categories` are sent as a
|
|
277
|
+
REPEATED key (one value is sent twice): the backend validates them with `@IsArray` behind Express 5's
|
|
278
|
+
simple query parser, where a comma list or `key[]=` is refused.
|
|
279
|
+
- **`getMessageStats` reads the bare object** `GET /v1/messages/stats` answers (it required a
|
|
280
|
+
`{ data }` envelope and could never succeed against the backend).
|
|
281
|
+
|
|
282
|
+
- **The flag filters validate what `GetFlagsQueryDto` takes**: the token filter is 1-50 letters and digits
|
|
283
|
+
(a 1-character filter threw; see the token name rules below) and the addresses
|
|
284
|
+
(`reporterAddress`, `reportedUserAddress`, also `createFlag`'s) are the forms `@GalaChainAddressField()`
|
|
285
|
+
takes after its normalisation: `eth|<40 hex>`, `0x<40 hex>`, 40 hex, `client|<24 hex>` or 24 hex
|
|
286
|
+
(`isBackendUserAddress` / `assertBackendUserAddress`). A bare client id is accepted now; a partial
|
|
287
|
+
address and `client|<name>` stay refused, because the backend answers 400 for them.
|
|
288
|
+
- **`updateModeratorInvite({ inviteId, description: '' })` is a valid update**: the backend's `UpdateModeratorInviteDto.description` has no minimum and its service updates on
|
|
289
|
+
`description !== undefined`, so `''` alone clears the label (the SDK refused it with "At least one of
|
|
290
|
+
role or description"). An update with neither field is still refused.
|
|
291
|
+
|
|
292
|
+
### Trades, tokens, users and streaming contracts (launchpad-backend origin/main)
|
|
293
|
+
|
|
294
|
+
- **`fetchTrades` reaches its route.** It sent the literal `/v1/tokens/:tokenName/trades` with the
|
|
295
|
+
name in a query parameter; the token is now in the path (lowercased, as the backend compares it).
|
|
296
|
+
`TradeQueryItem` types `tokenSymbol`, which `GET /v1/trades` returns. `getTrades` already unwrapped
|
|
297
|
+
the `{ data }` body (verified on the wire, `tests/g3-trades-wire.test.ts`).
|
|
298
|
+
- **`fetchTokenStats` returns what the backend sends**: `{ holdersCount, chatMessageCount, commentCount,
|
|
299
|
+
roomCount }` (`TokenStatsData`, was `EngagementStatsData`). It required a `stats` wrapper the backend
|
|
300
|
+
has none of, and so could never succeed; `totalEngagement`, participants and activity fields were
|
|
301
|
+
never sent.
|
|
302
|
+
- **`updateTokenConfig` takes `chatEnabled`** (was `messagesEnabled`, which the backend refuses with 400:
|
|
303
|
+
its `UpdateTokenConfigDto` has `chatEnabled`).
|
|
304
|
+
- **`fetchLaunchpadFee()` returns the number** (`data.feeAmount`); it returned the `{ feeAmount }` object
|
|
305
|
+
typed as a number.
|
|
306
|
+
- **Images reach their routes.** `uploadImageByTokenName` posts to `/v1/tokens/:tokenName/image` (the
|
|
307
|
+
placeholder went out literally, the name in a query parameter) and `uploadProfileImage` to
|
|
308
|
+
`POST /v1/users/me/image` (it used the token route); the profile upload needs no address or wallet.
|
|
309
|
+
- **`updateProfile` sends what is given.** `address` is optional (validated, not sent), `fullName` and
|
|
310
|
+
`profileImage` are optional (at least one), `profileImage: ''` clears the image, and the profile is no
|
|
311
|
+
longer fetched first to send the old image back. `fullName` is validated like the backend's
|
|
312
|
+
`UpdateProfileDto` (a trimmed 2..20 letters, one optional second word) and no wallet is required, only
|
|
313
|
+
a JWT.
|
|
314
|
+
- **User token lists take pages up to 100** (`USER_PAGINATION_CONSTRAINT.MAX_LIMIT` 20 -> 100; the
|
|
315
|
+
backend takes up to 1000, default 50); `getManagedTokens` sends no `pageSize` unless asked, so the
|
|
316
|
+
backend's 50 applies (the SDK's default 10 cut a longer list off).
|
|
317
|
+
- **`fetchVolumeData` returns the chart**: `{ dataPoints, isPreviousData, nextTime? }` with the candles
|
|
318
|
+
typed as the backend sends them (`startTime`, `openPrice`, `highPrice`, `lowPrice`, `closePrice`,
|
|
319
|
+
`volume`); `dataPoints` held the whole `{ data, isPreviousData }` object typed as `{ timestamp, value }[]`.
|
|
320
|
+
- **`getTradeQuote` takes an `AbortSignal`** (`TradeQuoteParams.signal`); `PlatformStatsData` volumes are
|
|
321
|
+
numbers (typed as strings); `listOverseerUsers` accepts `ASC` / `DESC` (the backend reads the order
|
|
322
|
+
case-insensitively).
|
|
323
|
+
- **Recordings match `GET /v1/tokens/:tokenName/recordings`**: a bare array with no paging.
|
|
324
|
+
`getStreamRecordings({ tokenName })` returns `{ recordings, tokenName }` (the fabricated `page`, `limit`,
|
|
325
|
+
`total`, `hasNext` are gone; the old code read `data.recordings` and could never succeed). A recording is
|
|
326
|
+
`{ id, playbackId, status, duration, createdAt, masterAccess, aspectRatio?, assets? }` (was `assetId`,
|
|
327
|
+
`tokenName`, `downloadable`, `fileSize`, `thumbnailUrl`); `RECORDING_STATUS` is Mux's `preparing` /
|
|
328
|
+
`ready` / `errored`. An unknown token is a `NetworkError` with `statusCode` 404.
|
|
329
|
+
- **Simulcasts match `/v1/tokens/:tokenName/simulcasts`**: the list is a bare array behind the owner's
|
|
330
|
+
session bearer (it was sent without Authorization); a target is `{ id, url, status, passthrough? }`;
|
|
331
|
+
`addSimulcastTarget` sends `{ platform, url, streamKey, passthrough? }` with the platform in lowercase
|
|
332
|
+
(`'youtube' | 'twitch' | 'facebook' | 'custom'`, `SIMULCAST_PLATFORM`), as the backend's
|
|
333
|
+
`CreateSimulcastTargetDto` requires (it refused `rtmpUrl`, `name` and uppercase platforms), and the
|
|
334
|
+
URL is RTMP or RTMPS only (the SDK allowed SRT, which the backend refuses).
|
|
335
|
+
- **`sdk.getChatStatus(tokenName)`** (`GET /live/:tokenName/chat`, public): `{ enabled, tokenName,
|
|
336
|
+
reason?, slowModeSeconds?, status? }`. `ChatStatusResponse` gains `tokenName` and `slowModeSeconds`.
|
|
337
|
+
- **The pinned message is the backend's `PinnedMessageData`** (`messageId`, `content`, `userAddress`,
|
|
338
|
+
`pinnedBy`, `pinnedAt`, `user?`, `holderTier?`): `getPinnedMessage` returned a type with `id` and
|
|
339
|
+
`createdAt` the backend never sends, and threw when nothing was pinned (`{ data: null }`); it returns
|
|
340
|
+
`{ message: null }` now. `getPinnedChatMessage` / `pinChatMessage` return that message as it is, and
|
|
341
|
+
`unpinChatMessage` takes its `messageId`.
|
|
342
|
+
- **`UpdateMessageOptions.type`**: a chat edit is capped at 500 characters before it is sent when the
|
|
343
|
+
type is known (`updateChatMessage` passes it).
|
|
344
|
+
- **`PoolData` names the streaming fields the backend sends**: `muxPlaybackId`, `muxStreamAspectRatio`,
|
|
345
|
+
`streamLanguage` (typed `playbackId`, `aspectRatio`, `language`, which the backend does not send, so
|
|
346
|
+
they were always undefined); `streamEnabled`, `messagesEnabled`, `commentsEnabled` are not on a pool.
|
|
347
|
+
- **New `sdk.getUserGalaBalance(address)`** (`GET /v1/users/:address/gala-balance`, public: the GALA
|
|
348
|
+
balance and USD value the backend reads) and **`sdk.fetchReferralCode()`**
|
|
349
|
+
(`GET /v1/connect/users/referrals/code`, JWT: `{ code, username? }`).
|
|
350
|
+
- **`TradesQueryResult` is the one trades result type, and it is not deprecated.** `getTrades`,
|
|
351
|
+
`getRecentTrades` and `getHolderHistory` return the backend's `{ trades, pageInfo }`; the
|
|
352
|
+
`@deprecated` marker (it pointed at `TradesListResult`, an `items`/`meta` type no method returns) and
|
|
353
|
+
`TradesListResult` are gone, and the `getTrades` examples read `pageInfo` (they destructured a `meta`
|
|
354
|
+
the result never had).
|
|
355
|
+
- **`UpdateTokenConfigOptions` is one type.** The package root exported a `token-config.dto` interface
|
|
356
|
+
(social links `string` only) while `updateTokenConfig` took a second one declared beside the message
|
|
357
|
+
types (`string | null`). The backend's `UpdateTokenConfigDto` takes `null` to clear a social link, so
|
|
358
|
+
the one type is the `string | null` form, declared in `token-config.dto` with
|
|
359
|
+
`validateUpdateTokenConfigOptions` (moved with it), and the dead duplicate
|
|
360
|
+
`InternalUpdateTokenConfigResponse` of `messages.dto` is gone.
|
|
361
|
+
- **The pin and invite result types are exported, one meaning per name.** `pinMessage` / `unpinMessage`
|
|
362
|
+
return `PinMessageResult` (`{ success }`) and `getPinnedMessage` returns `PinnedMessageResult`
|
|
363
|
+
(`{ message }`), both now exported from the root; the root's old `PinMessageResult`
|
|
364
|
+
(`{ tokenName, pinnedMessage }`, the shape of a removed REST route) is gone and
|
|
365
|
+
`getPinnedChatMessage` / `pinChatMessage` return `GetPinnedMessageResult`
|
|
366
|
+
(`pinnedMessage` is `null` when another moderator unpinned between the pin and the read back; it was
|
|
367
|
+
cast to non-null). `InviteScope`, `UpdateInviteRoleOptions` and `UpdateInviteRoleResult` are exported;
|
|
368
|
+
the identical `InviteScope` of `streaming.dto` and `ModeratedTokenInviteScope` are the one
|
|
369
|
+
`InviteScope`.
|
|
370
|
+
- **`fetchProfile` is typed and unwrapped.** `fetchProfile(address)` returned the whole
|
|
371
|
+
`{ status, error, message, data }` envelope typed `unknown`; it returns `PublicUserProfile` (the
|
|
372
|
+
backend's `ProfileResponse`: `address`, nullable `fullName` / `profileImage` / `bio`, `stats`
|
|
373
|
+
(`ProfileStats`), the four `BadgeStatus[]` ladders, `isLiveStreaming`, `liveStreamToken`) and a body
|
|
374
|
+
that is not that shape is a `NetworkError` (`Malformed profile response`). The service's no-address
|
|
375
|
+
form (`GET /v1/users/me/profile`, JWT) returns `CurrentUserProfile`. `BadgeStatus`, `ProfileStats`,
|
|
376
|
+
`PublicUserProfile` and `CurrentUserProfile` are exported.
|
|
377
|
+
- **`PoolData` types `badges`** (`PoolBadges`: the `volume` and `engagement` `BadgeStatus[]` ladders the
|
|
378
|
+
backend builds for `GET /v1/tokens` and `/:tokenName`) and the stored `volumeBadge` / `engagementBadge`
|
|
379
|
+
columns. The pool route sends no `chatEnabled`, `streamingEnabled` or `tokenSymbol` (they are not in
|
|
380
|
+
its attribute list), so `PoolData` does not declare them.
|
|
381
|
+
- **Message, comment and chat types are the backend's five shapes** (`GET /v1/messages` and the four write
|
|
382
|
+
answers differ; the SDK typed one invented `BaseContentItem` for all of them). `Message` is the list
|
|
383
|
+
item of `fetchMessages`, `getComments` and `getChatMessages`: `id` and `messageId` (the same string),
|
|
384
|
+
`user` / `userProfile` (the same non-null `{ fullName, profileImage }`), `updatedAt` `string | null`,
|
|
385
|
+
`reactions: { counts, userReacted? }` (was a plain `Record<string, number>`), and no `type`,
|
|
386
|
+
`holderTier` or `isPinned` (the backend sends none of them on a list item). Writes: `sendChatMessage`
|
|
387
|
+
returns `SentChatMessage` (no database id, `holderTier?`), `createComment` / `updateComment` return
|
|
388
|
+
`WrittenComment` (numeric `id` AND `messageId`, no `tokenName`), `updateChatMessage` returns
|
|
389
|
+
`EditedChatMessage` (numeric `id`, `reactions?` as `ReactionData`); `createMessage` / `updateMessage`
|
|
390
|
+
infer the result from `options.type`. `BaseContentItem`, `CommentItem`, `ChatMessageItem`,
|
|
391
|
+
`isChatMessageItem` and the dead `/v1/comments` result types (`CommentData`, `FetchCommentsResult`,
|
|
392
|
+
`PostCommentResult`, `FetchCommentsOptions`, `PostCommentOptions`, `DeleteCommentOptions`, file
|
|
393
|
+
`comment.dto.ts`) are gone; `isMessage` checks the real shape (it needed a `type` no item has, so it
|
|
394
|
+
was false for every item). Each id the examples took from a write (`result.comment.id`, a number, or
|
|
395
|
+
`result.message.id`, undefined) is the `messageId`.
|
|
396
|
+
- **Each method validates the token name of ITS backend route** (launchpad-backend origin/main). The SDK
|
|
397
|
+
refused names the routes take (3-20 everywhere, 2-20 on messages, 50 on trades) and sent names a route
|
|
398
|
+
refuses. Five rules, exported as `TOKEN_NAME_*_CONSTRAINT` (and `TokenNameConstraint`), replace
|
|
399
|
+
`LAUNCHPAD_TOKEN_NAME_CONSTRAINT`, `RELAXED_TOKEN_NAME_CONSTRAINT`, `EXTENDED_TOKEN_NAME_CONSTRAINT`
|
|
400
|
+
and the helpers' silent 3-20 default (`validateTokenName(name, field, constraint)`,
|
|
401
|
+
`isValidTokenName(name, constraint)`, `validateOptionalTokenName(...)` now take the route's rule):
|
|
402
|
+
`DTO` 1-25 letters and digits (1-20 until rc.2; `@TokenNameField()` request fields: `fetchPools` filter, `fetchVolumeData`,
|
|
403
|
+
`fetchTokenDistribution`, `fetchUserHolderContext`, `fetchMessages`, `createMessage`, `getTrades`,
|
|
404
|
+
flag create, moderator invites, `banToken`), `FILTER` 1-50 (the flag lists), `PATH` letters and digits
|
|
405
|
+
1-255 (a raw `:tokenName` the pool lookup resolves, no format check: `fetchToken`, `fetchTokenStats`,
|
|
406
|
+
`updateTokenConfig`, `getTokenConfig`, image upload, every stream, recording, simulcast, ban and chat
|
|
407
|
+
method, `getPinnedMessage`, the user token list filter), `AVAILABILITY` 1-50 characters, any
|
|
408
|
+
(`validateToken`, `checkPool`: `ValidateTokenDto`), `EVENT` 1-100 characters without HTML (the
|
|
409
|
+
`WebSocketAdminService` emits). The root helpers `validateTokenName(name, field, constraint)`,
|
|
410
|
+
`isValidTokenName(name, constraint)` and `validateOptionalTokenName(name, field, constraint)` take the
|
|
411
|
+
rule of the route you name (they had a silent 3-20 default). The chain's launch rule (letters and
|
|
412
|
+
digits, 1+, no cap; what the launch form and `createSale` take) is `tokenNameSchema`,
|
|
413
|
+
`launchTokenDataSchema` and `imageUploadOptionsSchema`; `graphDataOptionsSchema` and
|
|
414
|
+
`graduateTokenOptionsSchema` use the DTO rule (`dtoTokenNameSchema`) and `checkPoolOptionsSchema` the
|
|
415
|
+
availability rule (`availabilityTokenNameSchema`). `TRADES_QUERY_CONSTRAINTS` (a deprecated
|
|
416
|
+
composite) is gone.
|
|
417
|
+
- **The reaction routes take no token name.** `AddReactionDto` is `{ messageId, reactionType }` and the
|
|
418
|
+
backend refuses any other property (whitelist + forbidNonWhitelisted), so `addContentReaction` with a
|
|
419
|
+
`tokenName` answered 400; `AddContentReactionOptions` / `RemoveContentReactionOptions` lose
|
|
420
|
+
`tokenName` and nothing is validated or sent for it.
|
|
421
|
+
- **`uploadTokenImage` and `uploadProfileImage` work on a signer-plus-address SDK.** The facade threw
|
|
422
|
+
"Wallet is required" (and `uploadProfileImage` defaulted the address from the wallet) though
|
|
423
|
+
`POST /v1/tokens/:tokenName/image` and `POST /v1/users/me/image` read the user from the session JWT and
|
|
424
|
+
need nothing else; an app configured with `{ signer, walletAddress, session }` and no `wallet` uploads
|
|
425
|
+
with the session bearer (`tests/g7-uploads-wire.test.ts`). `uploadProfileImage` no longer invents an
|
|
426
|
+
address: one that is given is validated and left out of the request. Their JSDoc described
|
|
427
|
+
`imagePath` options and a Node-only API that do not exist (the options are `{ file }`).
|
|
428
|
+
- **Image uploads send multipart, not JSON.** Both uploads put the `FormData` through an axios instance
|
|
429
|
+
that carries `Content-Type: application/json` as an instance default; the request-level header was only
|
|
430
|
+
left out, so the default survived the merge and axios serialised the body as JSON
|
|
431
|
+
(`{"image":{}}`, the file lost; the 400 the backend answers was the only sign). `HttpClient` now
|
|
432
|
+
clears the Content-Type for a `FormData` body per request (an explicit `undefined`), so the transport
|
|
433
|
+
sets `multipart/form-data` with its boundary (axios' Node adapter, the browser's XHR). The existing
|
|
434
|
+
wire tests swapped the whole instance for one without the default and could not see it;
|
|
435
|
+
`tests/image-upload-multipart-wire.test.ts` (Node, real server) and
|
|
436
|
+
`tests/image-upload-multipart-browser-wire.test.ts` (jsdom XHR, real server, plus the FormData as it
|
|
437
|
+
leaves axios) run the real `createHttpExecutor` instance and read the bytes sent. These are the only
|
|
438
|
+
two multipart paths in the SDK.
|
|
439
|
+
- **One name for an image upload.** `UploadImageByTokenNameOptions.options.tokenName` (a second name that
|
|
440
|
+
went into the URL without being validated) is gone; the path takes the validated `tokenName`. The dead
|
|
441
|
+
`LaunchpadAPI.uploadImageByTokenName` (`POST /launchpad/upload-image`) and
|
|
442
|
+
`LaunchpadAPI.fetchTokenDistribution` (`GET /holders/:tokenName`), routes the backend does not have, are
|
|
443
|
+
removed with their tests (`ImageService` and `PoolService` are the working ones).
|
|
444
|
+
|
|
445
|
+
### The stream socket: one registry of subscribers (launchpad-backend origin/main)
|
|
446
|
+
|
|
447
|
+
`StreamWebSocketService` keeps ONE registry. `sdk.realtime` is the service of an SDK (built with
|
|
448
|
+
`streamWebSocketUrl`, the reconnect settings and the session's token). Every event the backend emits has a
|
|
449
|
+
typed entry on it (`RealtimeEventPayloads`, read from the emit sites, not from the backend's docs).
|
|
450
|
+
|
|
451
|
+
- **Any number of subscribers per event, per token, per room.** Every `subscribe*` method is synchronous,
|
|
452
|
+
returns that subscriber's OWN unsubscribe (a second call is harmless) and replaces nobody. The old
|
|
453
|
+
single callback slots (`globalCallbacks`, `roomCallbacks`, `globalFeedCallbacks`) are gone: a second
|
|
454
|
+
subscriber used to replace the first and the first's cleanup deleted the second's callback, and
|
|
455
|
+
`subscribeToStream` on a joined token wiped every other subscriber's callbacks. A subscriber that
|
|
456
|
+
throws or rejects is logged and skipped; the others are served. `LaunchpadSDK.watch*` are plain
|
|
457
|
+
subscribers (the fan-out, refcount and room bookkeeping `watchTokenRoomEvent` kept is deleted).
|
|
458
|
+
- **Rooms are refcounted.** A token room, a comment room, the global feed and the events firehose are
|
|
459
|
+
joined on the FIRST subscriber and left on the LAST; never two server subscriptions for one room. A
|
|
460
|
+
leave waits for the join's acknowledgement (a subscribe followed at once by an unsubscribe cannot strand
|
|
461
|
+
the room on the server). A subscribe before the socket is connected registers and joins on connect; with
|
|
462
|
+
`autoConnect` (default) it also opens the socket. It never throws `NOT_CONNECTED`.
|
|
463
|
+
- **Listeners are registered once per socket**, when it is created (they used to be registered again on
|
|
464
|
+
every `connect`, so every reconnect doubled every callback), and removed when it is closed.
|
|
465
|
+
- **Reconnect.** The service acts on the Socket's `connect` (the Manager's `reconnect` fires before the
|
|
466
|
+
socket is connected and used to run `authenticate()` too early). On every connection after the first:
|
|
467
|
+
re-authenticate FIRST with the token current at that moment (`tokenProvider`, never a remembered
|
|
468
|
+
one) if the socket was authenticated, THEN re-join every room that still has a subscriber, THEN call the
|
|
469
|
+
new `onReconnect(listener)` listeners (the app's version never fired, `reconnect` is a Manager event).
|
|
470
|
+
`onConnectionStateChange(listener)` is new. A server-side `io server disconnect` reconnects (socket.io
|
|
471
|
+
does not), and so do `connect()` and a new subscribe after socket.io ran out of reconnection attempts
|
|
472
|
+
(`socket.active` stays true then). `LaunchpadSDKConfig.streamReconnectAttempts` / `streamReconnectDelay` /
|
|
473
|
+
`streamReconnectDelayMax` reach socket.io (a browser app wants `Infinity`, 1000, 10000); defaults stay
|
|
474
|
+
5 / 2000.
|
|
475
|
+
- **`authenticate(token?)` returns `Promise<boolean>`** (it returned `void` and threw `NOT_CONNECTED`
|
|
476
|
+
when not connected): `true` on `authenticated`; `false` on `exception`, `stream_chat_auth_error`, the
|
|
477
|
+
10 s timeout, a dropped connection, no token, or a newer `authenticate` / `deauthenticate`. The same
|
|
478
|
+
token on the same connection sends nothing; a different token replaces the pending one and is sent after
|
|
479
|
+
the server answered the one in flight. `LaunchpadSDK.authenticateStreamWebSocket(token?)` takes the
|
|
480
|
+
app's token (or the session's) and returns the boolean. The handshake carries no token any more
|
|
481
|
+
(`StreamWebSocketConfig.authToken` is gone; a static token was presented, expired, on every reconnect).
|
|
482
|
+
- **`deauthenticate()`** (`sdk.deauthenticateStreamWebSocket()`): the server has no message that drops a
|
|
483
|
+
socket's identity, so an authenticated socket is closed and opened again (subscriptions stay registered;
|
|
484
|
+
`onReconnect` fires). `setWallet` / `setWalletProvider` call it.
|
|
485
|
+
- **`joinTokenRoom(token)`** holds a token's room with no listener (the room stays joined while its
|
|
486
|
+
listeners come and go); it returns its own release.
|
|
487
|
+
- **The overseers' firehose** (`subscribeEventsFirehose(listener, filters?)`): one server subscription
|
|
488
|
+
for all subscribers, joined once the socket is authenticated (it is overseer-only) and again after every
|
|
489
|
+
re-authentication. The backend's firehose takes no filters, so each subscriber's
|
|
490
|
+
`{ eventTypes, tokenName, userAddress }` filters are applied client-side.
|
|
491
|
+
- **The comment room.** `COMMENT_*` events are sent to the comment room only; `subscribeCommentCreated`
|
|
492
|
+
(and the new `Updated` / `Deleted`) now emit `subscribe_comment` (it never did, so it received nothing).
|
|
493
|
+
- **User-room and overseer events are delivered to global subscribers** (`subscribeGlobalModeratorAdded`
|
|
494
|
+
/ `Removed` / `RoleUpdated` / `InviteCreated`, `subscribeGlobalTokenBanned` / `Unbanned`,
|
|
495
|
+
`subscribeContentFlagged`, `subscribeFlagResolved`, `subscribeAIModerationComplete` / `Failed`,
|
|
496
|
+
`subscribeUserBanned` / `Unbanned`, `subscribeBanEnforcement`, `subscribeTradeUpdateNotify`,
|
|
497
|
+
`subscribeBalanceUpdated(token)`, ...), not only to a token room's subscribers.
|
|
498
|
+
- **Feed subscribers are served whether or not the token room is joined.** The backend sends a client in
|
|
499
|
+
both rooms one copy of an event; the service delivers it to the token subscribers and to the feed
|
|
500
|
+
subscribers (it used to deliver it to the token room only, never to the feed).
|
|
501
|
+
- **New events with typed payloads**: `STREAM_CHAT_UPDATED`, `STREAM_CHAT_DELETED`, `SLOW_MODE_CHANGED`,
|
|
502
|
+
`RECORDING_STATUS`, `RECORDINGS_COUNT_UPDATED`, `SIMULCAST_STATUS`, `DOWNLOAD_READY`,
|
|
503
|
+
`STREAM_CONTROL_STATUS_CHANGED`, `CONTENT_REACTION_ADDED` / `REMOVED`, `COMMENT_UPDATED` / `DELETED`,
|
|
504
|
+
`CONTENT_FLAGGED`, `FLAG_RESOLVED`, `AI_MODERATION_COMPLETE` / `FAILED`, `POOL_CREATED`,
|
|
505
|
+
`TRADE_UPDATE_NOTIFY`, `AI_MODERATION_GLOBAL_STATUS`, `EVENT` (firehose), `exception`.
|
|
506
|
+
`STREAM_COUNTDOWN_UPDATED` / `STREAM_LANGUAGE_UPDATED` are fed from `TOKEN_CONFIG_UPDATED` (the backend no
|
|
507
|
+
longer emits them separately). `getSiteConfigState()` returns the latest site-wide switches.
|
|
508
|
+
`subscribeFeedTokenEvents(cb)` hands every feed token event over named (`{ event, data }`).
|
|
509
|
+
- **A second copy of one occurrence is dropped** for the events the backend sends through two rooms a
|
|
510
|
+
client can be in (`CONTENT_FLAGGED`, `FLAG_RESOLVED`, `AI_MODERATION_*`, `POOL_CREATED`, `OVERSEER_*`):
|
|
511
|
+
within 5 seconds, by id.
|
|
512
|
+
- **A payload with no usable `tokenName` no longer throws inside the socket listener** (`data.tokenName.trim()`,
|
|
513
|
+
`data.content.slice`); it reaches the subscribers of the event for every token and none of a token.
|
|
514
|
+
- **The 30-second replay buffer is removed.** It replayed stale events (trade deltas, candles) to a
|
|
515
|
+
late subscriber as if they were new, and started a timer per event per token; registration now precedes
|
|
516
|
+
the join, so there is no join race left for it to cover.
|
|
517
|
+
- **Replaced APIs (9.0.0 is unpublished: no shims).** `setGlobalCallbacks` -> `addCallbacks(callbacks)`
|
|
518
|
+
(adds, returns the remover); `setGlobalFeedCallbacks`, `unsubscribeFromGlobalFeed` and the Promise form
|
|
519
|
+
of `subscribeToGlobalFeed` -> `subscribeToGlobalFeed(callbacks)` returns an unsubscribe;
|
|
520
|
+
`connectStreamWebSocket(callbacks)` adds subscribers; `subscribeToStream(token, callbacks)` adds, and
|
|
521
|
+
`unsubscribeFromStream(token)` removes only what `subscribeToStream` added. `subscribeEngagementStatsUpdated`
|
|
522
|
+
takes a token. `StreamingEventService` and its `streaming-events` types are deleted: the
|
|
523
|
+
`LaunchpadSDK.on*` methods they backed never fired (nothing fed them); they are now subscribers of the same
|
|
524
|
+
registry (`onStreamChatMessage`, `onViewerCountChanged`, `onUserBanned`, ...: every occurrence the socket
|
|
525
|
+
receives, for any token; join rooms with the `subscribe*` methods or `subscribeToStream`).
|
|
526
|
+
`disconnect()` releases every subscriber, listener and timer and rejects what is pending; the service can be
|
|
527
|
+
used again; `dispose()` (called by `sdk.cleanup()`) is final.
|
|
528
|
+
- **Payload types follow the backend**: `UserBannedEvent` / `UserUnbannedEvent` / `BanEnforcementEvent`
|
|
529
|
+
(`isGlobal`, `isPermanent`, optional `reason` / `expiresAt` / `userAddress`; `tokenName` is `null` for a
|
|
530
|
+
global ban; the type guards accept the banned user's own copy, which has no address),
|
|
531
|
+
`TradeExecutedEvent` (+ `symbol`, `icon`), `BalanceUpdatedEvent` (the backend sends no balances),
|
|
532
|
+
`StreamGlobalStatusEvent` / `StreamChatGlobalStatusEvent` (`updatedAt` optional: `{ enabled }`), and the
|
|
533
|
+
global-feed token types are the token-room types (the feed copies named other fields). `COMMENT_DELETED`
|
|
534
|
+
carries `commentId`, not `messageId`.
|
|
535
|
+
|
|
71
536
|
### Fixes
|
|
72
537
|
|
|
538
|
+
- `createSale`'s pre-flight refused launches the launch form and the deployed bundler accept (a
|
|
539
|
+
scheme-less link such as `t.me/x`, a lower-case symbol, a name of 2 or 21-25 characters, a
|
|
540
|
+
pre-buy of `5.`). It now refuses only what the bundler (fee window, bad words) or the chain
|
|
541
|
+
(`CreateTokenSaleDTO`, `TokenClass`) refuses: a name of letters and digits (any length), a symbol
|
|
542
|
+
of letters and digits in any case (the chain upper-cases it; the wire carries it as typed), no
|
|
543
|
+
link rules, a non-empty description (at most 1000) and image (at most 500), an integer
|
|
544
|
+
`saleStartTime`, and a plain-decimal pre-buy and curve portions, which are SIGNED in their
|
|
545
|
+
canonical spelling (`5.` -> `5`, `1.50` -> `1.5`: the chain keeps any other spelling a string
|
|
546
|
+
and refuses it). Bad words are still refused by the bundler after signing (it fetches the list).
|
|
547
|
+
`LaunchpadTradeInput`, `LaunchpadExtraFees` and `CreateSaleInput` are exported by name
|
|
548
|
+
(`@since 9.0.0`), and `LaunchpadTradeInput.extraFees` documents what is sent on a buy.
|
|
549
|
+
- `BundleSubmission.wait()` takes `{ signal?: AbortSignal }` beside `timeoutMs` / `pollIntervalMs`
|
|
550
|
+
(`@since 9.0.0`). An aborted wait rejects with `LaunchpadSDKError` `BUNDLE_WAIT_ABORTED`
|
|
551
|
+
(`details.method`, `details.bundleId`; an already aborted signal rejects before a socket is opened
|
|
552
|
+
or a read sent) and stops what it started: the in-flight read (the requestor gets the wait's
|
|
553
|
+
`AbortSignal` as `init.signal`), the pause between reads, the deadline timer, its socket listener
|
|
554
|
+
(an own socket is disconnected, a borrowed `bundleSocketFactory` socket never). Aborting stops
|
|
555
|
+
watching only; `attach` resumes the bundle. A wait that ends for any reason now stops its reads too
|
|
556
|
+
(they used to run on until the deadline, up to 10 minutes). Also: one failed confirmation read (an
|
|
557
|
+
HTTP error, a network error, an unreadable body) no longer rejects the wait with
|
|
558
|
+
`BUNDLE_REQUEST_FAILED` while the socket can still deliver: reads are repeated every
|
|
559
|
+
`pollIntervalMs` until the deadline, and only a verdict (PROCESSED / FAILED), the deadline or an
|
|
560
|
+
abort ends the wait. At the deadline after failing reads the error is `BUNDLE_CONFIRMATION_TIMEOUT`
|
|
561
|
+
with the last failed read in `details.lastReadFailure` (`{ message, status? }`); the deadline
|
|
562
|
+
also holds for a read that never answers.
|
|
73
563
|
- `graduateToken` bought `calculateBuyAmountForGraduation`'s `amount` (the GALA cost of the
|
|
74
564
|
remaining tokens) as the exact-buy token quantity. It buys `remainingTokens`, with the GALA cost
|
|
75
565
|
as `expectedAmount` (the slippage cap).
|
package/EXAMPLES.md
CHANGED
|
@@ -53,7 +53,7 @@ Q: What operation am I performing?
|
|
|
53
53
|
|
|
54
54
|
| Format | Pattern | Example | Used For |
|
|
55
55
|
|--------|---------|---------|----------|
|
|
56
|
-
| **tokenName** |
|
|
56
|
+
| **tokenName** | Letters and digits, 1-25 chars | `'anime'` | Bonding curve ops, price |
|
|
57
57
|
| **token id / class key** | `collection\|category\|type\|additionalKey` | `'GALA\|Unit\|none\|none'` | Transfers, balances, locks, standard tokens |
|
|
58
58
|
|
|
59
59
|
### Common Mistakes Table
|