@trinaryex/sdk 1.0.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/DESIGN.md +577 -0
- package/README.md +239 -0
- package/dist/ReadOnlyClient.d.ts +26 -0
- package/dist/TriexClient.d.ts +81 -0
- package/dist/book.d.ts +27 -0
- package/dist/config.d.ts +6 -0
- package/dist/errors.d.ts +29 -0
- package/dist/execute.d.ts +12 -0
- package/dist/funding.d.ts +15 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +1608 -0
- package/dist/money.d.ts +18 -0
- package/dist/onchain.d.ts +45 -0
- package/dist/paging.d.ts +8 -0
- package/dist/queries.d.ts +26 -0
- package/dist/schemas.d.ts +1146 -0
- package/dist/transactions.d.ts +76 -0
- package/dist/types.d.ts +172 -0
- package/dist/wait.d.ts +5 -0
- package/package.json +108 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
# `@trinaryex/sdk` — Trinary Exchange Trading SDK
|
|
2
|
+
|
|
3
|
+
**Status:** COMPLETE (2026-08-22) — all phases plus a full live-testnet trading lifecycle:
|
|
4
|
+
real deposits/withdrawals, resting bids picked up by the indexer, modify + cancel + refund
|
|
5
|
+
recovery, a REAL market-buy fill (14,999 CRED + exactly 2% fee), taker trade visible in
|
|
6
|
+
`/trades`, item held in the BM, settled claims, and a BM-funded limit sell — 12/12
|
|
7
|
+
lifecycle + 11/11 integration + 91 unit tests. Schemas verified against the published
|
|
8
|
+
swagger. Remaining: npm publish (CI release workflow ready; needs NPM_TOKEN + push).
|
|
9
|
+
**Package:** `@trinaryex/sdk` (repo: `sdk/`) — an umbrella SDK; the trading surface is the
|
|
10
|
+
first module, with room to grow into a higher-level, full-featured client.
|
|
11
|
+
**Audience:** players and bots trading on Trinary Exchange via an API key.
|
|
12
|
+
**Target (MVP):** **testnet only**, the **`stillness`** tenant; **only the most-recent (v1)
|
|
13
|
+
`triexbook` contracts** are supported. Item↔currency (CRED) markets via `multicoin_pool`
|
|
14
|
+
("pools"); coin-pools are out of scope for now.
|
|
15
|
+
**Models after:** [`@trinaryex/keyspace`](https://www.npmjs.com/package/@trinaryex/keyspace)
|
|
16
|
+
(client + `executor` pattern, `queries`/`transactions` split, zod validation, vite build,
|
|
17
|
+
semantic-release).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 1. What this SDK is (and isn't)
|
|
22
|
+
|
|
23
|
+
Trinary Exchange is a Sui-based, DeepBook-style market for an EVE-Frontier game economy.
|
|
24
|
+
Players trade **items** (multicoin balances, priced in the **CRED** trade currency) and
|
|
25
|
+
**currency** at **trade hubs** (Smart Storage Units / SSUs) through on-chain order books
|
|
26
|
+
(the `triexbook` DeepBook fork), settling through a per-player **balance manager** (the
|
|
27
|
+
on-chain trading account).
|
|
28
|
+
|
|
29
|
+
The SDK gives a player/bot one object to:
|
|
30
|
+
|
|
31
|
+
- **Read** market + account state through `api.trinary.exchange` (the `etl-api` indexer),
|
|
32
|
+
authenticated with an **API key** (`x-api-key`).
|
|
33
|
+
- **Write** (mutate) by building Sui **programmable transaction blocks (PTBs)** that the
|
|
34
|
+
caller's **wallet or keypair signs** — exactly the keyspace `executor` pattern.
|
|
35
|
+
|
|
36
|
+
> **The two planes have two different auth models.** The API key authorizes *reads only*.
|
|
37
|
+
> Every state change (create account, deposit, withdraw, place order) is an **on-chain
|
|
38
|
+
> transaction authorized by the player's Sui signature** — the API key never moves funds.
|
|
39
|
+
> This is the single most important thing to communicate to SDK users.
|
|
40
|
+
|
|
41
|
+
### Non-goals (for MVP)
|
|
42
|
+
|
|
43
|
+
- No tribe/DAO governance-wrapped trading (the `armature_trading` `board_voting` path in
|
|
44
|
+
triex-app-api). MVP is **personal** balance-manager trading only.
|
|
45
|
+
- ~~No cancel/modify order, no claim-settled conveniences~~ — **revised 2026-08-21**:
|
|
46
|
+
cancel/cancelAll/modify, `sweepable`, and `claimSettled` were pulled into the MVP (a
|
|
47
|
+
trading bot is not viable without them). Full multi-hub sweep-all and price-history
|
|
48
|
+
endpoints remain out.
|
|
49
|
+
- No custody. The SDK never holds keys; signing is delegated to an `executor`.
|
|
50
|
+
- No websocket/relay live order-book streaming (the app's `TradeContext` relay
|
|
51
|
+
subscriptions). MVP is request/response against the indexer.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 2. MVP scope → endpoint / transaction map
|
|
56
|
+
|
|
57
|
+
Each user story below is tagged **READ** (indexer or fullnode) or **WRITE** (on-chain PTB).
|
|
58
|
+
The "Gateway" column is the **current** state in
|
|
59
|
+
`dynamic-config-registry/gateway-routes/etl-api.json` (as of 2026-08-21).
|
|
60
|
+
|
|
61
|
+
| # | User story | Plane | Indexer endpoint / Move entrypoint | Gateway today |
|
|
62
|
+
|---|---|---|---|---|
|
|
63
|
+
| 1 | Create trading account **iff** none exists (balance manager) | READ+WRITE | READ on-chain `listOwnedObjects` (authoritative; `GET /v1/inventory/balance-manager` stays disabled) → WRITE `balance_manager::new()` (+ `transferObjects` to self) | n/a (on-chain read) |
|
|
64
|
+
| 2 | Fetch item balances | READ | `GET /v1/inventory/balances` — **hub-scoped** (`storage_unit_id` required): warehouse (wallet receipts, by `owner_address`), marketplace (BM, by `balance_manager_id`), hangar (by `inventory_key` owner-cap id) | **enabled** (50 CU) |
|
|
65
|
+
| 3 | Fetch trade-currency (CRED) balance | READ | **fullnode** — wallet coins (`listCoins`) + BM `BalanceKey<CRED>` dynamic field; the indexer inventory endpoint serves *items only* | n/a (fullnode) |
|
|
66
|
+
| 4 | Deposit items from hangar → trading account | READ+WRITE | READ `GET /v1/hubs/{hub_id}/vault` (vaultConfigId + collectionId) + on-chain owner-cap/char resolution → WRITE the **direct-from-hangar sequence** (borrow_owner_cap → receipt::deposit_for_receipt → return_owner_cap → balance_manager::deposit_multicoin); see §6.1 | **enabled** (vault) |
|
|
67
|
+
| 5 | Deposit currency → trading account | WRITE | `balance_manager::deposit<CRED>` (wallet coin selected/merged/split) | n/a (on-chain) |
|
|
68
|
+
| 6 | Discover items with live buy/sell orders across the universe | READ | `GET /v1/discovery` — **open orders sorted by recency**, with filters (hubs/item/side/bm/public) | **enabled** (150 CU) |
|
|
69
|
+
| 7 | Fetch trade-hub details (public/private, owner, location) | READ | `GET /v1/hubs/{hub_id}/vault` + `GET /v1/hubs/{hub_id}/location` (+ `GET /v1/collections/{collection_id}/hub` reverse lookup). `/location` **404s semantically** for unrevealed hubs (most of them) → `TradeHubDetail.location` is nullable. Tribe/fuel are NOT in these responses (post-MVP: hubs economics/enriched routes) | **enabled** |
|
|
70
|
+
| 8 | Fetch items with buy/sell orders at a specific storage unit | READ | `GET /v1/hubs/{hub_id}/items` (`has_bids`/`has_asks` flags) | **enabled** |
|
|
71
|
+
| 9 | Fetch order book for one item at that storage unit | READ | `GET /v1/hubs/{hub_id}/vault` → `GET /v1/pools/resolve` (**`collection_id`+`asset_id`**, not hub+item) → `GET /v1/pools/{pool_id}/orderbook` (returns resting **orders**, not levels) | **enabled** |
|
|
72
|
+
| 10 | Create **limit** buy/sell order | READ+WRITE | READ resolve chain + `GET /v1/pools/{pool_id}/metadata` (fee is 1e9-scaled `fee`, not bps) → WRITE `multicoin_pool::place_limit_order<Quote>` + deposit deficit | **enabled** (reads) |
|
|
73
|
+
| 11 | Create **market** buy/sell order | READ+WRITE | as #10 but `multicoin_pool::place_market_order<Quote>` (no price/expiry) | **enabled** (reads) |
|
|
74
|
+
| 14 | Read own open orders / fills / trades (bots) | READ | `GET /v1/balance-managers/{bm}/open-orders`, `/fills`, `/trades` (epoch-ms `before`/`after` paging) | **enabled** |
|
|
75
|
+
| 12 | Withdraw items from BM → storage unit | WRITE | `balance_manager::withdraw_all_multicoin` → `receipt::redeem_receipt(...ssu, character...)` | n/a (on-chain) |
|
|
76
|
+
| 13 | Withdraw currency from BM → wallet | WRITE | `balance_manager::withdraw_all<CRED>` + `transferObjects` to self | n/a (on-chain) |
|
|
77
|
+
|
|
78
|
+
**Phase 0 status (2026-08-21): done.** The market surface (pools, hubs, collections,
|
|
79
|
+
balance-manager reads, discovery) was enabled in DCR `62acb22`;
|
|
80
|
+
`GET /v1/inventory/balances` followed in `feat/publish-inventory-balances` (already
|
|
81
|
+
published to the running gateway). Two inventory routes stay deliberately disabled under
|
|
82
|
+
the sluice trust model (D11 — the gateway attaches no caller identity):
|
|
83
|
+
`/v1/inventory/balance-manager` (SDK reads on-chain instead — authoritative, §12) and
|
|
84
|
+
`/v1/inventory/receipt-objects` (wallet-receipt discovery is PTB *input* resolution and
|
|
85
|
+
belongs on the fullnode). Note `/v1/discovery` was repriced 30 → 150 CU (2 rps sustained
|
|
86
|
+
on the free/standard tier).
|
|
87
|
+
|
|
88
|
+
> **"Indexer-only" scopes market/account *data* reads, not PTB input resolution.** Building
|
|
89
|
+
> the item deposit/withdraw PTBs (#4, #12) requires live object references — owner-cap
|
|
90
|
+
> `version`/`digest` for `tx.receivingRef`, character/SSU owner-cap object IDs — which are
|
|
91
|
+
> resolved from the **fullnode `suiClient`**, not the indexer. These are always head-current
|
|
92
|
+
> (no lag) and are not "reads" in the indexer sense. The BM-existence check (#1) is likewise
|
|
93
|
+
> best done on-chain (`listOwnedObjects`) to avoid the double-create race (§13).
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 3. Architecture
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
┌────────────────────────────────────────────┐
|
|
101
|
+
player / bot │ @trinaryex/sdk │
|
|
102
|
+
(owns Sui key) ──┤ │
|
|
103
|
+
│ TriexClient (high-level facade) │
|
|
104
|
+
│ ├── account (balance manager lifecycle) │
|
|
105
|
+
│ ├── balances (item + currency reads) │
|
|
106
|
+
│ ├── market (discovery, hubs, orderbook) │
|
|
107
|
+
│ └── orders (limit/market, deposit/wdrw) │
|
|
108
|
+
│ │
|
|
109
|
+
│ queries.ts ──READ──► api.trinary.exchange│───► etl-api (indexer)
|
|
110
|
+
│ (fetch + x-api-key + zod validate) │ (Postgres read models)
|
|
111
|
+
│ │
|
|
112
|
+
│ transactions.ts ──build PTB──► executor() │───► player's wallet / keypair
|
|
113
|
+
│ (pure @mysten/sui Transaction builders) │ └─► Sui network (triexbook)
|
|
114
|
+
└────────────────────────────────────────────┘
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- **Reads** are pure HTTP to the gateway; the SDK adds `x-api-key`, validates responses with
|
|
118
|
+
zod, and returns typed objects. No on-chain reads in MVP (per the indexer-only decision).
|
|
119
|
+
- **Writes** are built as `@mysten/sui` `Transaction` objects by pure functions in
|
|
120
|
+
`transactions.ts`, then handed to a caller-supplied `executor(tx)` that signs+executes.
|
|
121
|
+
The SDK reads whatever object IDs / amounts it needs from the **indexer** first (BM id,
|
|
122
|
+
pool id, vault collection id, deposit deficit), then assembles the PTB.
|
|
123
|
+
|
|
124
|
+
### Why the executor pattern (copied from keyspace)
|
|
125
|
+
|
|
126
|
+
The SDK stays custody-free and wallet-agnostic. Two canonical executors:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// Bot / server keypair (@mysten/sui v2 core client)
|
|
130
|
+
executor: (tx) =>
|
|
131
|
+
suiClient.signAndExecuteTransaction({
|
|
132
|
+
transaction: tx, signer: keypair,
|
|
133
|
+
include: { effects: true, objectTypes: true },
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
// Browser wallet (@mysten/dapp-kit) — pass its signAndExecuteTransaction through
|
|
137
|
+
executor: (tx) => signAndExecuteTransaction({ transaction: tx })
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Executor results are **normalized** (`execute.ts`): both the v2 core-client
|
|
141
|
+
`TransactionResult` and the legacy `{ digest, objectChanges }` shape work, and on-chain
|
|
142
|
+
aborts are rethrown as typed `TransactionFailed` errors with the triexbook abort code
|
|
143
|
+
translated (§11 of TRIEX_SYSTEM_DESIGN). Include `effects` + `objectTypes` (v2) or
|
|
144
|
+
`objectChanges` (legacy) so created objects — e.g. a freshly-created `BalanceManager` —
|
|
145
|
+
are captured for read-your-writes.
|
|
146
|
+
|
|
147
|
+
Optional: a `sponsor` hook to route execution through the gas-station (sponsored/gasless) so
|
|
148
|
+
bots don't need SUI for gas. Post-MVP; the executor signature leaves room (§11, §9 Later).
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 4. Package layout (mirrors keyspace)
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
sdk/
|
|
156
|
+
├── src/
|
|
157
|
+
│ ├── index.ts # public surface (re-exports)
|
|
158
|
+
│ ├── TriexClient.ts # main facade: account/balances/market/orders groups
|
|
159
|
+
│ ├── ReadOnlyClient.ts # indexer-only client (no executor / no signing)
|
|
160
|
+
│ ├── config.ts # network + package-id + tenant resolution, defaults
|
|
161
|
+
│ ├── queries.ts # indexer HTTP client (fetch + x-api-key + zod parse)
|
|
162
|
+
│ ├── onchain.ts # fullnode reads: balances, receipts, character/cap resolution
|
|
163
|
+
│ ├── funding.ts # deficit sourcing: coin merge/split, receipts → hangar
|
|
164
|
+
│ ├── execute.ts # executor-result normalization (v2 + legacy shapes)
|
|
165
|
+
│ ├── book.ts # order-book analytics (levels, mid, spread, vwap)
|
|
166
|
+
│ ├── paging.ts # auto-pagination generators (discovery, fills, trades)
|
|
167
|
+
│ ├── transactions.ts # pure PTB builders (balance_manager, pool, receipt)
|
|
168
|
+
│ ├── money.ts # price scaling, fee/deposit math, decimals helpers
|
|
169
|
+
│ ├── types.ts # config, executor, domain types
|
|
170
|
+
│ ├── schemas.ts # zod schemas for indexer responses (versioned)
|
|
171
|
+
│ └── errors.ts # TriexError enum + TriexClientError class
|
|
172
|
+
├── test/ # vitest/jest unit tests (mock indexer + tx snapshot tests)
|
|
173
|
+
├── package.json # ESM, exports map, peerDeps, semantic-release
|
|
174
|
+
├── vite.config.ts # lib build (ES only) + vite-plugin-dts
|
|
175
|
+
├── tsconfig*.json
|
|
176
|
+
├── .releaserc.json # semantic-release (npm publish @trinaryex/sdk)
|
|
177
|
+
├── DESIGN.md # this document
|
|
178
|
+
└── README.md # quick-start + API reference (write after MVP lands)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Toolchain, matched to keyspace:** TypeScript ESM, `vite` lib build (ES only) +
|
|
182
|
+
`vite-plugin-dts`, `vitest` (keyspace uses jest; prefer **vitest** to match DCR conventions),
|
|
183
|
+
`eslint`/`prettier`, `semantic-release` with conventional commits, published public to npm.
|
|
184
|
+
`@mysten/sui` and `zod` are **peerDependencies**; the SDK bundles nothing heavy.
|
|
185
|
+
|
|
186
|
+
**Org conventions to honor** (`get_conventions scope:general`): path-based `v{N}` API
|
|
187
|
+
versioning (we only *consume* v1), **all timestamps are epoch-millisecond numbers** (parse
|
|
188
|
+
indexer timestamps as such, expose orders' `expireTimestamp` in ms), conventional-commit /
|
|
189
|
+
trunk-based git, no secrets committed.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## 5. Public API design
|
|
194
|
+
|
|
195
|
+
A **layered** design so the package can grow "higher level" later:
|
|
196
|
+
|
|
197
|
+
- **Low level (stable, testable):** `queries.ts` (one function per indexer endpoint) and
|
|
198
|
+
`transactions.ts` (one pure PTB builder per Move entrypoint). No I/O coupling.
|
|
199
|
+
- **High level (ergonomic facade):** `TriexClient` groups methods by domain and orchestrates
|
|
200
|
+
"read to resolve → build PTB → execute" flows.
|
|
201
|
+
|
|
202
|
+
### 5.1 Construction
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { SuiGrpcClient } from '@mysten/sui/grpc'
|
|
206
|
+
import { TriexClient } from '@trinaryex/sdk'
|
|
207
|
+
|
|
208
|
+
const client = new TriexClient({
|
|
209
|
+
// @mysten/sui v2 unified client (`.core` API) — no SuiClient/getFullnodeUrl.
|
|
210
|
+
suiClient: new SuiGrpcClient({
|
|
211
|
+
network: 'testnet',
|
|
212
|
+
baseUrl: 'https://fullnode.testnet.sui.io:443',
|
|
213
|
+
}),
|
|
214
|
+
apiKey: process.env.TRIEX_API_KEY!, // reads → x-api-key
|
|
215
|
+
indexerUrl: 'https://api.trinary.exchange', // default
|
|
216
|
+
network: 'testnet', // selects package-id bundle
|
|
217
|
+
executor: (tx) => signAndExecuteTransaction({ transaction: tx, options: { showObjectChanges: true } }),
|
|
218
|
+
// packageIds optional — resolved from `network` defaults, overridable:
|
|
219
|
+
// packageIds: { triexbook, multicoin, warehouseReceipts, credCoinType, clock: '0x6', ... }
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
`ReadOnlyClient` takes the same config minus `suiClient`/`executor` and exposes the
|
|
224
|
+
indexer-backed read surface with explicit identity params — for dashboards/bots that only
|
|
225
|
+
observe. Currency balances (a fullnode read) live on `TriexClient.balances.currency()` only.
|
|
226
|
+
|
|
227
|
+
### 5.2 Methods → user stories
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
// account (balance manager lifecycle)
|
|
231
|
+
client.account.get(address): Promise<TradingAccount | null> // #1 read (on-chain)
|
|
232
|
+
client.account.ensure(): Promise<{ balanceManagerId: string; created: boolean }> // #1 write (idempotent)
|
|
233
|
+
|
|
234
|
+
// balances — items are hub-scoped indexer reads; currency is a fullnode read
|
|
235
|
+
client.balances.atHub({ storageUnitId, inventoryKey? }): Promise<InventoryBalances> // #2 (warehouse/marketplace/hangar)
|
|
236
|
+
client.balances.currency(address?): Promise<CurrencyBalances> // #3 (wallet + BM CRED, head-current)
|
|
237
|
+
|
|
238
|
+
// deposits / withdrawals (#4, #5, #12, #13)
|
|
239
|
+
client.account.depositCurrency({ amount }): Promise<TxResult> // #5
|
|
240
|
+
client.account.depositItems({ storageUnitId, items: [{ assetId, amount }] }): Promise<TxResult> // #4
|
|
241
|
+
client.account.withdrawCurrency({ amount? }): Promise<TxResult> // #13 (default: all)
|
|
242
|
+
client.account.withdrawItems({ storageUnitId, characterId, items }): Promise<TxResult> // #12
|
|
243
|
+
|
|
244
|
+
// market discovery / hub info (#6, #7, #8, #9)
|
|
245
|
+
client.market.discover(filters?): Promise<DiscoveryResult> // #6
|
|
246
|
+
client.market.hub(hubId): Promise<TradeHubDetail> // #7 (vault + location/owner/visibility)
|
|
247
|
+
client.market.itemsAtHub(hubId): Promise<HubItemsPage> // #8
|
|
248
|
+
client.market.resolvePool({ storageUnitId, assetId }): Promise<string> // #9a (vault → resolve)
|
|
249
|
+
client.market.orderbook({ storageUnitId, assetId }): Promise<Orderbook> // #9 (resting orders, not levels)
|
|
250
|
+
client.market.poolMetadata(poolId): Promise<PoolMetadata> // #10/#11 (decimals + 1e9-scaled fee)
|
|
251
|
+
|
|
252
|
+
// order status (#14) — epoch-ms before/after paging
|
|
253
|
+
client.orders.openOrders(params?): Promise<OpenOrdersPage>
|
|
254
|
+
client.orders.fills(params?): Promise<FillsPage>
|
|
255
|
+
client.orders.trades(params?): Promise<TradesPage>
|
|
256
|
+
|
|
257
|
+
// orders (#10, #11) — each auto-ensures BM + deposits any deficit in one PTB
|
|
258
|
+
client.orders.limit({ storageUnitId, assetId, side, price, quantity, expireAt? }): Promise<TxResult> // #10
|
|
259
|
+
client.orders.market({ storageUnitId, assetId, side, quantity, quoteBudget? }): Promise<TxResult> // #11
|
|
260
|
+
|
|
261
|
+
// pulled forward from post-MVP (bots are not viable without them):
|
|
262
|
+
client.orders.cancel({ storageUnitId, assetId, orderId }) / cancelAll / modify
|
|
263
|
+
client.account.sweepable(): Promise<Sweepable> // claimable proceeds + idle BM items
|
|
264
|
+
client.account.claimSettled({ poolIds? }) // withdraw_settled_amounts per pool
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`side: 'buy' | 'sell'` maps to `isBid`; items are identified by `assetId` (the indexer's
|
|
268
|
+
numeric item-type id, a string). `TxResult = { digest: string; objectChanges?: ... }`.
|
|
269
|
+
Amounts are `bigint` in base units; the SDK converts human ↔ base using coin/item `decimals`
|
|
270
|
+
and applies **price scaling** and **bid-fee overhead** in `money.ts` (see §7). Domain types
|
|
271
|
+
are inferred from the pinned zod wire schemas (`schemas.ts`) — single source of truth.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 6. On-chain transaction builders (`transactions.ts`)
|
|
276
|
+
|
|
277
|
+
Confirmed Move entrypoints (from `triex-app-api` — personal, non-governance path). All are
|
|
278
|
+
pure functions `(args) => Transaction`; the facade fills object IDs from indexer reads.
|
|
279
|
+
|
|
280
|
+
| Builder | Move target | Notes |
|
|
281
|
+
|---|---|---|
|
|
282
|
+
| `newBalanceManager` | `${triexbook}::balance_manager::new()` | returns BM; `transferObjects([bm], self)` when freshly created |
|
|
283
|
+
| `depositCoin` | `${triexbook}::balance_manager::deposit<T>(bm, coin)` | coin prepared via list/merge/split of wallet coins |
|
|
284
|
+
| `depositMulticoinObject` | `${triexbook}::balance_manager::deposit_multicoin(bm, object)` | deposit an owned multicoin `Balance` object (wallet receipts) into BM |
|
|
285
|
+
| `withdrawAllCoin` | `${triexbook}::balance_manager::withdraw_all<T>(bm)` → `transferObjects` | #13 |
|
|
286
|
+
| `withdrawAllMulticoin` | `${triexbook}::balance_manager::withdraw_all_multicoin(bm, collectionId, assetId)` | #12 step 1 |
|
|
287
|
+
| `redeemReceipt` | `${warehouseReceipts}::receipt::redeem_receipt(balance, ssu, character, vaultConfig, collection, isOwner)` | #12 step 2 (BM item → hangar) |
|
|
288
|
+
| `ownerProof` | `${triexbook}::balance_manager::generate_proof_as_owner(bm)` | required before placing/withdrawing |
|
|
289
|
+
| `placeLimitOrderItem` | `${triexbook}::multicoin_pool::place_limit_order<Quote>(pool, bm, proof, orderType, selfMatch, price, qty, isBid, expireTs, clock)` | items (multicoin) |
|
|
290
|
+
| `placeMarketOrderItem` | `${triexbook}::multicoin_pool::place_market_order<Quote>(pool, bm, proof, selfMatch, qty, isBid, clock)` | items (multicoin) |
|
|
291
|
+
| `cancelOrderItem` | `${triexbook}::multicoin_pool::cancel_order<Quote>(pool, bm, proof, orderId u64, clock)` | implemented (+ `cancel_all_orders`, `modify_order`) |
|
|
292
|
+
| `withdrawCoin` | `${triexbook}::balance_manager::withdraw<T>(bm, amount)` | partial currency withdraw |
|
|
293
|
+
| `withdrawSettledAmounts` | `${triexbook}::multicoin_pool::withdraw_settled_amounts<Quote>(pool, bm, proof)` | claim post-fill proceeds into the BM |
|
|
294
|
+
|
|
295
|
+
> `<Quote>` is the CRED coin type (`credCoinType`). Item markets are `multicoin_pool`; the
|
|
296
|
+
> item is identified by `collectionId` + `assetId` (u64), not a Move type parameter.
|
|
297
|
+
|
|
298
|
+
**PTB composition rules learned from the app:**
|
|
299
|
+
- One BM per player, **created on demand** inside the same PTB as the first order, then
|
|
300
|
+
transferred to self. `ensure()` makes this explicit/idempotent.
|
|
301
|
+
- Orders are built as **atomic** PTBs: `[ensure BM] → deposit deficit → generate_proof →
|
|
302
|
+
place_order`. If placement aborts, the deposit rolls back.
|
|
303
|
+
- `clock` is the shared object `0x6`. `expireTimestamp` is **epoch ms**.
|
|
304
|
+
- Coin selection helper (`prepareWalletCoinInput`): `listCoins` → accumulate → `mergeCoins`
|
|
305
|
+
→ `splitCoins(amount)`; throws a typed `InsufficientBalance` error if short.
|
|
306
|
+
|
|
307
|
+
### 6.1 Direct-from-hangar item sourcing (resolves OQ-4)
|
|
308
|
+
|
|
309
|
+
A player can fund an item sell order **directly from their hangar** in the same PTB — the app
|
|
310
|
+
does this in `useTriexbookMulticoinOrders.ts`. The SDK ports this as `sourceItemsIntoBm(...)`,
|
|
311
|
+
covering a deposit deficit from two sources in order:
|
|
312
|
+
|
|
313
|
+
1. **Wallet multicoin receipts.** Find owned `${multicoin}::multicoin::Balance` objects for the
|
|
314
|
+
`assetId` in the right collection, then for each:
|
|
315
|
+
`balance_manager::deposit_multicoin(bm, object)`.
|
|
316
|
+
2. **SSU / character hangar inventory** (only if a deficit remains). For each relevant owner cap
|
|
317
|
+
(SSU owner cap and/or character owner cap):
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
[cap, borrowReceipt] = character::borrow_owner_cap<CapType>(character, receivingRef(capRef))
|
|
321
|
+
[receipt] = receipt::deposit_for_receipt<CapType>(
|
|
322
|
+
ssuObject, character, cap, vaultConfigId,
|
|
323
|
+
vaultCollectionId, assetId /*u64*/, amount /*u32*/)
|
|
324
|
+
character::return_owner_cap<CapType>(character, cap, borrowReceipt)
|
|
325
|
+
balance_manager::deposit_multicoin(bm, receipt)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
**RQ-3 resolved (from the app):** the SSU owner-cap type arg is
|
|
329
|
+
`${worldOriginalPackageId}::storage_unit::StorageUnit`, the character cap
|
|
330
|
+
`${worldOriginalPackageId}::character::Character`; a hub owner drains the SSU slot first,
|
|
331
|
+
then the character slot; non-owners use the character cap only. Hangar slots are dynamic
|
|
332
|
+
fields on the SSU keyed by owner-cap id (`Field<0x2::object::ID, Inventory>`), which is
|
|
333
|
+
also the indexer's `inventory_key`.
|
|
334
|
+
|
|
335
|
+
**Inputs the SDK must resolve first:**
|
|
336
|
+
- `vaultConfigId`, `vaultCollectionId` — from `GET /v1/hubs/{hub_id}/vault` (indexer) keyed off
|
|
337
|
+
the SSU (`storageUnitId` → `0x`-padded 64-hex object id).
|
|
338
|
+
- `characterId` + owner-cap object IDs — on-chain (`fetchCharacterInfo` equivalent via
|
|
339
|
+
`suiClient`), plus the cap object's `version`/`digest` for `tx.receivingRef` (fullnode read).
|
|
340
|
+
- `worldPackageId` (and `worldOriginalPackageId` for the cap type arg) — from the `stillness`
|
|
341
|
+
package-id set (§8).
|
|
342
|
+
|
|
343
|
+
The **withdraw-items** path (#12) is the mirror: `withdraw_all_multicoin` → `redeem_receipt`
|
|
344
|
+
back into the SSU/hangar (needs `ssu`, `character`, `vaultConfig`, `collection`, `isOwner`).
|
|
345
|
+
|
|
346
|
+
> This adds `worldPackageId`/`worldOriginalPackageId` and the SSU-owner-cap resolution to the
|
|
347
|
+
> SDK's responsibilities — heavier than a plain coin deposit. It's the most involved builder
|
|
348
|
+
> in the MVP and the first integration-test target on a live testnet SSU.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 7. Money math (`money.ts`)
|
|
353
|
+
|
|
354
|
+
- **Coin (currency-pair) pools:** `quote = base * price / TRIEXBOOK_PRICE_SCALING`
|
|
355
|
+
where `TRIEXBOOK_PRICE_SCALING = 1_000_000_000` (1e9).
|
|
356
|
+
- **Multicoin (item) pools:** scaling factor `1` → `quote = price * quantity` (confirmed
|
|
357
|
+
against the production app, which passes the unscaled price straight to
|
|
358
|
+
`place_limit_order`; TRIEX_SYSTEM_DESIGN §7's blanket "all prices ×1e9" describes coin
|
|
359
|
+
pools).
|
|
360
|
+
- **Bid deposit overhead:** v1 pools charge a **quote-denominated fee**; a bid must deposit
|
|
361
|
+
`quote × (1e9 + feeRateScaled) / 1e9` (floor-of-total — matches the app's
|
|
362
|
+
`computeBidQuoteDeposit` and the on-chain per-fill floor). `feeRateScaled` is pool
|
|
363
|
+
metadata's raw `fee` (scaled by 1e9; `20_000_000` = 2%, the default volatile fee) — the
|
|
364
|
+
endpoint exposes **no bps field**. Only buyers pay fees; asks are fee-free. Market bids
|
|
365
|
+
require an explicit `quoteBudget` (the app requires `quoteDepositAmount > 0`).
|
|
366
|
+
- **Decimals:** CRED and each item type carry `decimals` (pool metadata's
|
|
367
|
+
`base_asset_decimals` / `quote_asset_decimals`); the SDK exposes both `bigint` base-unit
|
|
368
|
+
and helper `toBase(human, decimals)` / `fromBase(base, decimals)`.
|
|
369
|
+
- **Pool version:** the SDK **always assumes v1** (it supports only the most-recent
|
|
370
|
+
contracts), so v1 fee treatment is hard-coded. No multi-version branching (resolves OQ-6).
|
|
371
|
+
|
|
372
|
+
> Getting scaling/fees wrong silently over/under-funds orders. `money.ts` is the highest-risk
|
|
373
|
+
> unit-test target: snapshot tests against known app values.
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## 8. Configuration & package IDs (`config.ts`)
|
|
378
|
+
|
|
379
|
+
**Canonical source (resolves OQ-2):** package IDs live in `triex-app-api`
|
|
380
|
+
`src/constants/tenants.ts` under the **`stillness`** tenant (the testnet world; also
|
|
381
|
+
`DEFAULT_TENANT`). `triex-app-api` already exposes them at
|
|
382
|
+
`GET /api/v1/package-ids?tenant=stillness` (env-override-applied, `Cache-Control: max-age=60`)
|
|
383
|
+
— **but that endpoint returns Move *package* IDs only; registry / shared-object IDs are
|
|
384
|
+
excluded**. The current stillness trading IDs (for the SDK `testnet` preset):
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
triexPackageId 0x291b9da738dffedd18d7c5049e5e6792270202e03f3c9d9db4c7097670bf6eb2
|
|
388
|
+
triexRegistryId 0x14a58f254b8243bf3f74c057d81cc310498b3d0fe3781650ad58405d7e5f17e4 (shared obj — not in /package-ids)
|
|
389
|
+
multicoinPackageId 0x99a4c039477ac7e7affcb5a5609dd23c29ab69e9436e740d94a4e168c2506cfb
|
|
390
|
+
warehouseReceiptsPackageId 0x0c9d4414aa12eaa1ebf9d32437c1e6403fbf941ffcdca5b9ca16d1769313417e
|
|
391
|
+
credCoinType 0xfbcbd9155669e157ce3999e073930b4c4b67255c3cf88d0d80c76342a31e6710::cred::CRED
|
|
392
|
+
worldPackageId 0x8b8a46ed766fa1358ce7c5c51f6a164b13d627a63e45343f69ed0ba0446c1aa1 (= worldOriginalPackageId)
|
|
393
|
+
clock 0x6
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
**SDK strategy:** ship a `testnet`/`stillness` **preset baked into the release** (single
|
|
397
|
+
supported world + always-latest contracts per §Contract versioning), each field overridable
|
|
398
|
+
via `packageIds`. Optionally hydrate the *package* IDs at runtime from
|
|
399
|
+
`/api/v1/package-ids?tenant=stillness` **iff** that route is externalized through the gateway
|
|
400
|
+
(it's a `triex-app-api` route, not `etl-api` — needs a gateway route to be reachable at
|
|
401
|
+
`api.trinary.exchange`; otherwise it's origin-only). Registry/shared-object IDs
|
|
402
|
+
(`triexRegistryId`, vault registry, etc.) stay baked in since `/package-ids` omits them.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## 9. Phasing / milestones
|
|
407
|
+
|
|
408
|
+
- **Phase 0 — Gateway enablement. ✅ DONE 2026-08-21.** Market surface enabled in DCR
|
|
409
|
+
`62acb22`; `GET /v1/inventory/balances` in `feat/publish-inventory-balances` (published
|
|
410
|
+
to the running gateway). Two inventory routes stay disabled by design (D11) with
|
|
411
|
+
fullnode alternatives — see §2. Remaining nit: merge the balances branch to `main` so
|
|
412
|
+
the next `push-gateway.sh` run doesn't revert it.
|
|
413
|
+
- **Phase 1 — Read core. ✅ DONE 2026-08-21.** `queries.ts` + `schemas.ts` (pinned against
|
|
414
|
+
the committed etl-api upstream spec — RQ-1) + `ReadOnlyClient`: discovery, hub details,
|
|
415
|
+
items-at-hub, pool resolve (vault → collection+asset chain), orderbook, pool metadata,
|
|
416
|
+
hub-scoped item balances, order-status reads. Currency balances implemented as fullnode
|
|
417
|
+
reads (`onchain.ts`, ported from the app's production hooks). Domain types inferred from
|
|
418
|
+
the zod schemas.
|
|
419
|
+
- **Phase 2 — Account lifecycle + deposits/withdrawals. ✅ DONE 2026-08-21.** Deposits
|
|
420
|
+
(coin merge/split; item sourcing wallet-receipts → hangar per §6.1), partial + full
|
|
421
|
+
withdrawals, item redeem into the hangar, `sweepable` + `claimSettled`. All fullnode
|
|
422
|
+
resolution ported from the app (`onchain.ts`, `funding.ts`).
|
|
423
|
+
- **Phase 3 — Orders. ✅ DONE 2026-08-21.** limit + market, buy + sell, atomic PTBs with
|
|
424
|
+
create-BM-in-tx and deficit-only funding; GTC default; market-buy rounding buffer;
|
|
425
|
+
cancel / cancelAll / modify pulled forward from post-MVP. Composition unit-tested
|
|
426
|
+
command-by-command against the app's flows (63+ tests).
|
|
427
|
+
- **Phase 3.5 — Order-status reads (in MVP).** `balance-managers/{bm}/open-orders`, `/fills`,
|
|
428
|
+
`/trades` — pulled into MVP for bots (story #14). Cheap once the balance-manager routes are
|
|
429
|
+
enabled in Phase 0.
|
|
430
|
+
- **Phase 4 — Packaging & docs.** README quick-start, examples (browser wallet + bot
|
|
431
|
+
keypair), semantic-release → publish `@trinaryex/sdk@0.x`.
|
|
432
|
+
- **Later (post-MVP):** full sweep-all convenience (multi-hub redeem), tribe/governance
|
|
433
|
+
trading, **gas-station sponsored (gasless) execution** (optional `sponsor` hook), live
|
|
434
|
+
orderbook streaming, mainnet, coin-pools (currency-pair markets). (Cancel/modify and
|
|
435
|
+
claim-settled were pulled INTO the MVP — a trading bot is not viable without them.)
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## 10. Auth & developer onboarding
|
|
440
|
+
|
|
441
|
+
- **Reads:** developer supplies an **API key** in the `x-api-key` header. Keys are minted
|
|
442
|
+
through the Developer API Keys panel / `stele` (surfaced in the app's `/settings`), billed
|
|
443
|
+
per-request in **Compute Units** against a rate-limit tier (`free`/`standard`/`growth`/…;
|
|
444
|
+
see `rate-limit-tiers/tiers.json`). SDK docs link **https://docs.trinary.exchange/docs/api-keys**,
|
|
445
|
+
which directs users to **trinary.exchange/settings** to create the key (resolves OQ-3).
|
|
446
|
+
- **Writes:** developer supplies a Sui **signer** (wallet in browser, keypair for bots) via
|
|
447
|
+
`executor`. The SDK never sees private keys. Bots need SUI for gas unless sponsored.
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## 11. Decisions & remaining questions
|
|
452
|
+
|
|
453
|
+
### Resolved (2026-08-15)
|
|
454
|
+
|
|
455
|
+
- **OQ-1 — Gateway enablement is work-to-do, not a policy blocker.** We own the routes and
|
|
456
|
+
will expose them; Phase 0 just needs doing (dev → prd). CU costs default to the values
|
|
457
|
+
already in `etl-api.json`.
|
|
458
|
+
- **OQ-2 — Package IDs:** source of truth is the `stillness` tenant in `triex-app-api`
|
|
459
|
+
(`GET /api/v1/package-ids?tenant=stillness`, package IDs only). SDK bakes a `testnet`
|
|
460
|
+
preset; registry/shared IDs baked in (see §8).
|
|
461
|
+
- **OQ-3 — Docs:** https://docs.trinary.exchange/docs/api-keys → create key at
|
|
462
|
+
trinary.exchange/settings (§10).
|
|
463
|
+
- **OQ-4 — Direct-from-hangar sourcing** fully mapped from `useTriexbookMulticoinOrders.ts`
|
|
464
|
+
(borrow_owner_cap → receipt::deposit_for_receipt → return_owner_cap → deposit_multicoin);
|
|
465
|
+
see §6.1.
|
|
466
|
+
- **OQ-5 — Ignore coin-pools.** MVP trades **item↔CRED** via `multicoin_pool` ("pools" family
|
|
467
|
+
only). Coin-pools deferred.
|
|
468
|
+
- **OQ-6 — Always v1.** SDK supports only the newest contracts; no version branching (§7).
|
|
469
|
+
- **OQ-7 — Gas:** left to the caller's `executor`; sponsored/gasless is a **post-MVP** feature
|
|
470
|
+
(optional `sponsor` hook).
|
|
471
|
+
- **OQ-8 — testnet only** (stillness).
|
|
472
|
+
- **OQ-9 — `/v1/discovery`** = open orders sorted by recency with filters; it's the discovery
|
|
473
|
+
primitive for #6. Finalize `DiscoveryResult` against its response shape in Phase 1.
|
|
474
|
+
- **OQ-10 — Order-status reads are IN the MVP** (story #14, Phase 3.5).
|
|
475
|
+
- **OQ-11 — Identity:** for now assume **one address ↔ one character ↔ one BM**; the balances
|
|
476
|
+
read stitches wallet + hangar + BM under that assumption.
|
|
477
|
+
|
|
478
|
+
### Resolved (2026-08-21, Phase 0/1)
|
|
479
|
+
|
|
480
|
+
- **RQ-1 — Response schemas: pinned** in `schemas.ts` against the committed upstream spec
|
|
481
|
+
(`dynamic-config-registry/upstream-specs/etl-api.json`). Notable deltas from the draft:
|
|
482
|
+
`pools/resolve` is keyed by `collection_id`+`asset_id` (hub flows resolve the vault
|
|
483
|
+
first); the orderbook returns resting **orders**, not aggregated levels; pool metadata
|
|
484
|
+
exposes the fee as a 1e9-scaled `fee` string (no bps field); hub vault/location carry
|
|
485
|
+
no tribe/fuel fields (post-MVP).
|
|
486
|
+
- **RQ-4 — `inventory/balances` keying:** hub-scoped (`storage_unit_id` required, one hub
|
|
487
|
+
per call); sections selected by explicit params — `owner_address` → warehouse,
|
|
488
|
+
`balance_manager_id` → marketplace, `inventory_key` (owner-cap id) → hangar. It serves
|
|
489
|
+
**items only** — CRED (#3) is NOT in the endpoint, so currency balances are fullnode
|
|
490
|
+
reads (`balances.currency()`), which write-flow deficit math wants anyway (§12).
|
|
491
|
+
|
|
492
|
+
### Remaining (resolve during Phase 2/3)
|
|
493
|
+
|
|
494
|
+
- **RQ-2:** Is `GET /api/v1/package-ids` reachable via `api.trinary.exchange`, or origin-only?
|
|
495
|
+
If not routed through the gateway, the SDK just uses the baked-in preset (fine for MVP).
|
|
496
|
+
- **RQ-3 — RESOLVED 2026-08-21** from the production app (see §6.1): SSU cap type
|
|
497
|
+
`{worldOriginal}::storage_unit::StorageUnit` (hub owners, drained first), character cap
|
|
498
|
+
`{worldOriginal}::character::Character` (everyone). Live-SSU spot check still worthwhile
|
|
499
|
+
once a funded character-owning signer is available.
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## 12. Indexer lag & consistency
|
|
504
|
+
|
|
505
|
+
The indexer (etl-api) trails chain head by a few seconds. **This is not a correctness problem
|
|
506
|
+
for writes; it's a UX/efficiency concern with two edges to design around.**
|
|
507
|
+
|
|
508
|
+
**Why writes are safe regardless of lag.** Every mutation is validated against *actual
|
|
509
|
+
on-chain state* by Sui validators, and each order is an **atomic PTB** (deposit + proof +
|
|
510
|
+
place bundled). A stale indexer read can only ever produce a transaction that **aborts and
|
|
511
|
+
rolls back** — never a corrupt state or lost funds:
|
|
512
|
+
|
|
513
|
+
- Deposit deficit computed from a stale **too-low** balance → harmless over-deposit (funds sit
|
|
514
|
+
safely in the BM, usable by the next order).
|
|
515
|
+
- Deposit deficit from a stale **too-high** balance → the order aborts on-chain; retryable.
|
|
516
|
+
- Market/limit order sized against a few-seconds-old book → ordinary market slippage.
|
|
517
|
+
|
|
518
|
+
**Edge 1 — double balance-manager creation (story #1).** "One BM per player" is a *convention*,
|
|
519
|
+
not on-chain-enforced, so a stale "no BM" read right after creation could mint a second BM.
|
|
520
|
+
Mitigations (all adopted):
|
|
521
|
+
1. `account.ensure()` resolves the BM id via an **on-chain `listOwnedObjects`** (a cheap,
|
|
522
|
+
head-current, correctness-critical single lookup), not the indexer.
|
|
523
|
+
2. The client **caches the BM id** after first create/read (read-your-writes).
|
|
524
|
+
3. Callers may pass a known `balanceManagerId` to skip discovery entirely.
|
|
525
|
+
|
|
526
|
+
**Edge 2 — read-your-writes for freshly created objects.** Every write returns object IDs from
|
|
527
|
+
`objectChanges` (new BM id, new order id). Bots should trust those over an immediate indexer
|
|
528
|
+
re-read. Optional `waitForTx`/confirm helper; optional short client-side cache (TTL) so a
|
|
529
|
+
just-submitted state isn't clobbered by a lagging read.
|
|
530
|
+
|
|
531
|
+
**Not affected by lag:** PTB *input* resolution (owner-cap `version`/`digest` for
|
|
532
|
+
`tx.receivingRef`, coin/receipt object selection) reads the **fullnode**, which is always
|
|
533
|
+
head-current. "Indexer-only" scopes market/account *data*, not transaction inputs.
|
|
534
|
+
|
|
535
|
+
**Docs stance:** the README states plainly that read endpoints are eventually-consistent
|
|
536
|
+
(seconds), on-chain writes are authoritative, and post-write reads should prefer returned
|
|
537
|
+
object IDs. For latency-sensitive bots, note the post-MVP live-stream option.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
## 13. Appendix — current `etl-api` gateway route inventory
|
|
542
|
+
|
|
543
|
+
From `dynamic-config-registry/gateway-routes/etl-api.json` (65 routes), **as of 2026-08-21**
|
|
544
|
+
(DCR `62acb22` + `feat/publish-inventory-balances`). Phase 0 is done: everything the MVP
|
|
545
|
+
reads is live.
|
|
546
|
+
|
|
547
|
+
**Enabled — MVP reads** (CU costs as published):
|
|
548
|
+
|
|
549
|
+
```
|
|
550
|
+
GET /v1/discovery (150 CU) # story 6 — repriced 30 → 150 (2 rps sustained on free/standard)
|
|
551
|
+
GET /v1/inventory/balances (50 CU) # story 2 (items only; hub-scoped)
|
|
552
|
+
GET /v1/hubs/{hub_id}/vault (20 CU) # stories 4,7,9 (vaultConfig/collection)
|
|
553
|
+
GET /v1/hubs/{hub_id}/location (20 CU) # story 7 (location/owner/visibility)
|
|
554
|
+
GET /v1/hubs/{hub_id}/items (20 CU) # story 8
|
|
555
|
+
GET /v1/collections/{collection_id}/hub (20 CU) # story 7 reverse lookup
|
|
556
|
+
GET /v1/pools/resolve (30 CU) # stories 9,10,11 (collection+asset → poolId)
|
|
557
|
+
GET /v1/pools/{pool_id}/orderbook (30 CU) # story 9
|
|
558
|
+
GET /v1/pools/{pool_id}/metadata (20 CU) # stories 10,11 (1e9-scaled fee)
|
|
559
|
+
GET /v1/balance-managers/{balance_manager_id}/open-orders (30 CU) # story 14
|
|
560
|
+
GET /v1/balance-managers/{balance_manager_id}/fills (30 CU) # story 14
|
|
561
|
+
GET /v1/balance-managers/{balance_manager_id}/trades (30 CU) # story 14
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
(Also enabled beyond MVP need: `/v1/balance-managers/{bm}/sweepable`, `/owners`, the wider
|
|
565
|
+
pools/hubs/assets/activity families, `GET /v1/characters/*`, `GET /v1/orgs*`,
|
|
566
|
+
`GET /v1/search` (100 CU), `GET /v1/stats`, `GET /v1/trades/recent`.)
|
|
567
|
+
|
|
568
|
+
**Deliberately disabled (D11 — sluice attaches no caller identity):** the remaining
|
|
569
|
+
`inventory` family, including the seven POST container mutations and two GETs the SDK
|
|
570
|
+
replaces with fullnode reads — `GET /v1/inventory/balance-manager` (story 1: on-chain
|
|
571
|
+
`listOwnedObjects` is authoritative) and `GET /v1/inventory/receipt-objects` (story 4:
|
|
572
|
+
wallet-receipt discovery is PTB input resolution). `coin-pools/*` stays disabled by choice.
|
|
573
|
+
|
|
574
|
+
> Remember a merge to `main` does **not** update the running gateway — run
|
|
575
|
+
> `scripts/push-gateway.sh --env <env>` (per CLAUDE.md). The balances route is already
|
|
576
|
+
> published; its branch still needs merging to `main` so a future publish from a clean
|
|
577
|
+
> checkout doesn't revert it.
|