@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.
- package/LICENSE +201 -0
- package/README.md +114 -0
- package/dist/assertion.d.ts +867 -0
- package/dist/assertion.js +63 -0
- package/dist/assurance.d.ts +353 -0
- package/dist/assurance.js +551 -0
- package/dist/citizen-market.d.ts +468 -0
- package/dist/citizen-market.js +16 -0
- package/dist/citizenship.d.ts +215 -0
- package/dist/citizenship.js +26 -0
- package/dist/client.d.ts +288 -0
- package/dist/client.js +370 -0
- package/dist/commercial-payment.d.ts +243 -0
- package/dist/commercial-payment.js +16 -0
- package/dist/contract-schema.generated.d.ts +14 -0
- package/dist/contract-schema.generated.js +3107 -0
- package/dist/errors.d.ts +171 -0
- package/dist/errors.js +28 -0
- package/dist/index.d.ts +621 -0
- package/dist/index.js +624 -0
- package/dist/marketplace-receipt.d.ts +306 -0
- package/dist/marketplace-receipt.js +116 -0
- package/dist/marketplace-settlement.d.ts +298 -0
- package/dist/marketplace-settlement.js +16 -0
- package/dist/money.d.ts +84 -0
- package/dist/money.js +34 -0
- package/dist/organisation.d.ts +60 -0
- package/dist/organisation.js +16 -0
- package/dist/purchase.d.ts +531 -0
- package/dist/purchase.js +107 -0
- package/dist/restriction.d.ts +262 -0
- package/dist/restriction.js +316 -0
- package/dist/routes.d.ts +155 -0
- package/dist/routes.js +128 -0
- package/dist/schema-types.d.ts +101 -0
- package/dist/schema-types.js +16 -0
- package/dist/schema.d.ts +24 -0
- package/dist/schema.js +16 -0
- package/dist/wallet.d.ts +92 -0
- package/dist/wallet.js +59 -0
- package/package.json +41 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `citizenship` — provisioning a citizen, and the sign-up grant that arrives
|
|
3
|
+
* with it (CAY-0031).
|
|
4
|
+
*
|
|
5
|
+
* Provisioning is an EXPLICIT, IDEMPOTENT CALL. It is not a side effect of
|
|
6
|
+
* reading a wallet, and it is not an event on a bus. `cayso.wallet` used to
|
|
7
|
+
* create a wallet on the first authenticated read (CAY-0022); it no longer
|
|
8
|
+
* creates anything, because a citizen who never opens a wallet page would never
|
|
9
|
+
* have been credited, and two concurrent first reads had no defined outcome.
|
|
10
|
+
* CAY-0031 amends that record: the wallet and the sign-up grant are
|
|
11
|
+
* provisioned together, by one call, in one transaction.
|
|
12
|
+
*
|
|
13
|
+
* ## A consumer can ask, and asking provisions nothing
|
|
14
|
+
*
|
|
15
|
+
* {@link ROUTES.citizenship} is a READ. It answers {@link CitizenshipView} for
|
|
16
|
+
* the calling citizen and writes nothing whatever the answer is — so a consumer
|
|
17
|
+
* may render "not provisioned yet" without that render being the thing that
|
|
18
|
+
* provisions. {@link ROUTES.citizenshipProvision} is the write, and it is the
|
|
19
|
+
* only one.
|
|
20
|
+
*
|
|
21
|
+
* The two are separate PATHS, not one path with two methods, and that is
|
|
22
|
+
* deliberate: the scope requirement CAY-0030's gate enforces is registered
|
|
23
|
+
* per-path, so a `GET` and a `POST` sharing `/api/citizenship` would share one
|
|
24
|
+
* scope requirement and the read would silently inherit the write's — or, worse,
|
|
25
|
+
* the write would inherit the read's. One path, one operation, one scope.
|
|
26
|
+
*
|
|
27
|
+
* ## The state is a CLOSED VALUE, never prose
|
|
28
|
+
*
|
|
29
|
+
* {@link Citizenship} is a discriminated union on `state`, so a consumer switches
|
|
30
|
+
* on a literal rather than pattern-matching a sentence. There is no `pending`, no
|
|
31
|
+
* `provisioning` and no `partial`. The call commits the wallet and the grant
|
|
32
|
+
* together or it commits neither, so a state meaning "half of it happened" would
|
|
33
|
+
* describe something this surface cannot produce.
|
|
34
|
+
*
|
|
35
|
+
* ## The third state is not "in between" (CAY-0081)
|
|
36
|
+
*
|
|
37
|
+
* A founder wallet is created at genesis, holding a founder's allocation, and is
|
|
38
|
+
* bound to its Citizen by a founder claim. A claimed founder was never provisioned
|
|
39
|
+
* and must never be: no sign-up grant reaches them by any route. So they are
|
|
40
|
+
* neither of the first two states. Reporting them as `not-provisioned` would
|
|
41
|
+
* invite the one call that must not happen, and a background retry of it would
|
|
42
|
+
* never stop. {@link CitizenshipFounderClaimed} says this Citizen holds a wallet,
|
|
43
|
+
* was never provisioned, and is not waiting to be.
|
|
44
|
+
*
|
|
45
|
+
* {@link FounderClaimRefusal} is the founder claim's closed set of refusals,
|
|
46
|
+
* answered by {@link ROUTES.citizenshipFounderClaim}. It carries
|
|
47
|
+
* `founder_wallet_lapsed` before anything can produce it, so that a consumer
|
|
48
|
+
* renders every member before it can arrive.
|
|
49
|
+
*
|
|
50
|
+
* ## NO NUMBERS LIVE HERE, AND THAT IS THE POINT
|
|
51
|
+
*
|
|
52
|
+
* {@link SignUpGrantTerms} names the sign-up grant's amount and its
|
|
53
|
+
* restriction period as FIELDS A CONSUMER READS AT RUNTIME. Their source is the
|
|
54
|
+
* canon PRD — the deck's tiered schedule, recorded in the canon's own questions
|
|
55
|
+
* table as governing — and they reach this surface as configuration read in one
|
|
56
|
+
* place, exactly as the policy figures do.
|
|
57
|
+
*
|
|
58
|
+
* **Do not "fix" this by inlining a figure.** A literal here would be a second
|
|
59
|
+
* home for a number whose only home is the canon, and the two go stale in
|
|
60
|
+
* opposite directions; the canon's own record of the figure exists precisely
|
|
61
|
+
* because an earlier verbal one was superseded. `cents` is a field, not a value.
|
|
62
|
+
* `restrictionMonths` is a field, not a duration. The same rule `cayso.wallet`
|
|
63
|
+
* states for `publishedValuePence` (CLAUDE.md, "Where numbers live").
|
|
64
|
+
*
|
|
65
|
+
* ## What this file must not decide
|
|
66
|
+
*
|
|
67
|
+
* - **No operator path.** There is no field here by which somebody other than
|
|
68
|
+
* the citizen could be provisioned, because in v1 there is not one: the call
|
|
69
|
+
* is authorised by the citizen's own assertion and nothing else (CAY-0031).
|
|
70
|
+
* The absence is structural, so there is nothing to refuse at runtime.
|
|
71
|
+
* - **No sweep, no forfeiture, no vesting.** `expiresAt` is the promise that
|
|
72
|
+
* was recorded (CAY-0020). Recording it is not the same as acting on it, and
|
|
73
|
+
* nothing here acts on it.
|
|
74
|
+
* - **No carve, no holding, no movement shape.** `movementId` is a handle a
|
|
75
|
+
* consumer can quote back; the movement itself is not a consumer's business
|
|
76
|
+
* and is not on this surface. Naming a grant `'restricted'` on arrival is where
|
|
77
|
+
* it LANDS; what happens when it moves is CAY-0074's — a carve does not move
|
|
78
|
+
* restriction, and a restricted lot may not be transferred.
|
|
79
|
+
*/
|
|
80
|
+
import type { Cents, Millis, WalletRecord } from './wallet.js';
|
|
81
|
+
/**
|
|
82
|
+
* The wire code a read refuses with when the citizen it names has not been
|
|
83
|
+
* provisioned — a value, so a consumer can compare against the contract's own
|
|
84
|
+
* string rather than retyping ours.
|
|
85
|
+
*
|
|
86
|
+
* `cayso.wallet`'s read answers this, with no wallet and no balance, for a
|
|
87
|
+
* citizen who has never been provisioned. It is deliberately the same spelling
|
|
88
|
+
* Piper already uses for the same state: one word for one condition across the
|
|
89
|
+
* two pillars, rather than two that have to be mapped.
|
|
90
|
+
*/
|
|
91
|
+
export declare const NOT_PROVISIONED: "not_provisioned";
|
|
92
|
+
/**
|
|
93
|
+
* The terms a provisioning applies: how much the sign-up grant is, and how
|
|
94
|
+
* long it lands restricted for.
|
|
95
|
+
*
|
|
96
|
+
* Read at runtime, from the deployment that serves them. See the file docblock:
|
|
97
|
+
* these are the canon's figures reaching a consumer as configuration, and they
|
|
98
|
+
* are never restated as literals anywhere — not here, not in a comment, not in
|
|
99
|
+
* the schema, not in an ADR.
|
|
100
|
+
*/
|
|
101
|
+
export interface SignUpGrantTerms {
|
|
102
|
+
/** The sign-up grant, in cents. A field, not a value. */
|
|
103
|
+
readonly cents: Cents;
|
|
104
|
+
/** How many months the grant lands restricted for. A field, not a duration. */
|
|
105
|
+
readonly restrictionMonths: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The sign-up grant a provisioned citizen actually received — one movement,
|
|
109
|
+
* final and append-only (CAY-0023). There is at most one of these per wallet,
|
|
110
|
+
* ever.
|
|
111
|
+
*
|
|
112
|
+
* It lands RESTRICTED and carries its expiry, because granted Cayso lands
|
|
113
|
+
* restricted and purchased Cayso does not (CONTEXT.md). `restriction` is typed as
|
|
114
|
+
* the literal `'restricted'` rather than the two-member vocabulary: a sign-up
|
|
115
|
+
* grant that arrived unrestricted is not a thing this surface can describe,
|
|
116
|
+
* which is the type half of the invariant the schema also enforces.
|
|
117
|
+
*/
|
|
118
|
+
export interface SignUpGrant {
|
|
119
|
+
/** The movement that carried the cents. Final; there is no second one. */
|
|
120
|
+
readonly movementId: string;
|
|
121
|
+
/** What arrived, in cents. The figure of the day it was granted, not today's terms. */
|
|
122
|
+
readonly cents: Cents;
|
|
123
|
+
/** Granted Cayso lands restricted. The literal is the invariant. */
|
|
124
|
+
readonly restriction: 'restricted';
|
|
125
|
+
/** The promised date the restriction lifts. Recorded, never swept (CAY-0020). */
|
|
126
|
+
readonly expiresAt: Millis;
|
|
127
|
+
readonly grantedAt: Millis;
|
|
128
|
+
}
|
|
129
|
+
/** A citizen this deployment has never provisioned. No wallet, no grant. */
|
|
130
|
+
export interface CitizenshipNotProvisioned {
|
|
131
|
+
readonly state: 'not-provisioned';
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A provisioned citizen: the wallet, and the one sign-up grant that arrived
|
|
135
|
+
* with it. Both, always — they were committed together, so a shape carrying one
|
|
136
|
+
* without the other would describe a state the call cannot produce.
|
|
137
|
+
*/
|
|
138
|
+
export interface CitizenshipProvisioned {
|
|
139
|
+
readonly state: 'provisioned';
|
|
140
|
+
readonly wallet: WalletRecord;
|
|
141
|
+
readonly signUpGrant: SignUpGrant;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A Citizen bound to a founder wallet by a founder claim (CAY-0081). The wallet,
|
|
145
|
+
* and NO sign-up grant.
|
|
146
|
+
*
|
|
147
|
+
* The absence is the type half of "a claimed founder never receives a sign-up
|
|
148
|
+
* grant". There is no `signUpGrant` field to be null, empty or zero, so a
|
|
149
|
+
* consumer cannot render a grant that was never made. The founder allocation is
|
|
150
|
+
* not described here either. It is the wallet's balance, read like any other
|
|
151
|
+
* balance, and not a fact about the claim.
|
|
152
|
+
*
|
|
153
|
+
* This is the answer to the founder claim, and the answer the citizenship read
|
|
154
|
+
* and the provisioning call give for the same Citizen. The provisioning call
|
|
155
|
+
* mints nothing when it gives it.
|
|
156
|
+
*/
|
|
157
|
+
export interface CitizenshipFounderClaimed {
|
|
158
|
+
readonly state: 'founder-claimed';
|
|
159
|
+
readonly wallet: WalletRecord;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Not provisioned, provisioned, or bound by a founder claim. Switch on `state`.
|
|
163
|
+
* There are three answers and a consumer's switch must handle all three.
|
|
164
|
+
*/
|
|
165
|
+
export type Citizenship = CitizenshipNotProvisioned | CitizenshipProvisioned | CitizenshipFounderClaimed;
|
|
166
|
+
/**
|
|
167
|
+
* Why a founder claim is refused. A closed union, so a consumer renders a
|
|
168
|
+
* member instead of pattern-matching prose (CAY-0023), and every member is a
|
|
169
|
+
* wire code. The status each is answered with is recorded in CAY-0081 and
|
|
170
|
+
* served by the route. A new reason is a new member and a version bump.
|
|
171
|
+
*
|
|
172
|
+
* The gate's refusals of a credential are NOT repeated here. A bad credential, a
|
|
173
|
+
* missing scope, an unaccepted verification method and a claim credential with
|
|
174
|
+
* no founder claim reference are all refused before the claim is asked about,
|
|
175
|
+
* with the codes the gate already answers.
|
|
176
|
+
*/
|
|
177
|
+
export type FounderClaimRefusal =
|
|
178
|
+
/**
|
|
179
|
+
* This deployment has performed no founder genesis, so there is nothing to
|
|
180
|
+
* claim. Unconfigured, so closed.
|
|
181
|
+
*/
|
|
182
|
+
'founder_genesis_not_performed'
|
|
183
|
+
/** The founder claim reference names no founder wallet. */
|
|
184
|
+
| 'founder_claim_reference_unknown'
|
|
185
|
+
/** Another Citizen has already claimed this founder wallet. A claim is made once. */
|
|
186
|
+
| 'founder_wallet_already_claimed'
|
|
187
|
+
/**
|
|
188
|
+
* The founder wallet's allocation lapsed and was forfeited to unissued. An
|
|
189
|
+
* empty founder wallet is not an allocation, and claiming it would leave a
|
|
190
|
+
* founder with neither an allocation nor a sign-up grant.
|
|
191
|
+
*
|
|
192
|
+
* In the union from its first version although nothing produces it yet: the
|
|
193
|
+
* lapse is built last (CAY-0081, open decision 6). A consumer renders it now
|
|
194
|
+
* rather than meeting it later as a change to a closed union.
|
|
195
|
+
*/
|
|
196
|
+
| 'founder_wallet_lapsed'
|
|
197
|
+
/**
|
|
198
|
+
* The caller is already bound to another wallet, provisioned ordinarily or
|
|
199
|
+
* claimed through another founder wallet. A Citizen holds one Citizen Wallet.
|
|
200
|
+
*/
|
|
201
|
+
| 'citizen_already_holds_a_wallet';
|
|
202
|
+
/**
|
|
203
|
+
* The answer to both citizenship routes: the current terms, and whether this
|
|
204
|
+
* citizen has been provisioned.
|
|
205
|
+
*
|
|
206
|
+
* `terms` is present either way on purpose. A consumer showing a citizen what
|
|
207
|
+
* provisioning would give them needs the figures BEFORE provisioning, and a
|
|
208
|
+
* consumer showing what a past provisioning gave them reads
|
|
209
|
+
* {@link SignUpGrant.cents} — the two can differ, because terms are today's
|
|
210
|
+
* configuration and a grant is a historical fact.
|
|
211
|
+
*/
|
|
212
|
+
export interface CitizenshipView {
|
|
213
|
+
readonly terms: SignUpGrantTerms;
|
|
214
|
+
readonly citizenship: Citizenship;
|
|
215
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
/**
|
|
17
|
+
* The wire code a read refuses with when the citizen it names has not been
|
|
18
|
+
* provisioned — a value, so a consumer can compare against the contract's own
|
|
19
|
+
* string rather than retyping ours.
|
|
20
|
+
*
|
|
21
|
+
* `cayso.wallet`'s read answers this, with no wallet and no balance, for a
|
|
22
|
+
* citizen who has never been provisioned. It is deliberately the same spelling
|
|
23
|
+
* Piper already uses for the same state: one word for one condition across the
|
|
24
|
+
* two pillars, rather than two that have to be mapped.
|
|
25
|
+
*/
|
|
26
|
+
export const NOT_PROVISIONED = 'not_provisioned';
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@percayso-community/cayso-contracts/client` — the thin client (CAY-0026).
|
|
3
|
+
*
|
|
4
|
+
* This ships from a *separate* entry point so a consumer wanting nothing but the
|
|
5
|
+
* types (`@percayso-community/cayso-contracts`) takes on no runtime at all. The barrel reaches none
|
|
6
|
+
* of this file; a test on the built output is what enforces that, not this
|
|
7
|
+
* comment (CAY-0026: the client is on its own subpath for exactly this reason).
|
|
8
|
+
*
|
|
9
|
+
* The point of the client is to make the correct call the *easy* call: it owns
|
|
10
|
+
* the fetch, the `Authorization: Bearer <assertion>` header and the idempotent
|
|
11
|
+
* retry once, so a consumer does not own each of them badly. Piper's tree is the
|
|
12
|
+
* cautionary tale — 22 hand-rolled `lib/*Api.ts` modules, one of which redeclares
|
|
13
|
+
* the wallet shape and loses its guarantee.
|
|
14
|
+
*
|
|
15
|
+
* ## The assertion is fetched per request, never cached
|
|
16
|
+
*
|
|
17
|
+
* `getAssertion()` is called *inside* each network attempt, never once at
|
|
18
|
+
* construction and never into a variable that outlives the call. An assertion is
|
|
19
|
+
* short-lived (60–120 seconds; see `assertion.ts`), so a credential fetched once
|
|
20
|
+
* and reused is a bug that only surfaces under load — which is to say in
|
|
21
|
+
* production, on the money path (CAY-0021: the assertion is verified per request
|
|
22
|
+
* and never cached; the caller must mint it per request in kind). A *retried*
|
|
23
|
+
* request re-fetches for the same reason: a backoff can outlast the credential,
|
|
24
|
+
* so each attempt gets a fresh one. That the retry re-fetches is a decision, not
|
|
25
|
+
* an accident.
|
|
26
|
+
*
|
|
27
|
+
* ## One method per route, no method that is not
|
|
28
|
+
*
|
|
29
|
+
* Every method reads its method and path from `ROUTES` — no URL is hand-written
|
|
30
|
+
* here. `ROUTES` exists precisely so that no consumer hand-writes one, and a
|
|
31
|
+
* client that hand-wrote them would be the first consumer to break the rule.
|
|
32
|
+
*
|
|
33
|
+
* ## The retry rule, and only this rule
|
|
34
|
+
*
|
|
35
|
+
* Retry only a request that is safe to repeat — a `GET`, or a `POST` *carrying an
|
|
36
|
+
* idempotency key* (that key is the whole reason a purchase is safe to send
|
|
37
|
+
* twice; CAY-0023) — and only on 429, 502, 503, 504 and a network failure. Three
|
|
38
|
+
* attempts, exponential backoff, honouring `Retry-After`. A 4xx that is not 429
|
|
39
|
+
* is *never* retried: retry a declined card and it becomes a fraud signal; fail
|
|
40
|
+
* to retry a flaky network and somebody's purchase is lost.
|
|
41
|
+
*
|
|
42
|
+
* `Retry-After` is honoured in both forms the header takes: an integer count of
|
|
43
|
+
* seconds, and an HTTP-date. A value in neither form is ignored and the computed
|
|
44
|
+
* backoff is used instead.
|
|
45
|
+
*
|
|
46
|
+
* ## What this file is not
|
|
47
|
+
*
|
|
48
|
+
* Transport, not a cache. The client holds no wallet, no balance, no purchase, no
|
|
49
|
+
* assertion and no ETag; `createCaysoClient` hands back an object closing over the
|
|
50
|
+
* options and nothing else. It never converts, rounds, formats or divides a money
|
|
51
|
+
* amount — amounts pass through untouched (CLAUDE.md: integer cents, always). And
|
|
52
|
+
* it declares no dependency — `fetch` is global on Node 26, and if you find
|
|
53
|
+
* yourself wanting a retry library you have found the part of this slice that is
|
|
54
|
+
* the point.
|
|
55
|
+
*
|
|
56
|
+
* ## The one line it logs, and why it is safe
|
|
57
|
+
*
|
|
58
|
+
* The client is otherwise silent — a debug line printing an `Authorization` header
|
|
59
|
+
* is a credential in a log aggregator, and there is none here. It makes exactly one
|
|
60
|
+
* exception: {@link CaysoClient.getVersion} warns, ONCE per client instance, when
|
|
61
|
+
* the server reports a contract version whose MAJOR differs from the one this client
|
|
62
|
+
* was built against ({@link CONTRACT_VERSION}). A different major is a
|
|
63
|
+
* backwards-incompatible surface, and a consumer that never hears about it debugs
|
|
64
|
+
* shape mismatches blind — so this is the one thing worth a line. The line carries
|
|
65
|
+
* ONLY version strings (the server's and this client's); it never touches the
|
|
66
|
+
* assertion, a header, a wallet id or any identity. It is routed through an
|
|
67
|
+
* injectable `warn` (default `console.warn`) so a consumer can capture it and a test
|
|
68
|
+
* can prove both what it says and that it says it once — never per request.
|
|
69
|
+
*/
|
|
70
|
+
import type { BuyBackMade, BuyBackRequest, CitizenMarketViewed, QuotePlaced, QuoteRequest, QuoteWithdrawalRequest, QuoteWithdrawn } from './citizen-market.js';
|
|
71
|
+
import type { CitizenshipFounderClaimed, CitizenshipView } from './citizenship.js';
|
|
72
|
+
import type { CommercialPaymentMade, CommercialPaymentRequest } from './commercial-payment.js';
|
|
73
|
+
import type { OrganisationView } from './organisation.js';
|
|
74
|
+
import type { PurchaseAuthorised, PurchaseCancelled, PurchaseRequest, PurchaseResult, PurchaseTerminal } from './purchase.js';
|
|
75
|
+
import type { WalletView } from './wallet.js';
|
|
76
|
+
/**
|
|
77
|
+
* The version surface `GET /api/version` answers (slice 29). Four fields, each
|
|
78
|
+
* from ONE source on the server: the `contractVersion` this deployment publishes,
|
|
79
|
+
* the `supportedContractVersions` it will still speak (the negotiation set), the
|
|
80
|
+
* `schemaVersion` of its database (the migration runner's own number) and the
|
|
81
|
+
* `commit` it was built from — `'unknown'`, honestly, when the build did not stamp
|
|
82
|
+
* one. It carries no wallet, no balance and no economic number; a version endpoint
|
|
83
|
+
* reports versions.
|
|
84
|
+
*/
|
|
85
|
+
export interface CaysoVersion {
|
|
86
|
+
readonly contractVersion: string;
|
|
87
|
+
readonly supportedContractVersions: readonly string[];
|
|
88
|
+
readonly schemaVersion: number;
|
|
89
|
+
readonly commit: string;
|
|
90
|
+
}
|
|
91
|
+
export interface CaysoClientOptions {
|
|
92
|
+
readonly baseUrl: string;
|
|
93
|
+
/** Called before every request. Assertions are short-lived, so never cache one. */
|
|
94
|
+
getAssertion(): Promise<string>;
|
|
95
|
+
/** Injectable for tests. Defaults to global fetch. */
|
|
96
|
+
fetchImpl?: typeof fetch;
|
|
97
|
+
/**
|
|
98
|
+
* Where a contract-version-skew warning goes. Defaults to `console.warn`. This is
|
|
99
|
+
* the ONLY thing the client ever logs, it fires at most ONCE per client instance,
|
|
100
|
+
* and its message is version strings alone — never an assertion, a header, a
|
|
101
|
+
* wallet id or any identity (see the file docblock). Injected so a consumer can
|
|
102
|
+
* route it and a test can capture it.
|
|
103
|
+
*/
|
|
104
|
+
warn?: (message: string) => void;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* One method per entry in `ROUTES`, and no method that is not. `getMyWallet`
|
|
108
|
+
* answers `null` where the contract says a wallet may not exist yet, rather than
|
|
109
|
+
* throwing.
|
|
110
|
+
*/
|
|
111
|
+
export interface CaysoClient {
|
|
112
|
+
getMyWallet(): Promise<WalletView | null>;
|
|
113
|
+
/**
|
|
114
|
+
* Whether the caller has been provisioned, and on what terms. Reads only —
|
|
115
|
+
* calling this provisions nobody, which is the whole reason it is a separate
|
|
116
|
+
* route from {@link provisionCitizenship} (CAY-0031).
|
|
117
|
+
*/
|
|
118
|
+
getMyCitizenship(): Promise<CitizenshipView>;
|
|
119
|
+
/**
|
|
120
|
+
* Provision the caller: a wallet and its sign-up grant, committed together.
|
|
121
|
+
*
|
|
122
|
+
* IDEMPOTENT BY THE CALLER'S OWN IDENTITY, so it carries no idempotency key.
|
|
123
|
+
* The key a purchase needs exists because two purchases by one citizen are a
|
|
124
|
+
* legitimate thing to want and only the key tells them apart; two provisionings
|
|
125
|
+
* of one citizen are the same act by definition, and the schema makes the second
|
|
126
|
+
* one a no-op that answers with the first one's result. Calling it twice is
|
|
127
|
+
* safe, and so is retrying it.
|
|
128
|
+
*/
|
|
129
|
+
provisionCitizenship(): Promise<CitizenshipView>;
|
|
130
|
+
/**
|
|
131
|
+
* Claim the founder wallet the caller's assertion names, and answer the wallet
|
|
132
|
+
* record (CAY-0081).
|
|
133
|
+
*
|
|
134
|
+
* IT TAKES NO ARGUMENT, for {@link provisionOrganisation}'s reason. The founder
|
|
135
|
+
* wallet is named by the founder claim reference, which travels inside the signed
|
|
136
|
+
* assertion and nowhere else; a reference passed beside the credential would be
|
|
137
|
+
* the caller's claim about which allocation is theirs (CAY-0021).
|
|
138
|
+
*
|
|
139
|
+
* IDEMPOTENT BY THE CALLER'S OWN IDENTITY, like {@link provisionCitizenship}: the
|
|
140
|
+
* Citizen who already claimed the wallet gets the same answer and nothing is
|
|
141
|
+
* written. So it carries no idempotency key and a retry is safe. Each attempt
|
|
142
|
+
* fetches a fresh assertion, which is what a single-use `jti` needs.
|
|
143
|
+
*
|
|
144
|
+
* The answer is `founder-claimed` alone, with no `terms` beside it. A founder is
|
|
145
|
+
* never offered a sign-up grant, so the grant's terms are not part of the answer.
|
|
146
|
+
* A refusal arrives as a thrown {@link CaysoApiError} whose `code` is a
|
|
147
|
+
* `FounderClaimRefusal` member, or one of the gate's codes.
|
|
148
|
+
*/
|
|
149
|
+
claimFounderWallet(): Promise<CitizenshipFounderClaimed>;
|
|
150
|
+
/**
|
|
151
|
+
* Provision the ORGANISATION the caller's assertion attests authority for, and
|
|
152
|
+
* answer its wallet (CAY-0059).
|
|
153
|
+
*
|
|
154
|
+
* IT TAKES NO ARGUMENT, AND THAT IS THE DECISION RATHER THAN AN OVERSIGHT. The
|
|
155
|
+
* Organisation ID is read from the organisation-authority claim on the verified
|
|
156
|
+
* assertion and from nowhere else — so there is no value a caller could pass
|
|
157
|
+
* that would reach the holder key, and a signature here naming an organisation
|
|
158
|
+
* would be the wallet-creation oracle CAY-0057 describes.
|
|
159
|
+
*
|
|
160
|
+
* IDEMPOTENT on the attested Organisation ID, exactly as
|
|
161
|
+
* {@link provisionCitizenship} is idempotent on the caller's own identity, so it
|
|
162
|
+
* carries no idempotency key and a repeat is the same act rather than a second
|
|
163
|
+
* one. A credential carrying no organisation authority is refused; absence
|
|
164
|
+
* provisions nothing.
|
|
165
|
+
*/
|
|
166
|
+
provisionOrganisation(): Promise<OrganisationView>;
|
|
167
|
+
/**
|
|
168
|
+
* Authorise a purchase — or REPLAY one (CAY-0032). Posting the same
|
|
169
|
+
* `idempotencyKey` twice returns the ORIGINAL authorisation, at the original
|
|
170
|
+
* price, with a freshly minted client secret; the purchase is never minted twice
|
|
171
|
+
* and there is no second hold at the payment gateway. Resume IS this call, which
|
|
172
|
+
* is why there is no `resumePurchase` beside it.
|
|
173
|
+
*
|
|
174
|
+
* The answer is the contract's declared union, MINUS its refused branch: a
|
|
175
|
+
* refusal arrives as a non-2xx and this client turns every non-2xx into a thrown
|
|
176
|
+
* {@link CaysoApiError} whose `code` is that branch's `error` — one closed reason,
|
|
177
|
+
* reached through the mechanism a consumer already handles. So a caller switches
|
|
178
|
+
* on `outcome` over the two branches it can be handed, and catches for the third.
|
|
179
|
+
*
|
|
180
|
+
* `'terminal'` means the key's purchase has finished and will not change: a
|
|
181
|
+
* repeat of it will keep answering `'terminal'` forever, and the remedy is a new
|
|
182
|
+
* purchase under a NEW key.
|
|
183
|
+
*/
|
|
184
|
+
authorisePurchase(req: PurchaseRequest): Promise<PurchaseAuthorised | PurchaseTerminal>;
|
|
185
|
+
getPurchase(purchaseId: string): Promise<PurchaseResult>;
|
|
186
|
+
/**
|
|
187
|
+
* Cancel a purchase the caller started, releasing its reservation (CAY-0032).
|
|
188
|
+
*
|
|
189
|
+
* IDEMPOTENT: cancelling an already-cancelled purchase answers the same
|
|
190
|
+
* {@link PurchaseCancelled} and is not an error, so a retry after a dropped
|
|
191
|
+
* connection is safe and carries no key of its own — the purchase id IS the key.
|
|
192
|
+
* A purchase that has already reached a terminal status is REFUSED, and the
|
|
193
|
+
* refusal arrives as a thrown {@link CaysoApiError} whose `code` is the closed
|
|
194
|
+
* {@link import('./purchase.js').PurchaseCancelRefusalReason}; a succeeded purchase can never be
|
|
195
|
+
* cancelled, because undoing an issued and captured purchase is a refund and
|
|
196
|
+
* that is a different act.
|
|
197
|
+
*/
|
|
198
|
+
cancelPurchase(purchaseId: string): Promise<PurchaseCancelled>;
|
|
199
|
+
/**
|
|
200
|
+
* Pay a Citizen from an Organisation Wallet — or REPLAY a payment already made
|
|
201
|
+
* under the same key (CAY-0079).
|
|
202
|
+
*
|
|
203
|
+
* A replay with the same key, payee and cents answers the movement already
|
|
204
|
+
* written, in the same shape, so a retry after a dropped connection is safe and
|
|
205
|
+
* never moves cents twice. The same key with a different payee or a different
|
|
206
|
+
* number of cents is refused, never answered at either.
|
|
207
|
+
*
|
|
208
|
+
* The answer is {@link CommercialPaymentMade} alone. A refusal arrives as a
|
|
209
|
+
* thrown {@link CaysoApiError} whose `code` is a `CommercialPaymentRefusal`
|
|
210
|
+
* member, one of the gate's codes, or a seam code, as {@link authorisePurchase}'s
|
|
211
|
+
* refusals do. A refusal means nothing moved.
|
|
212
|
+
*/
|
|
213
|
+
makeCommercialPayment(req: CommercialPaymentRequest): Promise<CommercialPaymentMade>;
|
|
214
|
+
/**
|
|
215
|
+
* Read the citizen market as the caller sees it (CAY-0080 §1.4): the published
|
|
216
|
+
* value and its band, both bounds per quote unit, the best bid and the best ask with
|
|
217
|
+
* their sizes, and the caller's own quotes. No match price and no history. Reading
|
|
218
|
+
* writes nothing. A refusal arrives as a thrown {@link CaysoApiError} whose `code`
|
|
219
|
+
* is a `CitizenMarketViewRefusal` member or a seam code.
|
|
220
|
+
*/
|
|
221
|
+
getCitizenMarket(): Promise<CitizenMarketViewed>;
|
|
222
|
+
/**
|
|
223
|
+
* Place a quote — or REPLAY one already placed under the same key (CAY-0080 §1).
|
|
224
|
+
*
|
|
225
|
+
* A replay with the same key, side, price and size answers the quote already
|
|
226
|
+
* placed, in the same shape, so a retry after a dropped connection never places
|
|
227
|
+
* two. The answer says the quote was accepted and nothing about fills: what has
|
|
228
|
+
* matched is on {@link getCitizenMarket}, among the caller's own quotes. A refusal
|
|
229
|
+
* arrives as a thrown {@link CaysoApiError} whose `code` is a
|
|
230
|
+
* `QuotePlacementRefusal` member, one of the gate's codes, or a seam code, and
|
|
231
|
+
* means nothing was written.
|
|
232
|
+
*/
|
|
233
|
+
placeQuote(req: QuoteRequest): Promise<QuotePlaced>;
|
|
234
|
+
/**
|
|
235
|
+
* Withdraw one of the caller's resting quotes, after its minimum resting time.
|
|
236
|
+
*
|
|
237
|
+
* IDEMPOTENT: withdrawing a quote the caller already withdrew answers the same
|
|
238
|
+
* {@link QuoteWithdrawn}, so a retry carries no key of its own — the quote id IS
|
|
239
|
+
* the key. A refusal arrives as a thrown {@link CaysoApiError} whose `code` is a
|
|
240
|
+
* `QuoteWithdrawalRefusal` member, and the quote rests as it was.
|
|
241
|
+
*/
|
|
242
|
+
withdrawQuote(req: QuoteWithdrawalRequest): Promise<QuoteWithdrawn>;
|
|
243
|
+
/**
|
|
244
|
+
* Ask the Treasury to buy back unrestricted cents at the floor reference, in whole
|
|
245
|
+
* quote units — or REPLAY a buy-back already made under the same key (CAY-0080 §2.2).
|
|
246
|
+
*
|
|
247
|
+
* A replay with the same key and size answers the buy-back already made, in the same
|
|
248
|
+
* shape, so a retry after a dropped connection never sells twice. The same key with
|
|
249
|
+
* a different size is refused. A refusal arrives as a thrown {@link CaysoApiError}
|
|
250
|
+
* whose `code` is a `BuyBackRefusal` member, one of the gate's codes, or a seam code,
|
|
251
|
+
* and means nothing moved and nothing is paid.
|
|
252
|
+
*/
|
|
253
|
+
requestBuyBack(req: BuyBackRequest): Promise<BuyBackMade>;
|
|
254
|
+
getVersion(): Promise<CaysoVersion>;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Raised for any non-2xx response and any network failure. `code` is parsed from
|
|
258
|
+
* the body's `{ error, message }`, falling back to `'request_failed'` when the
|
|
259
|
+
* body is absent, empty, unparseable or the wrong shape — the client never throws
|
|
260
|
+
* a `SyntaxError` out at a consumer. Exported as a value, extending `Error` with
|
|
261
|
+
* a fixed prototype and name, so `instanceof CaysoApiError` holds for a consumer.
|
|
262
|
+
*/
|
|
263
|
+
export declare class CaysoApiError extends Error {
|
|
264
|
+
readonly code: string;
|
|
265
|
+
readonly status: number;
|
|
266
|
+
readonly detail?: string;
|
|
267
|
+
constructor(code: string, status: number, detail?: string);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* The injectable timing seam, kept deliberately *off* `CaysoClientOptions` — the
|
|
271
|
+
* public options are `baseUrl`, `getAssertion` and `fetchImpl` and nothing more.
|
|
272
|
+
* The suite drives the backoff schedule through this without sleeping for real; a
|
|
273
|
+
* test suite that slept for real is a test suite people stop running.
|
|
274
|
+
*/
|
|
275
|
+
interface CaysoClientInternals {
|
|
276
|
+
sleep(ms: number): Promise<void>;
|
|
277
|
+
}
|
|
278
|
+
export declare function createCaysoClient(options: CaysoClientOptions): CaysoClient;
|
|
279
|
+
/**
|
|
280
|
+
* The construction seam the test suite uses to inject `sleep`. Not part of the
|
|
281
|
+
* supported client surface — a consumer calls {@link createCaysoClient}. It lives
|
|
282
|
+
* here rather than on the public options type so the timing stays untestable-by-
|
|
283
|
+
* accident for a consumer and fully testable for the suite.
|
|
284
|
+
*
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
export declare function createCaysoClientWithInternals(options: CaysoClientOptions, internals: CaysoClientInternals): CaysoClient;
|
|
288
|
+
export {};
|