@trinaryex/sdk 3.9.0 → 4.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 +253 -71
- package/README.md +218 -16
- package/dist/ReadOnlyClient.d.ts +42 -8
- package/dist/TriexClient.d.ts +73 -8
- package/dist/armature/OrgClient.d.ts +372 -0
- package/dist/armature/OrgClientGroups.d.ts +186 -0
- package/dist/armature/OrgsApi.d.ts +69 -0
- package/dist/armature/actions.d.ts +75 -0
- package/dist/armature/create.d.ts +27 -0
- package/dist/armature/currency.d.ts +64 -0
- package/dist/armature/executors.d.ts +44 -0
- package/dist/armature/governance.d.ts +194 -0
- package/dist/armature/harness.d.ts +96 -0
- package/dist/armature/lifecycle.d.ts +84 -0
- package/dist/armature/plan.d.ts +39 -0
- package/dist/armature/proposals.d.ts +51 -0
- package/dist/armature/queries.d.ts +16 -0
- package/dist/armature/schemas.d.ts +559 -0
- package/dist/armature/trading.d.ts +114 -0
- package/dist/armature/transactions.d.ts +160 -0
- package/dist/armature/treasury.d.ts +41 -0
- package/dist/armature/tree.d.ts +13 -0
- package/dist/armature/types.d.ts +59 -0
- package/dist/armature/upgrade.d.ts +27 -0
- package/dist/armature/vault.d.ts +94 -0
- package/dist/coins/CoinsApi.d.ts +53 -0
- package/dist/coins/funding.d.ts +6 -0
- package/dist/coins/money.d.ts +36 -0
- package/dist/coins/onchain.d.ts +162 -0
- package/dist/coins/schemas.d.ts +456 -0
- package/dist/coins/transactions.d.ts +82 -0
- package/dist/coins/types.d.ts +115 -0
- package/dist/errors.d.ts +11 -1
- package/dist/funding.d.ts +2 -2
- package/dist/http.d.ts +3 -0
- package/dist/index.d.ts +63 -8
- package/dist/index.js +10587 -959
- package/dist/money.d.ts +15 -0
- package/dist/moveAbort.d.ts +28 -0
- package/dist/moveAbortCatalog.generated.d.ts +9 -0
- package/dist/onchain.d.ts +55 -3
- package/dist/paging.d.ts +9 -3
- package/dist/queries.d.ts +35 -7
- package/dist/schemas.d.ts +1911 -50
- package/dist/transactions.d.ts +63 -1
- package/dist/types.d.ts +116 -7
- package/package.json +3 -1
package/DESIGN.md
CHANGED
|
@@ -11,8 +11,8 @@ first module, with room to grow into a higher-level, full-featured client.
|
|
|
11
11
|
**Audience:** players and bots trading on Trinary Exchange via an API key.
|
|
12
12
|
**Target (MVP):** **testnet only**, the **`stillness`** tenant; **only the most-recent (v1)
|
|
13
13
|
[CLOB contracts](https://github.com/loash-industries/trinary-exchange)** are supported.
|
|
14
|
-
Item↔currency (CRED) markets via `multicoin_pool` ("pools"); coin-pools
|
|
15
|
-
for
|
|
14
|
+
Item↔currency (CRED) markets via `multicoin_pool` ("pools"); coin (currency-pair) pools
|
|
15
|
+
were added for cycle 7 as the `coins` group — see [DESIGN-COINS.md](./DESIGN-COINS.md).
|
|
16
16
|
**Models after:** [`@trinaryex/keyspace`](https://www.npmjs.com/package/@trinaryex/keyspace)
|
|
17
17
|
(client + `executor` pattern, `queries`/`transactions` split, zod validation, vite build,
|
|
18
18
|
semantic-release).
|
|
@@ -61,20 +61,20 @@ The "Gateway" column is the **current** state in
|
|
|
61
61
|
|
|
62
62
|
| # | User story | Plane | Indexer endpoint / Move entrypoint | Gateway today |
|
|
63
63
|
|---|---|---|---|---|
|
|
64
|
-
| 1 | Create trading account **iff** none exists (balance manager) | READ+WRITE | READ on-chain `listOwnedObjects` (authoritative; `GET /v1/inventory/balance-manager` stays disabled) → WRITE `
|
|
64
|
+
| 1 | Create trading account **iff** none exists (balance manager) | READ+WRITE | READ on-chain `listOwnedObjects` (authoritative; `GET /v1/inventory/balance-manager` stays disabled) → WRITE `trading_account::new()` (+ `transferObjects` to self) | n/a (on-chain read) |
|
|
65
65
|
| 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) |
|
|
66
66
|
| 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) |
|
|
67
|
-
| 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 →
|
|
68
|
-
| 5 | Deposit currency → trading account | WRITE | `
|
|
67
|
+
| 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 → trading_account::deposit_multicoin); see §6.1 | **enabled** (vault) |
|
|
68
|
+
| 5 | Deposit currency → trading account | WRITE | `trading_account::deposit<CRED>` (wallet coin selected/merged/split) | n/a (on-chain) |
|
|
69
69
|
| 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) |
|
|
70
70
|
| 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** |
|
|
71
71
|
| 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** |
|
|
72
72
|
| 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** |
|
|
73
73
|
| 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) |
|
|
74
74
|
| 11 | Create **market** buy/sell order | READ+WRITE | as #10 but `multicoin_pool::place_market_order<Quote>` (no price/expiry) | **enabled** (reads) |
|
|
75
|
-
| 14 | Read own open orders / fills / trades (bots) | READ | `GET /v1/
|
|
76
|
-
| 12 | Withdraw items from BM → storage unit | WRITE | `
|
|
77
|
-
| 13 | Withdraw currency from BM → wallet | WRITE | `
|
|
75
|
+
| 14 | Read own open orders / fills / trades (bots) | READ | `GET /v1/trading-accounts/{trading_account_id}/open-orders`, `/fills`, `/trades` (epoch-ms `before`/`after` paging; `next_cursor` is the last row's timestamp, null on a short page) | **enabled** |
|
|
76
|
+
| 12 | Withdraw items from BM → storage unit | WRITE | `trading_account::withdraw_all_multicoin` → `receipt::redeem_receipt(...ssu, character...)` | n/a (on-chain) |
|
|
77
|
+
| 13 | Withdraw currency from BM → wallet | WRITE | `trading_account::withdraw_all<CRED>` + `transferObjects` to self | n/a (on-chain) |
|
|
78
78
|
|
|
79
79
|
**Phase 0 status (2026-08-21): done.** The market surface (pools, hubs, collections,
|
|
80
80
|
balance-manager reads, discovery) was enabled in DCR `62acb22`;
|
|
@@ -93,6 +93,49 @@ on the free/standard tier).
|
|
|
93
93
|
> (no lag) and are not "reads" in the indexer sense. The BM-existence check (#1) is likewise
|
|
94
94
|
> best done on-chain (`listOwnedObjects`) to avoid the double-create race (§13).
|
|
95
95
|
|
|
96
|
+
### 2.1 Beyond the stories — full gateway read coverage (2026-10-01)
|
|
97
|
+
|
|
98
|
+
Every operation the gateway publishes now has an SDK method (bar `/v1/coins`,
|
|
99
|
+
owned by the coin-pool module, and the deprecated `/v1/tribes/{id}` alias).
|
|
100
|
+
`test/gateway.test.ts` enforces it: a newly published, non-deprecated route
|
|
101
|
+
fails the suite until it is wrapped or explicitly excluded, and
|
|
102
|
+
`test/schema-conformance.test.ts` parses payloads synthesised from every
|
|
103
|
+
wrapped operation's response schema (minimal: required fields only, nullables
|
|
104
|
+
null; full: every field) so a requiredness/nullability drift fails CI before it
|
|
105
|
+
throws on live data.
|
|
106
|
+
|
|
107
|
+
| Need | Endpoint | SDK | CU |
|
|
108
|
+
|---|---|---|---|
|
|
109
|
+
| What became of an order (open / filled / cancelled + fills) | `GET /v1/pools/{pool_id}/orders/{order_id}` | `orders.get` · `ro.order` | 30 |
|
|
110
|
+
| One fill, both sides + both fees | `GET /v1/fills/{event_digest}` | `orders.fill` · `ro.fill` | 30 |
|
|
111
|
+
| Public tape, all item markets | `GET /v1/trades/recent` | `market.recentTrades` · `iterateRecentTrades` | 50 |
|
|
112
|
+
| Plain market price (no fee), item-wide or per hub | `GET /v1/display-prices`, `/{item_id}` | `market.displayPrices` / `displayPrice` | 50 / 20 |
|
|
113
|
+
| Hub fee reserve + depth | `GET /v1/hubs/economics` | `market.hubEconomics` | 50 |
|
|
114
|
+
| Pools by unclaimed fees | `GET /v1/pools/top-by-fees` | `market.topPoolsByFees` | 50 |
|
|
115
|
+
| Platform aggregates | `GET /v1/stats` | `market.stats` | 50 |
|
|
116
|
+
| Characters | `GET /v1/characters/{id}`, `/address/{a}`, `/name/{n}`, `/batch` | `characters.get` / `byAddress` / `byName` / `batch` | 20 / 20 / 20 / 50 |
|
|
117
|
+
| Tribes | `GET /v1/world/tribes/{tribe_id}` | `characters.tribe` | 20 |
|
|
118
|
+
| World items & recipes (CDN, static) | `GET /v1/world/items`, `/{asset_id}`, `/v1/world/recipes`, `/{recipe_id}` | `world.items` / `item` / `recipes` / `recipesFor` | 20 each |
|
|
119
|
+
| Routing (location-api) | `GET /v1/routing/route`, `/compare`, `/stats` | `routing.route` / `compare` / `stats` | 100 / 300 / 20 |
|
|
120
|
+
|
|
121
|
+
Notes that change how results are read:
|
|
122
|
+
|
|
123
|
+
- **Units on the public tape differ.** `/v1/trades/recent` returns
|
|
124
|
+
HUMAN-READABLE `price` / `quantity` / `fee_amount` (already shifted by the
|
|
125
|
+
quote decimals) — the SDK keeps them as strings. Every other trade/fill read
|
|
126
|
+
is raw integers → `bigint`.
|
|
127
|
+
- **Fees (cycle 7).** Pool metadata's `fee` is the fee class's ENTRY tier (the
|
|
128
|
+
higher of old/new while a class change is pending) — an upper bound, right
|
|
129
|
+
for deposit buffers. The fee a fill charged is on the fill; `fee_rate_bps`
|
|
130
|
+
on the tape is derived per fill (`taker_fee × 10000 / quote_quantity`), and
|
|
131
|
+
the indexer now records maker fees too. Display prices carry no fee.
|
|
132
|
+
- **Solar system names are player-reported (cycle 7).** location-api fills
|
|
133
|
+
names from EF-Map community reports; until a system is named, its
|
|
134
|
+
`solar_system_name` is null and name lookups 404 (`SolarSystemNotFound` /
|
|
135
|
+
`RouteNotFound`) while id lookups work. Routing is name-only.
|
|
136
|
+
`/v1/spatial/stats` gains `known_solar_system_names`. The SDK accepts both
|
|
137
|
+
the published (string) and post-deploy (nullable) shapes.
|
|
138
|
+
|
|
96
139
|
---
|
|
97
140
|
|
|
98
141
|
## 3. Architecture
|
|
@@ -104,8 +147,11 @@ on the free/standard tier).
|
|
|
104
147
|
│ TriexClient (high-level facade) │
|
|
105
148
|
│ ├── account (balance manager lifecycle) │
|
|
106
149
|
│ ├── balances (item + currency reads) │
|
|
107
|
-
│ ├── market (discovery, hubs, orderbook
|
|
108
|
-
│
|
|
150
|
+
│ ├── market (discovery, hubs, orderbook, │
|
|
151
|
+
│ │ tape, prices, stats) │
|
|
152
|
+
│ ├── orders (limit/market, status reads) │
|
|
153
|
+
│ ├── spatial · routing (star map, routes) │
|
|
154
|
+
│ └── characters · world (players, items) │
|
|
109
155
|
│ │
|
|
110
156
|
│ queries.ts ──READ──► api.trinary.exchange│───► etl-api (indexer)
|
|
111
157
|
│ (fetch + x-api-key + zod validate) │ (Postgres read models)
|
|
@@ -228,9 +274,9 @@ observe. Currency balances (a fullnode read) live on `TriexClient.balances.curre
|
|
|
228
274
|
### 5.2 Methods → user stories
|
|
229
275
|
|
|
230
276
|
```ts
|
|
231
|
-
// account (
|
|
277
|
+
// account (trading account lifecycle)
|
|
232
278
|
client.account.get(address): Promise<TradingAccount | null> // #1 read (on-chain)
|
|
233
|
-
client.account.ensure(): Promise<{
|
|
279
|
+
client.account.ensure(): Promise<{ tradingAccountId: string; created: boolean }> // #1 write (idempotent)
|
|
234
280
|
|
|
235
281
|
// balances — items are hub-scoped indexer reads; currency is a fullnode read
|
|
236
282
|
client.balances.atHub({ storageUnitId, inventoryKey? }): Promise<InventoryBalances> // #2 (warehouse/marketplace/hangar)
|
|
@@ -259,7 +305,7 @@ client.market.hubsEnriched({ hubIds }): Promise<HubEnriched[]> // batch
|
|
|
259
305
|
client.market.assemblyOwners({ assemblyIds }): Promise<AssemblyOwner[]> // batch ≤200
|
|
260
306
|
client.market.assembliesEnriched({ assemblyIds }): Promise<AssemblyEnriched[]> // batch ≤200: + character/name
|
|
261
307
|
client.market.solarSystemNames({ solarSystemIds }): Promise<SolarSystemName[]> // batch ≤200
|
|
262
|
-
client.account.owners({
|
|
308
|
+
client.account.owners({ tradingAccountIds }): Promise<TradingAccountOwner[]> // batch ≤200: player:/ou: tagged
|
|
263
309
|
|
|
264
310
|
// spatial — the star map (Universe | Solar Systems); no account or signer
|
|
265
311
|
client.spatial.system(nameOrId): Promise<SolarSystem> // coords + constellation + region
|
|
@@ -273,15 +319,52 @@ client.spatial.stats(): Promise<SpatialStats> // star-ma
|
|
|
273
319
|
client.orders.openOrders(params?): Promise<OpenOrdersPage>
|
|
274
320
|
client.orders.fills(params?): Promise<FillsPage>
|
|
275
321
|
client.orders.trades(params?): Promise<TradesPage>
|
|
322
|
+
client.orders.get({ poolId, orderId }): Promise<OrderDetail> // any order, any state, + fills
|
|
323
|
+
client.orders.fill(eventDigest): Promise<FillDetail> // both sides + both fees
|
|
324
|
+
|
|
325
|
+
// market-wide feeds, prices & rankings (§2.1)
|
|
326
|
+
client.market.recentTrades({ before?, after?, limit?, publicOnly?, assetId? }): Promise<RecentTradesPage>
|
|
327
|
+
client.market.displayPrices({ itemIds, storageUnitIds?, fallback? }): Promise<DisplayPrice[]>
|
|
328
|
+
client.market.displayPrice(itemId, { storageUnitId?, fallback? }?): Promise<DisplayPrice>
|
|
329
|
+
client.market.hubEconomics({ hubIds }): Promise<HubEconomics[]>
|
|
330
|
+
client.market.topPoolsByFees({ limit? }?): Promise<PoolFees[]>
|
|
331
|
+
client.market.stats(): Promise<PlatformStats>
|
|
332
|
+
|
|
333
|
+
// routing — names only; names are player-reported in cycle 7
|
|
334
|
+
client.routing.route({ origin, destination, optimization?, mass?, gateWeight?, maxJumpRangeLy? }): Promise<Route>
|
|
335
|
+
client.routing.compare({ origin, destination, mass?, gateWeight?, maxJumpRangeLy? }): Promise<RouteComparison>
|
|
336
|
+
client.routing.stats(): Promise<RoutingStats>
|
|
337
|
+
|
|
338
|
+
// characters & tribes
|
|
339
|
+
client.characters.get(characterId, { enrich? }?): Promise<Character>
|
|
340
|
+
client.characters.byAddress(address?, { enrich? }?): Promise<Character[]> // defaults to the player
|
|
341
|
+
client.characters.byName(name): Promise<Character[]>
|
|
342
|
+
client.characters.batch({ addresses }): Promise<CharacterLookup[]> // ≤500
|
|
343
|
+
client.characters.tribe(tribeId): Promise<Tribe>
|
|
344
|
+
|
|
345
|
+
// world reference data (static — cache it)
|
|
346
|
+
client.world.items(): Promise<WorldItem[]>
|
|
347
|
+
client.world.item(assetId): Promise<ItemInfo>
|
|
348
|
+
client.world.recipes(): Promise<Recipe[]>
|
|
349
|
+
client.world.recipesFor(productAssetId): Promise<Recipe[]> // [] when not craftable
|
|
276
350
|
|
|
277
351
|
// orders (#10, #11) — each auto-ensures BM + deposits any deficit in one PTB
|
|
278
352
|
client.orders.limit({ storageUnitId, assetId, side, price, quantity, expireAt? }): Promise<TxResult> // #10
|
|
279
353
|
client.orders.market({ storageUnitId, assetId, side, quantity, quoteBudget? }): Promise<TxResult> // #11
|
|
280
354
|
|
|
281
355
|
// pulled forward from post-MVP (bots are not viable without them):
|
|
282
|
-
client.orders.cancel({ storageUnitId, assetId, orderId }) / cancelAll / modify
|
|
356
|
+
client.orders.cancel({ storageUnitId, assetId, orderId }) / cancelMany({ …, orderIds }) / cancelAll / modify
|
|
357
|
+
client.orders.fees({ storageUnitId, assetId } | { poolId }): Promise<TradingFees> // ladder + own tier (fullnode)
|
|
283
358
|
client.account.sweepable(): Promise<Sweepable> // claimable proceeds + idle BM items
|
|
284
359
|
client.account.claimSettled({ poolIds? }) // withdraw_settled_amounts per pool
|
|
360
|
+
|
|
361
|
+
// trading-account administration (owner-only)
|
|
362
|
+
client.account.register() // registry::get_trading_account_ids bookkeeping
|
|
363
|
+
client.account.mintCap({ kind, recipient? }) / revokeCap({ capId }) / caps(address?)
|
|
364
|
+
|
|
365
|
+
// item-pool lifecycle
|
|
366
|
+
client.market.createPool({ storageUnitId, assetId }) // 500 CRED, permissionless
|
|
367
|
+
client.market.claimOperatorShare({ poolIds }) // hub revenue share, permissionless
|
|
285
368
|
```
|
|
286
369
|
|
|
287
370
|
`side: 'buy' | 'sell'` maps to `isBid`; items are identified by `assetId` (the indexer's
|
|
@@ -294,23 +377,51 @@ are inferred from the pinned zod wire schemas (`schemas.ts`) — single source o
|
|
|
294
377
|
|
|
295
378
|
## 6. On-chain transaction builders (`transactions.ts`)
|
|
296
379
|
|
|
297
|
-
Confirmed Move entrypoints (
|
|
298
|
-
|
|
380
|
+
Confirmed Move entrypoints (personal, non-governance path), verified against
|
|
381
|
+
trinary-exchange `main` (cycle 7 + TRIEX-158): the `balance_manager` module is now
|
|
382
|
+
`trading_account` (`BalanceManager` → `TradingAccount`), order ids are `u128`, and
|
|
383
|
+
placement / cancel / cancel-many / cancel-all / modify / swaps take the shared `FeePolicy`
|
|
384
|
+
(`triexFeePolicy`) right after the pool. All are pure functions that append to a caller's
|
|
385
|
+
`Transaction`; the facade fills object IDs from indexer + fullnode reads. Unit tests pin
|
|
386
|
+
each target's argument order (`test/writes.test.ts`, `test/transactions.test.ts`).
|
|
299
387
|
|
|
300
388
|
| Builder | Move target | Notes |
|
|
301
389
|
|---|---|---|
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `
|
|
305
|
-
| `
|
|
306
|
-
| `
|
|
390
|
+
| `newTradingAccount` | `trading_account::new()` | `transferObjects([bm], self)` when freshly created |
|
|
391
|
+
| `newTradingAccountWithOwner` / `…WithOwnerAndCaps` | `trading_account::new_with_custom_owner(_and_caps)(owner)` | builder only; the `_and_caps` caps go to `owner` on-chain |
|
|
392
|
+
| `registerTradingAccount` | `trading_account::register_trading_account(bm, registry)` | `account.register()`; ≤100 per owner |
|
|
393
|
+
| `mintTradingAccountCap` | `trading_account::mint_{trade,deposit,withdraw}_cap(bm)` | `account.mintCap()`; ≤1000 live caps |
|
|
394
|
+
| `revokeTradingAccountCap` | `trading_account::revoke_trade_cap(bm, &ID)` | `account.revokeCap()`; revokes any of the three cap kinds |
|
|
395
|
+
| `generateProofAsOwner` / `generateProofAsTrader` | `trading_account::generate_proof_as_{owner(bm), trader(bm, tradeCap)}` | proof before placing/cancelling/claiming |
|
|
396
|
+
| `depositCoin` / `depositCoinWithCap` | `trading_account::deposit<T>(bm, coin)` / `deposit_with_cap<T>(bm, cap, coin)` | coin prepared via list/merge/split of wallet coins |
|
|
397
|
+
| `depositMulticoinObject` / `depositMulticoinWithCap` | `trading_account::deposit_multicoin(bm, balance)` / `…_with_cap(bm, cap, balance)` | wallet receipts / hangar receipts into the account |
|
|
398
|
+
| `withdrawCoin` / `withdrawAllCoin` / `withdrawCoinWithCap` | `trading_account::withdraw<T>(bm, amount)` / `withdraw_all<T>(bm)` / `withdraw_with_cap<T>(bm, cap, amount)` | #13 |
|
|
399
|
+
| `withdrawMulticoin` / `withdrawAllMulticoin` / `withdrawMulticoinWithCap` | `trading_account::withdraw_multicoin(bm, collectionId, assetId, amount)` / `withdraw_all_multicoin(bm, collectionId, assetId)` / `…_with_cap` | #12 step 1 (`withdrawItems` uses the partial form when `amount` is set) |
|
|
307
400
|
| `redeemReceipt` | `${warehouseReceipts}::receipt::redeem_receipt(balance, ssu, character, vaultConfig, collection, isOwner)` | #12 step 2 (BM item → hangar) |
|
|
308
|
-
| `
|
|
309
|
-
| `
|
|
310
|
-
| `
|
|
311
|
-
| `
|
|
312
|
-
| `
|
|
313
|
-
| `
|
|
401
|
+
| `placeLimitOrderItem` | `multicoin_pool::place_limit_order<Quote>(pool, policy, bm, proof, orderType, selfMatch, price, qty, isBid, expireTs, clock)` | `…_with_quote_fees` is an identical alias |
|
|
402
|
+
| `placeMarketOrderItem` | `multicoin_pool::place_market_order<Quote>(pool, policy, bm, proof, selfMatch, qty, isBid, clock)` | IOC at the max/min price |
|
|
403
|
+
| `cancelOrderItem` / `cancelOrdersItem` / `cancelAllOrdersItem` | `multicoin_pool::cancel_order(…, orderId u128, clock)` / `cancel_orders(…, vector<u128>, clock)` / `cancel_all_orders(…, clock)` | all `(pool, policy, bm, proof, …)` |
|
|
404
|
+
| `modifyOrderItem` | `multicoin_pool::modify_order<Quote>(pool, policy, bm, proof, orderId u128, newQty, clock)` | reduce only |
|
|
405
|
+
| `withdrawSettledAmounts` / `…Permissionless` | `multicoin_pool::withdraw_settled_amounts<Quote>(pool, bm, proof)` / `…_permissionless(pool, bm)` | no `FeePolicy` |
|
|
406
|
+
| `swapExact{Base,Quote}For{Quote,Base}Item` | `multicoin_pool::swap_exact_*<Quote>(pool, policy, in, credIn, minOut, clock)` | builder only: account-less swaps (temporary account on-chain) |
|
|
407
|
+
| `swapExact…WithTradingAccountItem` | `multicoin_pool::swap_exact_*_with_trading_account<Quote>(pool, policy, bm, tradeCap, depositCap, withdrawCap, in, minOut, clock)` | builder only |
|
|
408
|
+
| `createPermissionlessPoolItem` | `multicoin_pool::create_permissionless_pool<Quote>(registry, policy, collection, assetId, fee)` | `market.createPool()`; fee = 500 CRED |
|
|
409
|
+
| `claimOperatorShareItem` | `multicoin_pool::claim_operator_share<Quote>(pool, policy, registry, clock)` | `market.claimOperatorShare()` |
|
|
410
|
+
| `updatePoolAllowedVersionsItem` | `multicoin_pool::update_pool_allowed_versions<Quote>(pool, registry)` | builder only (post-upgrade maintenance) |
|
|
411
|
+
|
|
412
|
+
**Fee reads** (`onchain.ts` `getPoolTradingFees`, facade `orders.fees()`) are one
|
|
413
|
+
simulated transaction (`simulateTransaction` + `commandResults`, nothing executes) of the
|
|
414
|
+
view functions `multicoin_pool::{pool_fee_class, pool_fee_schedule,
|
|
415
|
+
pool_fee_schedule_next, trade_params_for_account, account_fee_tier,
|
|
416
|
+
account_fee_turnover}` and `fee_policy::cancel_retention_bps`.
|
|
417
|
+
|
|
418
|
+
**Deliberately not wrapped:** `trading_account::new_with_uid_owner_and_caps` (TRIEX-158 —
|
|
419
|
+
takes `&mut UID`, so only a Move module that owns the parent object can call it; Armature
|
|
420
|
+
uses it), every `TriexAdminCap`-gated function (`create_pool_admin`, `set_pool_fee_class`,
|
|
421
|
+
`unregister_pool_admin`, `update_allowed_versions`, `withdraw_pool_fees`, the
|
|
422
|
+
`fee_policy` class / operator-share / adapter setters, the `registry` setters), and
|
|
423
|
+
`fee_policy::register_operator_beneficiary_with_witness` (callable only through the
|
|
424
|
+
admin-registered adapter package's witness).
|
|
314
425
|
|
|
315
426
|
> `<Quote>` is the CRED coin type (`credCoinType`). Item markets are `multicoin_pool`; the
|
|
316
427
|
> item is identified by `collectionId` + `assetId` (u64), not a Move type parameter.
|
|
@@ -332,7 +443,7 @@ covering a deposit deficit from two sources in order:
|
|
|
332
443
|
|
|
333
444
|
1. **Wallet multicoin receipts.** Find owned `${multicoin}::multicoin::Balance` objects for the
|
|
334
445
|
`assetId` in the right collection, then for each:
|
|
335
|
-
`
|
|
446
|
+
`trading_account::deposit_multicoin(bm, object)`.
|
|
336
447
|
2. **SSU / character hangar inventory** (only if a deficit remains). For each relevant owner cap
|
|
337
448
|
(SSU owner cap and/or character owner cap):
|
|
338
449
|
|
|
@@ -342,7 +453,7 @@ covering a deposit deficit from two sources in order:
|
|
|
342
453
|
ssuObject, character, cap, vaultConfigId,
|
|
343
454
|
vaultCollectionId, assetId /*u64*/, amount /*u32*/)
|
|
344
455
|
character::return_owner_cap<CapType>(character, cap, borrowReceipt)
|
|
345
|
-
|
|
456
|
+
trading_account::deposit_multicoin(bm, receipt)
|
|
346
457
|
```
|
|
347
458
|
|
|
348
459
|
**RQ-3 resolved (from the app):** the SSU owner-cap type arg is
|
|
@@ -372,17 +483,36 @@ back into the SSU/hangar (needs `ssu`, `character`, `vaultConfig`, `collection`,
|
|
|
372
483
|
## 7. Money math (`money.ts`)
|
|
373
484
|
|
|
374
485
|
- **Coin (currency-pair) pools:** `quote = base * price / TRIEX_PRICE_SCALING`
|
|
375
|
-
where `TRIEX_PRICE_SCALING = 1_000_000_000` (1e9).
|
|
486
|
+
where `TRIEX_PRICE_SCALING = 1_000_000_000` (1e9). Full cycle-7 model (maker + taker
|
|
487
|
+
fees, bid deposit bound, minimum size) in DESIGN-COINS.md §3; code in `src/coins/money.ts`.
|
|
376
488
|
- **Multicoin (item) pools:** scaling factor `1` → `quote = price * quantity` (confirmed
|
|
377
489
|
against the production app, which passes the unscaled price straight to
|
|
378
490
|
`place_limit_order`; TRIEX_SYSTEM_DESIGN §7's blanket "all prices ×1e9" describes coin
|
|
379
491
|
pools).
|
|
380
|
-
- **
|
|
381
|
-
`quote × (1e9
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
492
|
+
- **Fee model (cycle 7).** Every fee is quote-denominated and priced by
|
|
493
|
+
`quote_fee::fee_from_scaled_rate` = `floor(quote × min(rate, 1e9) / 1e9)` (`computeQuoteFee`).
|
|
494
|
+
Rates live in the shared `FeePolicy`: each pool carries a fee class whose ladder
|
|
495
|
+
(`fee_schedule::FeeSchedule`, ≤16 tiers, taker and maker rates non-increasing by tier;
|
|
496
|
+
multicoin launch ladder 2.2%/1.8% → 1.1%/0.9%) is resolved against the account's trailing
|
|
497
|
+
30-epoch **fee turnover** (taker fees paid + maker fees earned at fill, exchange-wide per
|
|
498
|
+
quote). Schedule changes are staged for the next epoch. Who pays what
|
|
499
|
+
(`order_info::calculate_partial_fill_balances`, `state::process_fills`, `fill.move`):
|
|
500
|
+
- bid taker: `floor(taker × matched quote)` once on the aggregate, on top of the quote;
|
|
501
|
+
- bid maker: `floor(maker × resting quote)` escrowed at placement; on cancel / modify-down /
|
|
502
|
+
expiry the class's `cancel_retention_bps` share of the released escrow is kept;
|
|
503
|
+
- ask taker: `floor(taker × matched quote)` out of the proceeds;
|
|
504
|
+
- ask maker: `floor(maker × fill quote)` per fill, out of the proceeds.
|
|
505
|
+
- **Bid deposit:** `computeBidQuoteDeposit(price, qty, rate)` = `notional + floor(notional ×
|
|
506
|
+
rate / 1e9)`. The facade passes `TradingFees.bidEscrowFeeRate` = max(tier-0 taker, tier-0
|
|
507
|
+
maker) over the active and staged ladders — tier 0 bounds every tier, and floors are
|
|
508
|
+
subadditive, so the deposit covers any match/rest split at any tier, even across an epoch
|
|
509
|
+
boundary. Surplus stays in the trading account. Asks deposit only items.
|
|
510
|
+
- **Quotes:** `estimateMarketBuyCost` (asks low-first, fee on the aggregate — exact for an
|
|
511
|
+
unchanged book, so the old per-fill rounding buffer is gone; `marketBuyRoundingBuffer` is
|
|
512
|
+
deprecated), `estimateMarketSellProceeds` (bids high-first, net of the taker fee),
|
|
513
|
+
`computeAskProceeds`. Use the account's own rate from `orders.fees()`. Pool metadata's
|
|
514
|
+
`fee` is only the entry-tier taker rate (no maker rate, no tiers). Market bids require an
|
|
515
|
+
explicit `quoteBudget`; a market order fills at most 100 makers (`MAX_FILLS`).
|
|
386
516
|
- **Decimals:** CRED and each item type carry `decimals` (pool metadata's
|
|
387
517
|
`base_asset_decimals` / `quote_asset_decimals`); the SDK exposes both `bigint` base-unit
|
|
388
518
|
and helper `toBase(human, decimals)` / `fromBase(base, decimals)`.
|
|
@@ -404,12 +534,13 @@ back into the SSU/hangar (needs `ssu`, `character`, `vaultConfig`, `collection`,
|
|
|
404
534
|
excluded**. The current stillness trading IDs (for the SDK `testnet` preset):
|
|
405
535
|
|
|
406
536
|
```
|
|
407
|
-
triexPackageId
|
|
408
|
-
triexRegistryId
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
537
|
+
triexPackageId 0xdbf259ed33d70666379492199137e2ad50fcc1ed1e56acd5780329f7fe982945 (cycle 7 + TRIEX-158 fresh publish; = original id)
|
|
538
|
+
triexRegistryId 0x2f37ad133427cabf94653be9a67560f87ea7458eaef2b410f6497808cef722c2 (shared obj — not in /package-ids)
|
|
539
|
+
triexFeePolicyId 0xcd63402799fe6b3a3ff2d963f90449e843f178659c349a08a1503db516163748 (shared obj — not in /package-ids)
|
|
540
|
+
multicoinPackageId 0xdbb778cba30e7deccf61169fbfbcd10a867654e1e2822facd789a99bd2c4e2ba
|
|
541
|
+
warehouseReceiptsPackageId 0x134dfa96ad8bc50d4a2055cd78c91e264feb2fe79facf2d030f8bb466a80bb68
|
|
542
|
+
credCoinType 0xfbcbd9155669e157ce3999e073930b4c4b67255c3cf88d0d80c76342a31e6710::cred::CRED (never republished)
|
|
543
|
+
worldPackageId 0x7be18d6294e533bedd9a5d70a96ce8d9d4b87a7c74188ba65d3fe966bbed9d92 (= worldOriginalPackageId)
|
|
413
544
|
clock 0x6
|
|
414
545
|
```
|
|
415
546
|
|
|
@@ -451,7 +582,8 @@ via `packageIds`. Optionally hydrate the *package* IDs at runtime from
|
|
|
451
582
|
keypair), semantic-release → publish `@trinaryex/sdk@0.x`.
|
|
452
583
|
- **Later (post-MVP):** full sweep-all convenience (multi-hub redeem), tribe/governance
|
|
453
584
|
trading, **gas-station sponsored (gasless) execution** (optional `sponsor` hook), live
|
|
454
|
-
orderbook streaming, mainnet
|
|
585
|
+
orderbook streaming, mainnet. Coin-pools (currency-pair markets) landed in cycle 7 —
|
|
586
|
+
DESIGN-COINS.md. (Cancel/modify and
|
|
455
587
|
claim-settled were pulled INTO the MVP — a trading bot is not viable without them.)
|
|
456
588
|
|
|
457
589
|
---
|
|
@@ -484,7 +616,9 @@ via `packageIds`. Optionally hydrate the *package* IDs at runtime from
|
|
|
484
616
|
(borrow_owner_cap → receipt::deposit_for_receipt → return_owner_cap → deposit_multicoin);
|
|
485
617
|
see §6.1.
|
|
486
618
|
- **OQ-5 — Ignore coin-pools.** MVP trades **item↔CRED** via `multicoin_pool` ("pools" family
|
|
487
|
-
only). Coin-pools deferred.
|
|
619
|
+
only). Coin-pools deferred. **Lifted (cycle 7):** coin pools merged into the main `triex`
|
|
620
|
+
package and are traded through `client.coins` — fullnode reads (the coin-pool indexer
|
|
621
|
+
routes are not on the gateway) plus `GET /v1/coins`; see DESIGN-COINS.md.
|
|
488
622
|
- **OQ-6 — Always v1.** SDK supports only the newest contracts; no version branching (§7).
|
|
489
623
|
- **OQ-7 — Gas:** left to the caller's `executor`; sponsored/gasless is a **post-MVP** feature
|
|
490
624
|
(optional `sponsor` hook).
|
|
@@ -558,40 +692,88 @@ object IDs. For latency-sensitive bots, note the post-MVP live-stream option.
|
|
|
558
692
|
|
|
559
693
|
---
|
|
560
694
|
|
|
561
|
-
## 13. Appendix —
|
|
695
|
+
## 13. Appendix — published gateway surface → SDK
|
|
562
696
|
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
697
|
+
The gateway's published OpenAPI document (`https://api.trinary.exchange/swagger.json`,
|
|
698
|
+
vendored at `test/fixtures/gateway-openapi.json`) — **60 operations, all `GET`**, as of
|
|
699
|
+
2026-10-01. Upstreams per `dynamic-config-registry/gateway-routes/`: etl-api (market
|
|
700
|
+
data), location-api (`/v1/spatial/*`, `/v1/routing/*`) and the public CDN
|
|
701
|
+
(`/v1/world/items`, `/v1/world/recipes*`). `npm run check:gateway [-- --live]` lists any
|
|
702
|
+
operation the SDK does not wrap.
|
|
568
703
|
|
|
569
704
|
```
|
|
570
|
-
|
|
571
|
-
GET /v1/
|
|
572
|
-
GET /v1/
|
|
573
|
-
GET /v1/
|
|
574
|
-
GET /v1/
|
|
575
|
-
GET /v1/
|
|
576
|
-
GET /v1/pools/
|
|
577
|
-
GET /v1/
|
|
578
|
-
GET /v1/
|
|
579
|
-
GET /v1/
|
|
580
|
-
GET /v1/
|
|
581
|
-
GET /v1/
|
|
705
|
+
# Market data (etl-api)
|
|
706
|
+
GET /v1/discovery 150 CU market.discover · iterateDiscovery
|
|
707
|
+
GET /v1/pools/resolve 30 CU market.resolvePool (internal: indexer.resolvePool)
|
|
708
|
+
GET /v1/pools/{pool_id}/orderbook 30 CU indexer.orderbook
|
|
709
|
+
GET /v1/pools/{pool_id}/metadata 20 CU market.poolMetadata
|
|
710
|
+
GET /v1/pools/{pool_id}/orders/{order_id} 30 CU orders.get · ro.order
|
|
711
|
+
GET /v1/pools/top-by-fees 50 CU market.topPoolsByFees
|
|
712
|
+
GET /v1/hubs/{hub_id}/items/{asset_id}/orderbook 50 CU market.orderbook
|
|
713
|
+
GET /v1/fills/{event_digest} 30 CU orders.fill · ro.fill
|
|
714
|
+
GET /v1/trades/recent 50 CU market.recentTrades · iterateRecentTrades
|
|
715
|
+
GET /v1/display-prices 50 CU market.displayPrices
|
|
716
|
+
GET /v1/display-prices/{item_id} 20 CU market.displayPrice
|
|
717
|
+
GET /v1/stats 50 CU market.stats
|
|
718
|
+
# Hubs & locations (etl-api)
|
|
719
|
+
GET /v1/hubs/{hub_id}/vault 20 CU market.hub (+ write flows)
|
|
720
|
+
GET /v1/hubs/{hub_id}/location 20 CU market.hub
|
|
721
|
+
GET /v1/hubs/{hub_id}/items 20 CU market.itemsAtHub
|
|
722
|
+
GET /v1/hubs/locations 50 CU market.hubLocations · iterateHubLocations
|
|
723
|
+
GET /v1/hubs/enriched 50 CU market.hubsEnriched
|
|
724
|
+
GET /v1/hubs/economics 50 CU market.hubEconomics
|
|
725
|
+
GET /v1/hubs/{hub_id}/nearby 50 CU market.nearbyHubs
|
|
726
|
+
GET /v1/hubs/nearby-by-system 50 CU market.nearbyHubsBySystem
|
|
727
|
+
GET /v1/items/{item_id}/locations 50 CU market.itemLocations · iterateItemLocations
|
|
728
|
+
GET /v1/collections/{collection_id}/hub 20 CU indexer.collectionHub
|
|
729
|
+
GET /v1/assemblies/owners 50 CU market.assemblyOwners
|
|
730
|
+
GET /v1/assemblies/enriched 50 CU market.assembliesEnriched
|
|
731
|
+
GET /v1/solar-systems/names 50 CU market.solarSystemNames
|
|
732
|
+
# Trading accounts (etl-api)
|
|
733
|
+
GET /v1/inventory/balances 50 CU balances.atHub
|
|
734
|
+
GET /v1/trading-accounts/{id}/open-orders 30 CU orders.openOrders · iterateOpenOrders
|
|
735
|
+
GET /v1/trading-accounts/{id}/fills 30 CU orders.fills · iterateFills
|
|
736
|
+
GET /v1/trading-accounts/{id}/trades 30 CU orders.trades · iterateTrades
|
|
737
|
+
GET /v1/trading-accounts/{id}/sweepable 30 CU account.sweepable / claimSettled
|
|
738
|
+
GET /v1/trading-accounts/owners 50 CU account.owners
|
|
739
|
+
# Characters, tribes & world (etl-api + public CDN)
|
|
740
|
+
GET /v1/characters/{object_id} 20 CU characters.get
|
|
741
|
+
GET /v1/characters/address/{address} 20 CU characters.byAddress
|
|
742
|
+
GET /v1/characters/name/{name} 20 CU characters.byName
|
|
743
|
+
GET /v1/characters/batch 50 CU characters.batch
|
|
744
|
+
GET /v1/world/tribes/{tribe_id} 20 CU characters.tribe
|
|
745
|
+
GET /v1/tribes/{tribe_id} 20 CU — deprecated 2026-08-20 alias of the above; not wrapped
|
|
746
|
+
GET /v1/world/items 20 CU world.items (CDN)
|
|
747
|
+
GET /v1/world/items/{asset_id} 20 CU world.item
|
|
748
|
+
GET /v1/world/items/search 50 CU market.searchItems
|
|
749
|
+
GET /v1/world/recipes 20 CU world.recipes (CDN)
|
|
750
|
+
GET /v1/world/recipes/{recipe_id} 20 CU world.recipesFor (CDN)
|
|
751
|
+
# Star map & routing (location-api)
|
|
752
|
+
GET /v1/spatial/systems/{solar_system} 20 CU spatial.system
|
|
753
|
+
GET /v1/spatial/systems 50 CU spatial.systems
|
|
754
|
+
GET /v1/spatial/systems/{solar_system}/nearby 50 CU spatial.nearbySystems
|
|
755
|
+
GET /v1/spatial/coordinates/nearby 50 CU spatial.systemsNearCoordinates
|
|
756
|
+
GET /v1/spatial/systems/search/autocomplete 20 CU spatial.autocompleteSystems
|
|
757
|
+
GET /v1/spatial/stats 20 CU spatial.stats
|
|
758
|
+
GET /v1/routing/route 100 CU routing.route
|
|
759
|
+
GET /v1/routing/compare 300 CU routing.compare
|
|
760
|
+
GET /v1/routing/stats 20 CU routing.stats
|
|
761
|
+
# Organizations (etl-api) — Armature module, DESIGN-ARMATURE.md §13.1
|
|
762
|
+
GET /v1/orgs · /v1/orgs/{org_id} · /v1/orgs/directory · /v1/orgs/{org_id}/proposals
|
|
763
|
+
GET /v1/players/{address}/orgs · /v1/players/{address}/accessible-keyspaces
|
|
764
|
+
GET /v1/hubs/{hub_id}/dao-vaults · /v1/search
|
|
765
|
+
# Coin (currency-pair) markets — coin-pool module
|
|
766
|
+
GET /v1/coins
|
|
582
767
|
```
|
|
583
768
|
|
|
584
|
-
(
|
|
585
|
-
pools/hubs/assets/activity families, `GET /v1/characters/*`, `GET /v1/orgs*`,
|
|
586
|
-
`GET /v1/search` (100 CU), `GET /v1/stats`, `GET /v1/trades/recent`.)
|
|
587
|
-
|
|
588
|
-
**Deliberately disabled (D11 — sluice attaches no caller identity):** the remaining
|
|
769
|
+
**Deliberately unpublished (D11 — sluice attaches no caller identity):** the remaining
|
|
589
770
|
`inventory` family, including the seven POST container mutations and two GETs the SDK
|
|
590
771
|
replaces with fullnode reads — `GET /v1/inventory/balance-manager` (story 1: on-chain
|
|
591
772
|
`listOwnedObjects` is authoritative) and `GET /v1/inventory/receipt-objects` (story 4:
|
|
592
|
-
wallet-receipt discovery is PTB input resolution). `coin-pools/*` stays disabled by choice
|
|
773
|
+
wallet-receipt discovery is PTB input resolution). `coin-pools/*` stays disabled by choice —
|
|
774
|
+
the `coins` group reads coin pools from the fullnode instead and uses only `GET /v1/coins`.
|
|
593
775
|
|
|
594
776
|
> Remember a merge to `main` does **not** update the running gateway — run
|
|
595
|
-
> `scripts/push-gateway.sh --env <env>`
|
|
596
|
-
>
|
|
597
|
-
>
|
|
777
|
+
> `scripts/push-gateway.sh --env <env>` in dynamic-config-registry. location-api's
|
|
778
|
+
> cycle-7 names change (nullable `solar_system_name`, `known_solar_system_names`) is
|
|
779
|
+
> deployed but not yet reflected in the published spec; the SDK accepts both shapes.
|