@percayso-community/cayso-contracts 0.23.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.
Files changed (41) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +114 -0
  3. package/dist/assertion.d.ts +867 -0
  4. package/dist/assertion.js +63 -0
  5. package/dist/assurance.d.ts +353 -0
  6. package/dist/assurance.js +551 -0
  7. package/dist/citizen-market.d.ts +468 -0
  8. package/dist/citizen-market.js +16 -0
  9. package/dist/citizenship.d.ts +215 -0
  10. package/dist/citizenship.js +26 -0
  11. package/dist/client.d.ts +288 -0
  12. package/dist/client.js +370 -0
  13. package/dist/commercial-payment.d.ts +243 -0
  14. package/dist/commercial-payment.js +16 -0
  15. package/dist/contract-schema.generated.d.ts +14 -0
  16. package/dist/contract-schema.generated.js +3107 -0
  17. package/dist/errors.d.ts +171 -0
  18. package/dist/errors.js +28 -0
  19. package/dist/index.d.ts +621 -0
  20. package/dist/index.js +624 -0
  21. package/dist/marketplace-receipt.d.ts +306 -0
  22. package/dist/marketplace-receipt.js +116 -0
  23. package/dist/marketplace-settlement.d.ts +298 -0
  24. package/dist/marketplace-settlement.js +16 -0
  25. package/dist/money.d.ts +84 -0
  26. package/dist/money.js +34 -0
  27. package/dist/organisation.d.ts +60 -0
  28. package/dist/organisation.js +16 -0
  29. package/dist/purchase.d.ts +531 -0
  30. package/dist/purchase.js +107 -0
  31. package/dist/restriction.d.ts +262 -0
  32. package/dist/restriction.js +316 -0
  33. package/dist/routes.d.ts +155 -0
  34. package/dist/routes.js +128 -0
  35. package/dist/schema-types.d.ts +101 -0
  36. package/dist/schema-types.js +16 -0
  37. package/dist/schema.d.ts +24 -0
  38. package/dist/schema.js +16 -0
  39. package/dist/wallet.d.ts +92 -0
  40. package/dist/wallet.js +59 -0
  41. package/package.json +41 -0
@@ -0,0 +1,468 @@
1
+ /**
2
+ * `citizen-market` — the requests, the answers and the closed refusal unions for
3
+ * the three acts a Citizen performs against the citizen market (CAY-0080):
4
+ * placing a quote, withdrawing one, and asking the Treasury to buy back.
5
+ *
6
+ * This file is the Piper-facing half of CAY-0080's slices 3 and 4, and slice 8
7
+ * (#289) serves the book: placing a quote at `ROUTES.placeQuote`, withdrawing one at
8
+ * `ROUTES.withdrawQuote`, and reading the book at `ROUTES.citizenMarket`. Slice 10
9
+ * (#291) serves the buy-back at `ROUTES.requestBuyBack`. The market stays shut until
10
+ * every figure it needs is set in the canon: a deployment missing one answers `503`
11
+ * `citizen_market_not_configured`, and a buy-back missing one of its own answers
12
+ * `503 buy_back_not_configured`. Neither is a member of these unions.
13
+ *
14
+ * ## The holder is the verified subject, so no request names a wallet
15
+ *
16
+ * A quote acts for one Citizen Wallet (CAY-0080 §1.1), and that wallet is the
17
+ * verified subject's own, resolved inside `apps/api` from the holders mapping
18
+ * (§7). The caller does not say which wallet it is. A request that named one would
19
+ * be a claim about who the caller is, which Cayso never trusts (CAY-0021), and a
20
+ * field an Organisation Wallet's or the Treasury's id could be written into. With
21
+ * no such field, a request cannot express a quote for somebody else's wallet or
22
+ * for an organisation's. The service records the resolved wallet id on the quote,
23
+ * and the book never shows it.
24
+ *
25
+ * No request and no answer here carries a Percayso identity, a binding, or the
26
+ * wallet id of the other side of a match.
27
+ *
28
+ * ## Integer pence per quote unit, whole quote units
29
+ *
30
+ * A price is an integer number of pence per quote unit, in whole price ticks, and
31
+ * a size is a whole number of quote units (CAY-0080 §1.2, Open decision C). Every
32
+ * fill's cents and sterling are exact by construction, and nothing on the path
33
+ * divides. The quote unit and the price tick are canon figures and are never
34
+ * restated here. A deployment that has not been given one refuses the request as
35
+ * an unconfigured seam, never as a member of these unions, and never reads the
36
+ * absence as "no limit".
37
+ *
38
+ * ## Refusal at entry is §1.6's set, and it is fixed
39
+ *
40
+ * {@link QuoteEntryRefusal} is exactly the set CAY-0080 §1.6 fixed when the record
41
+ * was accepted, spelled as the record proposed. A refused quote writes nothing.
42
+ *
43
+ * Placing a quote also carries an idempotency key, because a bid placed twice by a
44
+ * retried request can fill twice. Reusing a key for a different quote is refused
45
+ * with `idempotency_key_reused_for_a_different_quote`. That refusal is about the
46
+ * request, not about the quote on its merits, so it sits beside §1.6's set in
47
+ * {@link QuotePlacementRefusal} rather than inside it, and the record's set stays
48
+ * the record's.
49
+ *
50
+ * ## What a placed quote does not say
51
+ *
52
+ * A quote is matched on arrival, and whatever does not fill rests (§1.3). Each
53
+ * fill is a match of its own, with its own key and its own record, which moves
54
+ * through statuses of its own as it settles cents before sterling (§3.2). None of
55
+ * that is on {@link QuotePlaced}: the match record is settlement's (slice 9), and
56
+ * how the sterling leg reaches the seller is Open decision M, which is Simon
57
+ * James's and not yet answered. The answer to a placement says the quote was
58
+ * accepted into the book, under which id and until when, and nothing about fills.
59
+ *
60
+ * ## Eligibility, and B7's exit window
61
+ *
62
+ * All three operations are money-grade in `ASSURANCE_POLICY` (Open decision Q).
63
+ * Withdrawal is gated at the same bar as placement, so a lower-assurance credential
64
+ * cannot interfere with a holder's quotes. A holder who no longer meets the profile
65
+ * has their quotes withdrawn by the market rather than left stranded.
66
+ *
67
+ * During B7's exit window an excluded holder places no new quote, and the market
68
+ * withdraws their resting quotes: Simon named transfer and the buy-back as the exit
69
+ * routes, and entering the market is not an exit. The buy-back stays open to them.
70
+ * No exclusion signal reaches Cayso yet, so nothing here names an excluded holder.
71
+ * A refusal nothing can raise would be a dormant code path, and the case joins in
72
+ * the slice that delivers the signal, as `payee_excluded` does for the commercial
73
+ * payment (CAY-0079, Open decision F).
74
+ *
75
+ * ## What this file must not decide
76
+ *
77
+ * - **No figures.** Quote unit, price tick, floor, rounding, lifetime, resting
78
+ * time and every limit live in the canon PRD. None is restated here.
79
+ * - **No match shape.** See above.
80
+ * - **No rationing.** A buy-back that eligible sterling cannot meet is refused
81
+ * whole, with `eligible_sterling_insufficient` (§2.3). Whether to ration instead
82
+ * — pay part, queue, or pay some holders first — is Open decision I, Simon
83
+ * James's, and nothing that rations is here. Refusing whole is the answer that
84
+ * pays nobody more than the Treasury can meet while he decides.
85
+ * - **No per-holder buy-back limit.** Figure 11 goes to Simon with I and M. Until
86
+ * he answers it is unset, and the buy-back is shut by configuration rather than
87
+ * refused by a reason here, so no limit reason is named.
88
+ * - **No Treasury quote.** The Treasury rests no quote in the book (Open decision
89
+ * G). Its buying side is {@link BuyBackRequest}, and its selling side is the
90
+ * purchase.
91
+ * - **No free text.** A quote has a side, a limit price and a size. A field in
92
+ * which two people could arrange a price elsewhere, or send a message, would
93
+ * bring the book inside the Online Safety Act gate (CAY-0080, Consequences).
94
+ */
95
+ import type { Cents, Millis } from './wallet.js';
96
+ /** A **bid** is a quote to buy, and an **ask** a quote to sell. */
97
+ export type QuoteSide = 'bid' | 'ask';
98
+ /**
99
+ * A Citizen placing a limit quote in the citizen market (CAY-0080 §1.1). There is
100
+ * no quote that fills at whatever the book offers, no stop quote, no hidden quote
101
+ * and no quote on behalf of somebody else, so there is no field for any of them.
102
+ */
103
+ export interface QuoteRequest {
104
+ readonly side: QuoteSide;
105
+ /**
106
+ * Integer pence per quote unit, a positive whole number of price ticks, no lower
107
+ * than the floor reference and no higher than the published value now.
108
+ */
109
+ readonly limitPricePence: number;
110
+ /** A positive whole number of quote units. */
111
+ readonly sizeQuoteUnits: number;
112
+ /** Caller-generated, unique per holder. The same key never places two quotes; a repeat REPLAYS. */
113
+ readonly idempotencyKey: string;
114
+ }
115
+ /**
116
+ * A quote the book accepted. It rests until it is matched, withdrawn by its holder
117
+ * after the minimum resting time, withdrawn by the market, or expires.
118
+ *
119
+ * It carries no wallet id. The holder knows their own wallet, and the book shows
120
+ * price and size only.
121
+ */
122
+ export interface Quote {
123
+ /** A handle to withdraw it by. */
124
+ readonly quoteId: string;
125
+ readonly side: QuoteSide;
126
+ readonly limitPricePence: number;
127
+ readonly sizeQuoteUnits: number;
128
+ readonly placedAt: Millis;
129
+ /** When the quote lifetime ends, if it is still resting then. */
130
+ readonly expiresAt: Millis;
131
+ }
132
+ /**
133
+ * The quote was accepted into the book. Answered for the request that placed it and
134
+ * for every replay of its key, in the same shape and with the same `quoteId`.
135
+ */
136
+ export interface QuotePlaced {
137
+ readonly outcome: 'placed';
138
+ readonly quote: Quote;
139
+ }
140
+ /** The quote was refused, with one reason. Nothing was written and nothing was reserved. */
141
+ export interface QuoteRefused {
142
+ readonly outcome: 'refused';
143
+ readonly error: QuotePlacementRefusal;
144
+ }
145
+ /** The ONE declared answer to placing a quote, discriminated by `outcome`. */
146
+ export type QuoteResponse = QuotePlaced | QuoteRefused;
147
+ /**
148
+ * Why a quote was refused at entry: CAY-0080 §1.6's set, closed, spelled here.
149
+ *
150
+ * The record fixed the SET when it was accepted, and this contract fixes the
151
+ * spelling. A new reason is a new member and a contract version, never a
152
+ * free-text field.
153
+ */
154
+ export type QuoteEntryRefusal =
155
+ /**
156
+ * The verification method is outside the money-grade profile
157
+ * (`ASSURANCE_POLICY['place-quote']`). `dev-bypass` is never inside it.
158
+ */
159
+ 'assurance_not_money_grade'
160
+ /** The wallet the verified subject resolves to is an Organisation Wallet or the Treasury (§6). */
161
+ | 'wallet_not_a_citizens'
162
+ /** No wallet is bound to the verified subject now, for example after erasure. */
163
+ | 'wallet_has_no_live_binding'
164
+ /** The size is zero, negative or not a whole number of quote units. */
165
+ | 'size_not_whole_quote_units'
166
+ /** The price is not a positive whole number of price ticks. */
167
+ | 'price_not_whole_ticks'
168
+ /** The limit sits below the floor reference now (Open decision E). */
169
+ | 'price_below_floor_reference'
170
+ /** The limit sits above the published value now (Open decision D). */
171
+ | 'price_above_published_value'
172
+ /**
173
+ * An ask: the wallet's unrestricted cents that are neither frozen nor already
174
+ * reserved do not cover it. Restricted cents are never counted (§4.1).
175
+ */
176
+ | 'unrestricted_cents_insufficient'
177
+ /**
178
+ * The holder has a resting quote on the other side that this one would match.
179
+ * "The holder" is decided by the binding, not by wallet id (§5.3).
180
+ */
181
+ | 'quote_would_cross_own_quote'
182
+ /** The quote passes the per-quote size limit. */
183
+ | 'quote_size_above_limit'
184
+ /** The holder already has the permitted number of resting quotes. */
185
+ | 'resting_quotes_at_limit'
186
+ /** The holder has entered the permitted number of quotes in the period. */
187
+ | 'quote_rate_limit_reached'
188
+ /** The quote would take the holder past the permitted volume in the period. */
189
+ | 'holder_volume_limit_reached'
190
+ /** The published value cannot be read, so neither bound can be checked. */
191
+ | 'published_value_unavailable';
192
+ /**
193
+ * Everything a placement can be refused with: §1.6's set, and the key discipline
194
+ * every value-moving request on this surface keeps.
195
+ */
196
+ export type QuotePlacementRefusal = QuoteEntryRefusal
197
+ /** The key was already used by this holder for a different side, price or size. */
198
+ | 'idempotency_key_reused_for_a_different_quote'
199
+ /**
200
+ * The quote met the book and the sterling leg could not be reached to authorise the
201
+ * match (CAY-0080 §3.2). Nothing was written — not the quote, not a match — so the
202
+ * placement may be retried unchanged under the same key.
203
+ */
204
+ | 'sterling_leg_unavailable';
205
+ /** A holder withdrawing one of their own resting quotes. */
206
+ export interface QuoteWithdrawalRequest {
207
+ readonly quoteId: string;
208
+ }
209
+ /**
210
+ * A withdrawal. What had not filled leaves the book, and the reservation an ask held
211
+ * on it is released, exactly once. Fills already matched are unaffected.
212
+ */
213
+ export interface QuoteWithdrawal {
214
+ readonly quoteId: string;
215
+ /** The part of the size that had not filled and left the book with the withdrawal. */
216
+ readonly withdrawnQuoteUnits: number;
217
+ readonly withdrawnAt: Millis;
218
+ }
219
+ /**
220
+ * The quote was withdrawn. Answered for the request that withdrew it and for every
221
+ * repeat of that request, in the same shape: withdrawing twice is one withdrawal.
222
+ */
223
+ export interface QuoteWithdrawn {
224
+ readonly outcome: 'withdrawn';
225
+ readonly withdrawal: QuoteWithdrawal;
226
+ }
227
+ /** The withdrawal was refused, with one reason. The quote rests as it was. */
228
+ export interface QuoteWithdrawalRefused {
229
+ readonly outcome: 'refused';
230
+ readonly error: QuoteWithdrawalRefusal;
231
+ }
232
+ /** The ONE declared answer to withdrawing a quote, discriminated by `outcome`. */
233
+ export type QuoteWithdrawalResponse = QuoteWithdrawn | QuoteWithdrawalRefused;
234
+ /**
235
+ * Why a withdrawal was refused. Closed. Refused withdrawals are counted rather than
236
+ * hidden, so a holder can see why a withdrawal waited (CAY-0080 §5.2).
237
+ */
238
+ export type QuoteWithdrawalRefusal =
239
+ /** The method is outside `ASSURANCE_POLICY['withdraw-quote']`, the same bar as placing one (Q). */
240
+ 'assurance_not_money_grade'
241
+ /** No wallet is bound to the verified subject now. */
242
+ | 'wallet_has_no_live_binding'
243
+ /**
244
+ * No quote has this id under the caller's wallet. Somebody else's quote answers
245
+ * the same, so the answer is never a way to learn whose a quote is.
246
+ */
247
+ | 'quote_not_found'
248
+ /** The quote already left the book: filled in full, expired, or withdrawn by the market. */
249
+ | 'quote_not_resting'
250
+ /** The quote has not yet rested for the minimum resting time (§5.2). */
251
+ | 'quote_minimum_resting_time_not_reached';
252
+ /**
253
+ * A Citizen asking the Treasury's standing buy-back to buy unrestricted cents, at
254
+ * the floor reference, in whole quote units (CAY-0080 §2.2). It is not matched and
255
+ * it is not in the book. The cents bought are cancelled (Open decision H).
256
+ */
257
+ export interface BuyBackRequest {
258
+ /** A positive whole number of quote units. */
259
+ readonly sizeQuoteUnits: number;
260
+ /** Caller-generated, unique per holder. The same key never buys back twice; a repeat REPLAYS. */
261
+ readonly idempotencyKey: string;
262
+ }
263
+ /**
264
+ * A buy-back the Treasury made. The cents have left the holder's wallet and been
265
+ * cancelled: no holding arrived anywhere, and they re-enter circulation only by a
266
+ * purchase at the published value.
267
+ *
268
+ * It is priced at the floor reference and published value recorded at authorisation,
269
+ * so a quickening in flight does not change what the holder receives. The payout
270
+ * stays behind the fake gateway until #119 and the FCA outcome, and its own status
271
+ * is not on this shape (CAY-0080 §3.4).
272
+ */
273
+ export interface BuyBack {
274
+ readonly buyBackId: string;
275
+ readonly sizeQuoteUnits: number;
276
+ /** The cents that left the wallet and were cancelled. */
277
+ readonly cents: Cents;
278
+ /** The published value at authorisation, in pence per ₡1. */
279
+ readonly publishedValuePence: number;
280
+ /** The floor reference at authorisation, in pence per quote unit. */
281
+ readonly floorReferencePence: number;
282
+ /** What the Treasury pays for it: the floor reference times the size. */
283
+ readonly sterlingPence: number;
284
+ readonly boughtBackAt: Millis;
285
+ }
286
+ /**
287
+ * The buy-back was made. Answered for the request that made it and for every replay
288
+ * of its key, in the same shape.
289
+ */
290
+ export interface BuyBackMade {
291
+ readonly outcome: 'bought-back';
292
+ readonly buyBack: BuyBack;
293
+ }
294
+ /** The buy-back was refused whole, with one reason. Nothing moved and nothing is paid. */
295
+ export interface BuyBackRefused {
296
+ readonly outcome: 'refused';
297
+ readonly error: BuyBackRefusal;
298
+ }
299
+ /** The ONE declared answer to a buy-back, discriminated by `outcome`. */
300
+ export type BuyBackResponse = BuyBackMade | BuyBackRefused;
301
+ /**
302
+ * Why a buy-back was refused. Closed, and refused before anything is paid (§2.2).
303
+ *
304
+ * A shortfall of eligible sterling is refused whole, never rationed (§2.3); whether
305
+ * to ration is Open decision I, Simon James's. There is no member for a per-holder
306
+ * limit (figure 11), which is his too and joins when he answers. See the file's
307
+ * docblock.
308
+ */
309
+ export type BuyBackRefusal =
310
+ /** The method is outside `ASSURANCE_POLICY['request-buy-back']`. */
311
+ 'assurance_not_money_grade'
312
+ /**
313
+ * The wallet the verified subject resolves to is an Organisation Wallet or the
314
+ * Treasury. An organisation is paid only by commercial payment (§6).
315
+ */
316
+ | 'wallet_not_a_citizens'
317
+ /** No wallet is bound to the verified subject now. */
318
+ | 'wallet_has_no_live_binding'
319
+ /** The size is zero, negative or not a whole number of quote units. */
320
+ | 'size_not_whole_quote_units'
321
+ /**
322
+ * The wallet's unrestricted cents that are neither frozen nor reserved do not
323
+ * cover it. Restricted cents are never counted: cash-out through the Treasury is
324
+ * refused for every restricted kind.
325
+ */
326
+ | 'unrestricted_cents_insufficient'
327
+ /** The published value cannot be read, so the floor reference cannot be priced. */
328
+ | 'published_value_unavailable'
329
+ /**
330
+ * The Treasury's eligible sterling — never its bank balance — cannot meet the
331
+ * sterling this buy-back would pay, after what earlier buy-backs have reserved
332
+ * (§2.3). Refused whole: nothing is partly paid, queued or rationed, and the
333
+ * Treasury operator is alarmed. A later request may be met once eligible sterling
334
+ * is. Pending Simon James's answer to Open decision I.
335
+ */
336
+ | 'eligible_sterling_insufficient'
337
+ /** The key was already used by this holder for a different size. */
338
+ | 'idempotency_key_reused_for_a_different_buy_back'
339
+ /**
340
+ * Too many concurrent draws on the wallet, and the retries ran out. Nothing moved,
341
+ * and the caller may try again under the same key.
342
+ */
343
+ | 'carve_contended';
344
+ /**
345
+ * One side of the book at its best price: the price, and the size resting at it
346
+ * across every quote at that price. It names no quote, no wallet and no holder, and
347
+ * it says nothing about how many quotes make up the size (CAY-0080 §1.4).
348
+ */
349
+ export interface BookLevel {
350
+ /** Integer pence per quote unit. */
351
+ readonly limitPricePence: number;
352
+ /** Whole quote units resting at this price, net of what has already matched. */
353
+ readonly sizeQuoteUnits: number;
354
+ }
355
+ /** Where a quote stands. A quote leaves the book once, and does not come back. */
356
+ export type QuoteStatus = 'resting' | 'filled' | 'withdrawn' | 'expired';
357
+ /**
358
+ * Why a quote was withdrawn. A closed union, so a consumer renders it rather than
359
+ * reading prose, and a new reason is a new member and a contract version.
360
+ *
361
+ * - `holder_withdrew` — the holder withdrew it, after the minimum resting time.
362
+ * - `below_floor_reference` — a quickening raised the floor reference above its
363
+ * limit, and the market withdrew it before the first match under the new band
364
+ * (CAY-0080 §1.5). This is how the holder is told.
365
+ * - `no_live_binding` — its wallet was bound to nobody when the book reached it,
366
+ * for example after erasure (§7).
367
+ * - `authorisation_refused` — a BID only: it met an ask, and the holder's sterling
368
+ * could not be authorised for the match (CAY-0080 §3.2, step 2). No cents moved
369
+ * and the ask rests as it was; the bid leaves the book rather than meeting the
370
+ * next ask with sterling that was just refused.
371
+ *
372
+ * There is no member for B7's exit window: nothing in this version can raise it, and
373
+ * a reason nothing can raise would be a dormant code path.
374
+ */
375
+ export type QuoteWithdrawalReason = 'holder_withdrew' | 'below_floor_reference' | 'no_live_binding' | 'authorisation_refused';
376
+ /**
377
+ * One of the caller's own quotes that was not withdrawn: resting, filled, or
378
+ * expired. It carries no reason, because only a withdrawal has one.
379
+ */
380
+ export interface OwnQuoteNotWithdrawn {
381
+ readonly quoteId: string;
382
+ readonly side: QuoteSide;
383
+ readonly limitPricePence: number;
384
+ readonly sizeQuoteUnits: number;
385
+ readonly placedAt: Millis;
386
+ readonly expiresAt: Millis;
387
+ /** The part of the size that has matched. What is left is the size less this. */
388
+ readonly filledQuoteUnits: number;
389
+ /** The earliest moment the holder may withdraw it: the minimum resting time after placing. */
390
+ readonly withdrawableFrom: Millis;
391
+ readonly status: 'resting' | 'filled' | 'expired';
392
+ }
393
+ /** One of the caller's own quotes that was withdrawn, and why. */
394
+ export interface OwnQuoteWithdrawn {
395
+ readonly quoteId: string;
396
+ readonly side: QuoteSide;
397
+ readonly limitPricePence: number;
398
+ readonly sizeQuoteUnits: number;
399
+ readonly placedAt: Millis;
400
+ readonly expiresAt: Millis;
401
+ readonly filledQuoteUnits: number;
402
+ readonly withdrawableFrom: Millis;
403
+ readonly status: 'withdrawn';
404
+ readonly withdrawalReason: QuoteWithdrawalReason;
405
+ }
406
+ /**
407
+ * One of the caller's own quotes, as the book stands. Discriminated by `status`,
408
+ * and only a withdrawn quote carries a reason, so a reason cannot be attached to a
409
+ * quote that was not withdrawn. The schema holds the same pair (migration 0050).
410
+ */
411
+ export type OwnQuote = OwnQuoteNotWithdrawn | OwnQuoteWithdrawn;
412
+ /**
413
+ * What each side sees (CAY-0080 §1.4, Open decision F): the published value with its
414
+ * band, the floor reference, and the best bid and the best ask with the size resting
415
+ * at each. The caller's own quotes are beside them, which is how a holder is told
416
+ * that the market withdrew one.
417
+ *
418
+ * It carries no match price, no feed, no history, no chart, no change since
419
+ * anything, and nothing about the next quickening. Each of those invites reading
420
+ * Cayso as something held to see what it does, and the last is a forecast.
421
+ *
422
+ * The caller's own quotes are those still resting and those that left the book no
423
+ * longer ago than a quote may rest. That is the holder's own record, not the book's
424
+ * history, and it names nobody else.
425
+ */
426
+ export interface CitizenMarketView {
427
+ /** What ₡1 is worth in sterling now, in pence. Never a price. */
428
+ readonly publishedValuePence: number;
429
+ /** The band the published value is set by. */
430
+ readonly band: number;
431
+ /**
432
+ * The upper bound: the published value converted to pence per quote unit,
433
+ * rounded down so the band narrows.
434
+ */
435
+ readonly publishedValuePerQuoteUnitPence: number;
436
+ /**
437
+ * The lower bound: what the Treasury's standing buy-back pays per quote unit now,
438
+ * rounded up so the band narrows.
439
+ */
440
+ readonly floorReferencePence: number;
441
+ /** The whole number of cents one quote unit is, as this deployment is configured. */
442
+ readonly quoteUnitCents: Cents;
443
+ /** The whole number of pence a price moves by, as this deployment is configured. */
444
+ readonly priceTickPence: number;
445
+ /** The best resting bid, or null when none rests. */
446
+ readonly bestBid: BookLevel | null;
447
+ /** The best resting ask, or null when none rests. */
448
+ readonly bestAsk: BookLevel | null;
449
+ readonly ownQuotes: readonly OwnQuote[];
450
+ }
451
+ /** The book, as it stood when it was read. */
452
+ export interface CitizenMarketViewed {
453
+ readonly outcome: 'viewed';
454
+ readonly market: CitizenMarketView;
455
+ }
456
+ /**
457
+ * Why the book was not shown. Closed, and each member means what it means at entry:
458
+ * the book is read by a Citizen with a live binding, because the view carries their
459
+ * own quotes, and it is priced from the published value, which has to be readable.
460
+ */
461
+ export type CitizenMarketViewRefusal = 'wallet_not_a_citizens' | 'wallet_has_no_live_binding' | 'published_value_unavailable';
462
+ /** The book was not shown, with one reason. Nothing was written. */
463
+ export interface CitizenMarketViewRefused {
464
+ readonly outcome: 'refused';
465
+ readonly error: CitizenMarketViewRefusal;
466
+ }
467
+ /** The ONE declared answer to reading the book, discriminated by `outcome`. */
468
+ export type CitizenMarketResponse = CitizenMarketViewed | CitizenMarketViewRefused;
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Copyright 2026 Percayso Community
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export {};