@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,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `restriction` — what a restriction actually PERMITS, per kind of holding
|
|
3
|
+
* (CAY-0035).
|
|
4
|
+
*
|
|
5
|
+
* `wallet.ts` reports a balance split restricted and unrestricted. It says what
|
|
6
|
+
* the split IS and has never said what it MEANS, and until this file two
|
|
7
|
+
* definitions of "restricted" were in print across the programme describing
|
|
8
|
+
* materially different products:
|
|
9
|
+
*
|
|
10
|
+
* - restricted from **cash-out**, with the cents otherwise usable; and
|
|
11
|
+
* - **not yet spendable or giftable** at all.
|
|
12
|
+
*
|
|
13
|
+
* The difference is whether a citizen who joined today can pay for Community
|
|
14
|
+
* services with their reward, or must look at an unusable balance until it
|
|
15
|
+
* expires. **The first is right and the second is rejected** — this repository's
|
|
16
|
+
* own glossary already said so (`CONTEXT.md`, `Restricted / unrestricted`:
|
|
17
|
+
* "Restricted Cayso can still be spent — it is cash-out that is restricted"), and
|
|
18
|
+
* the canon's accounting rests on it: the backing ceiling binds on UNRESTRICTED
|
|
19
|
+
* Cayso only and a restriction expiry is a funding event, which is only coherent
|
|
20
|
+
* if restricted Cayso circulates meanwhile (CAY-0013). `@cayso/ledger`'s
|
|
21
|
+
* `HoldingFrozen` has said the same thing beside the code since it was written: a
|
|
22
|
+
* freeze is a refusal to move at all, and restriction is not that.
|
|
23
|
+
*
|
|
24
|
+
* ## The table, and why every cell is written out
|
|
25
|
+
*
|
|
26
|
+
* {@link CAPABILITY_POLICY} is four capabilities against four kinds of holding —
|
|
27
|
+
* sixteen cells, each `'permitted'`, `'refused'` or `'open'`, each carrying the
|
|
28
|
+
* reason it holds. Nothing is left to be inferred from a lot's expiry date: an
|
|
29
|
+
* inferred cell is a cell nobody decided, which is the state this file exists to
|
|
30
|
+
* end.
|
|
31
|
+
*
|
|
32
|
+
* It is a mapped type over both axes, so adding a kind or a capability is a
|
|
33
|
+
* COMPILE ERROR at every cell it does not yet have an answer for — the same
|
|
34
|
+
* mechanism `assurance.ts` uses to stop a new verification method silently
|
|
35
|
+
* widening what an operation accepts.
|
|
36
|
+
*
|
|
37
|
+
* ## No cell is open — and the question that held two open is answered
|
|
38
|
+
*
|
|
39
|
+
* Whether a carve moves the source's restriction with it was the one question
|
|
40
|
+
* this programme held open on purpose, and it held both transfer cells for a
|
|
41
|
+
* restricted lot open. **The pillar owner answered it on 23 September 2026
|
|
42
|
+
* (CAY-0074): a carve does not move restriction.** What arrives is decided by
|
|
43
|
+
* what the arrival is — *earned versus granted* (CAY-0001) — so a provider paid
|
|
44
|
+
* from a restricted lot earns those cents unrestricted.
|
|
45
|
+
*
|
|
46
|
+
* That answer makes a plain TRANSFER of a restricted lot the act that would
|
|
47
|
+
* launder the restriction off, through a second wallet, and convert
|
|
48
|
+
* no-liability cents into backed cents on nobody's schedule. So the same decision
|
|
49
|
+
* **refuses** it, for a promotional reward and a founder allocation alike.
|
|
50
|
+
* Restricted cents still pay for Community services from the day they land.
|
|
51
|
+
*
|
|
52
|
+
* `'open'` stays a member of {@link CapabilityStanding}, and
|
|
53
|
+
* {@link OPEN_CAPABILITIES} stays on the surface, now empty. An open cell is still
|
|
54
|
+
* a first-class answer the next undecided question can take, and removing either
|
|
55
|
+
* would be a breaking change to the published contract for no gain.
|
|
56
|
+
*
|
|
57
|
+
* There is therefore **no boolean helper on this surface**. A function returning
|
|
58
|
+
* `boolean` over a standing would have to fold `'open'` into `true` or `false`,
|
|
59
|
+
* and either fold would decide the next open question by accident — which is
|
|
60
|
+
* precisely what publishing a cell as open is meant to prevent. A consumer switches on `standing`
|
|
61
|
+
* exhaustively and renders any open cell as open.
|
|
62
|
+
*
|
|
63
|
+
* ## What this file is NOT
|
|
64
|
+
*
|
|
65
|
+
* **It is not enforcement.** Nothing here carves, transfers or cashes out, and
|
|
66
|
+
* nothing here is consulted by anything that does. The policy has to be published
|
|
67
|
+
* BEFORE code decides what a restricted holding may do, which is the whole
|
|
68
|
+
* sequencing argument of the record; the later slice that writes the checks reads
|
|
69
|
+
* this table rather than inventing a second copy of it. Holdings carry no `kind`
|
|
70
|
+
* column today, so nothing in this tree could join the table to a lot yet, and no
|
|
71
|
+
* migration belongs in a policy record.
|
|
72
|
+
*
|
|
73
|
+
* **It is not a sweep, a vest or a forfeiture.** A restriction expiry is a
|
|
74
|
+
* condition evaluated on read (CAY-0019), recorded twice with the promise
|
|
75
|
+
* governing (CAY-0020). There is no scheduled work here and time-based economic
|
|
76
|
+
* outcomes want block height rather than a local clock.
|
|
77
|
+
*
|
|
78
|
+
* **It carries no numbers.** No grant size, no restriction period, no founder
|
|
79
|
+
* share, no vesting length. Every amount, rate, tier, schedule and threshold
|
|
80
|
+
* lives in the canon PRD and reaches code as configuration (`CLAUDE.md`). This is
|
|
81
|
+
* a statement about CAPABILITIES, and it is written without a single figure.
|
|
82
|
+
*
|
|
83
|
+
* **It is data and types, and nothing runs on import** (`CLAUDE.md`: packages
|
|
84
|
+
* have no side effects).
|
|
85
|
+
*
|
|
86
|
+
* ## Why the policy is not in the generated contract schema
|
|
87
|
+
*
|
|
88
|
+
* `contract-schema.generated.ts` describes the DATA CROSSING THE BOUNDARY — the
|
|
89
|
+
* wire shapes a consumer decodes off a response. This table is not one: it is a
|
|
90
|
+
* static policy a consumer imports at compile time and renders beside the
|
|
91
|
+
* restricted/unrestricted split it already receives. Nothing on the wire changed,
|
|
92
|
+
* so the surface files the generator walks are unchanged — the same call
|
|
93
|
+
* `assurance.ts` documents for the acceptance policy.
|
|
94
|
+
*/
|
|
95
|
+
/** The restriction states a holding reads in. Matches the glossary exactly. */
|
|
96
|
+
export type Restriction = 'restricted' | 'unrestricted';
|
|
97
|
+
/**
|
|
98
|
+
* Where a holding's cents came from. The four kinds differ in what they may do
|
|
99
|
+
* and in whether they are restricted at all, so they are four kinds rather than
|
|
100
|
+
* one kind with a flag.
|
|
101
|
+
*
|
|
102
|
+
* `'promotional-reward'` is the sign-up grant and its siblings — granted Cayso a
|
|
103
|
+
* citizen did not pay for and did not earn. `'earned'` is a payment for
|
|
104
|
+
* verifiable work: a storage payout, a bounty, a settled invoice. The split
|
|
105
|
+
* between those two is the standing principle that decides restriction status
|
|
106
|
+
* (CAY-0001, *earned versus granted*).
|
|
107
|
+
*/
|
|
108
|
+
export type HoldingKind = 'purchased' | 'promotional-reward' | 'earned' | 'founder-allocation';
|
|
109
|
+
/**
|
|
110
|
+
* What a holder may want to do with a holding's cents.
|
|
111
|
+
*
|
|
112
|
+
* `'sell-at-a-quoted-price'` is named for what it does rather than for the venue
|
|
113
|
+
* it would happen at: the venue's ordinary name is banned standing alone of Cayso
|
|
114
|
+
* for regulatory reasons (`scripts/banned-words.md`), and naming a capability
|
|
115
|
+
* after a trading venue is exactly the association the prohibition exists to
|
|
116
|
+
* avoid. It is a **quoted sale** in this repository's vocabulary (`CONTEXT.md`),
|
|
117
|
+
* and it is a different act from `'cash-out-through-the-treasury'` — one finds a
|
|
118
|
+
* counterparty at a quoted price, the other exercises the standing buyback
|
|
119
|
+
* against eligible sterling.
|
|
120
|
+
*/
|
|
121
|
+
export type HoldingCapability = 'spend-on-community-services' | 'transfer-to-another-holder' | 'sell-at-a-quoted-price' | 'cash-out-through-the-treasury';
|
|
122
|
+
/** The capability holds. */
|
|
123
|
+
export interface PermittedStanding {
|
|
124
|
+
readonly standing: 'permitted';
|
|
125
|
+
readonly because: string;
|
|
126
|
+
}
|
|
127
|
+
/** The capability does not hold, and the refusal is decided rather than pending. */
|
|
128
|
+
export interface RefusedStanding {
|
|
129
|
+
readonly standing: 'refused';
|
|
130
|
+
readonly because: string;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Nobody has decided, and this surface will not decide by omission.
|
|
134
|
+
*
|
|
135
|
+
* An open standing is a first-class answer, not a missing one. It names the two
|
|
136
|
+
* canon rules that disagree and what turns on settling them, so a reader can see
|
|
137
|
+
* that the gap is known rather than overlooked — and so that a consumer rendering
|
|
138
|
+
* the table shows a citizen an honest "not decided" instead of a confident lie in
|
|
139
|
+
* either direction.
|
|
140
|
+
*/
|
|
141
|
+
export interface OpenStanding {
|
|
142
|
+
readonly standing: 'open';
|
|
143
|
+
readonly because: string;
|
|
144
|
+
/** The two canon rules that point opposite ways. Both, always — one rule is not a conflict. */
|
|
145
|
+
readonly conflictingRules: readonly [string, string];
|
|
146
|
+
/** What settling it changes, so the cost of leaving it open is visible. */
|
|
147
|
+
readonly whatTurnsOnIt: string;
|
|
148
|
+
}
|
|
149
|
+
/** One cell's answer. Switch on `standing`; there is no fourth member and no ordering. */
|
|
150
|
+
export type CapabilityStanding = PermittedStanding | RefusedStanding | OpenStanding;
|
|
151
|
+
/** One cell of the table, carrying its own coordinates so a rendered row is self-describing. */
|
|
152
|
+
export interface CapabilityCell {
|
|
153
|
+
readonly kind: HoldingKind;
|
|
154
|
+
readonly capability: HoldingCapability;
|
|
155
|
+
/**
|
|
156
|
+
* What a lot of this kind may do **in the restriction state it lands in** —
|
|
157
|
+
* which is {@link RESTRICTION_ON_ARRIVAL}`[kind].onArrival`. What it may do once
|
|
158
|
+
* it reads unrestricted is {@link ONCE_UNRESTRICTED}, stated once because it is
|
|
159
|
+
* the same for every kind.
|
|
160
|
+
*/
|
|
161
|
+
readonly standing: CapabilityStanding;
|
|
162
|
+
}
|
|
163
|
+
/** A cell's address, for naming one without carrying the whole of it. */
|
|
164
|
+
export interface CapabilityCoordinate {
|
|
165
|
+
readonly kind: HoldingKind;
|
|
166
|
+
readonly capability: HoldingCapability;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* A cell for EVERY (kind, capability) pair. The mapped type is the mechanism
|
|
170
|
+
* behind "a kind or a capability added later cannot leave a hole": add a member to
|
|
171
|
+
* either union and this table stops compiling until somebody has written a
|
|
172
|
+
* standing and a reason for each new cell.
|
|
173
|
+
*/
|
|
174
|
+
export type CapabilityPolicy = {
|
|
175
|
+
readonly [K in HoldingKind]: {
|
|
176
|
+
readonly [C in HoldingCapability]: CapabilityCell;
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* **The permissions table.** What a holding of each kind may do, in the
|
|
181
|
+
* restriction state that kind lands in.
|
|
182
|
+
*
|
|
183
|
+
* Read it with {@link RESTRICTION_ON_ARRIVAL}, which says which kinds land
|
|
184
|
+
* restricted, and with {@link ONCE_UNRESTRICTED}, which says what a lot may do
|
|
185
|
+
* after its restriction lapses. Each `because` is written for the kind it sits
|
|
186
|
+
* under rather than shared, because two cells that agree today can stop agreeing
|
|
187
|
+
* for reasons that were never the same.
|
|
188
|
+
*/
|
|
189
|
+
export declare const CAPABILITY_POLICY: CapabilityPolicy;
|
|
190
|
+
/**
|
|
191
|
+
* Whether a lot of one kind lands restricted, and whether it ever stops being so.
|
|
192
|
+
*
|
|
193
|
+
* `'never-restricted'` is not the absence of an expiry date to be inferred from —
|
|
194
|
+
* it is the statement that this kind has no restricted phase at all, so there is
|
|
195
|
+
* nothing for an expiry to lift.
|
|
196
|
+
*/
|
|
197
|
+
export interface ArrivalAndExpiry {
|
|
198
|
+
readonly kind: HoldingKind;
|
|
199
|
+
/** The restriction a lot of this kind carries when it lands. */
|
|
200
|
+
readonly onArrival: Restriction;
|
|
201
|
+
/**
|
|
202
|
+
* `'never-restricted'` — lands unrestricted; there is no restriction to expire,
|
|
203
|
+
* now or later. `'expires'` — lands restricted and carries a promised date; the
|
|
204
|
+
* period is the canon's and reaches code as configuration, never a literal here.
|
|
205
|
+
*/
|
|
206
|
+
readonly expiry: 'never-restricted' | 'expires';
|
|
207
|
+
readonly because: string;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* What each kind arrives as, and what expiry means for it.
|
|
211
|
+
*
|
|
212
|
+
* **Expiry is not uniform across the four kinds, and the answer has three parts.**
|
|
213
|
+
* Two kinds have no expiry at all, because they never land restricted. The two
|
|
214
|
+
* that do run on their own clocks — a reward's and a founder allocation's are
|
|
215
|
+
* different periods, held in the canon. And the DESTINATION is uniform: whichever
|
|
216
|
+
* clock ran, a lapsed lot reads unrestricted and stands exactly at
|
|
217
|
+
* {@link ONCE_UNRESTRICTED}, which is why that table is stated once rather than
|
|
218
|
+
* four times.
|
|
219
|
+
*
|
|
220
|
+
* Expiry is a condition evaluated on READ (CAY-0019), recorded twice with the
|
|
221
|
+
* promise governing (CAY-0020). Nothing sweeps, nothing vests on a job, and no
|
|
222
|
+
* local clock decides it.
|
|
223
|
+
*/
|
|
224
|
+
export declare const RESTRICTION_ON_ARRIVAL: {
|
|
225
|
+
readonly [K in HoldingKind]: ArrivalAndExpiry;
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* What ANY lot may do once it reads unrestricted, whatever it arrived as.
|
|
229
|
+
*
|
|
230
|
+
* This is the answer to "what happens to a restricted lot's capabilities when the
|
|
231
|
+
* restriction expires", and it is stated once because it does not vary by kind: a
|
|
232
|
+
* lapsed reward and a vested founder allocation stand exactly where a purchase and
|
|
233
|
+
* an earned payment stood all along. The origin of the cents decided how long they
|
|
234
|
+
* were restricted; it does not go on deciding anything afterwards.
|
|
235
|
+
*
|
|
236
|
+
* The transfer refusal lifts here, and that is worth saying plainly: it refuses
|
|
237
|
+
* moving a RESTRICTED lot, because a carve does not move restriction and the
|
|
238
|
+
* transfer would launder it off (CAY-0074). Once there is no restriction, there is
|
|
239
|
+
* nothing to launder, and transferring is permitted.
|
|
240
|
+
*/
|
|
241
|
+
export declare const ONCE_UNRESTRICTED: {
|
|
242
|
+
readonly [C in HoldingCapability]: CapabilityStanding;
|
|
243
|
+
};
|
|
244
|
+
/**
|
|
245
|
+
* Every holding kind, as a value. Derived from {@link CAPABILITY_POLICY} rather
|
|
246
|
+
* than hand-listed: the table is a mapped type over the union, so its keys ARE the
|
|
247
|
+
* union and this list cannot drift from it.
|
|
248
|
+
*/
|
|
249
|
+
export declare const HOLDING_KINDS: readonly HoldingKind[];
|
|
250
|
+
/** Every capability, derived from the table's own rows for the same reason. */
|
|
251
|
+
export declare const HOLDING_CAPABILITIES: readonly HoldingCapability[];
|
|
252
|
+
/**
|
|
253
|
+
* Every cell nobody has decided, as coordinates. EMPTY since CAY-0074 answered the
|
|
254
|
+
* question that held the two transfer cells open; kept so the next undecided cell
|
|
255
|
+
* has somewhere to be listed.
|
|
256
|
+
*
|
|
257
|
+
* Derived from the table, so it cannot list a cell the table has settled or miss
|
|
258
|
+
* one it has not. It exists so that "which cells are open" is a question with a
|
|
259
|
+
* runtime answer: a consumer rendering the table, or a later enforcement slice
|
|
260
|
+
* auditing its own coverage, reads this rather than remembering.
|
|
261
|
+
*/
|
|
262
|
+
export declare const OPEN_CAPABILITIES: readonly CapabilityCoordinate[];
|
|
@@ -0,0 +1,316 @@
|
|
|
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
|
+
// --- The table ---------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* **The permissions table.** What a holding of each kind may do, in the
|
|
19
|
+
* restriction state that kind lands in.
|
|
20
|
+
*
|
|
21
|
+
* Read it with {@link RESTRICTION_ON_ARRIVAL}, which says which kinds land
|
|
22
|
+
* restricted, and with {@link ONCE_UNRESTRICTED}, which says what a lot may do
|
|
23
|
+
* after its restriction lapses. Each `because` is written for the kind it sits
|
|
24
|
+
* under rather than shared, because two cells that agree today can stop agreeing
|
|
25
|
+
* for reasons that were never the same.
|
|
26
|
+
*/
|
|
27
|
+
export const CAPABILITY_POLICY = {
|
|
28
|
+
// Bought with sterling, which arrived with the cents. Nothing is held back from
|
|
29
|
+
// somebody who paid the published value for it.
|
|
30
|
+
purchased: {
|
|
31
|
+
'spend-on-community-services': {
|
|
32
|
+
kind: 'purchased',
|
|
33
|
+
capability: 'spend-on-community-services',
|
|
34
|
+
standing: {
|
|
35
|
+
standing: 'permitted',
|
|
36
|
+
because: 'Purchased cents land unrestricted and are the holder’s own service credit. Buying ' +
|
|
37
|
+
'Cayso in order to pay for Community services is what the purchase path exists for.',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
'transfer-to-another-holder': {
|
|
41
|
+
kind: 'purchased',
|
|
42
|
+
capability: 'transfer-to-another-holder',
|
|
43
|
+
standing: {
|
|
44
|
+
standing: 'permitted',
|
|
45
|
+
because: 'An unrestricted lot carries no restriction, so the refusal that applies to a restricted ' +
|
|
46
|
+
'lot does not arise (CAY-0074). Who may be on the other end is a separate question about ' +
|
|
47
|
+
'counterparties, not about what a restriction permits.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
'sell-at-a-quoted-price': {
|
|
51
|
+
kind: 'purchased',
|
|
52
|
+
capability: 'sell-at-a-quoted-price',
|
|
53
|
+
standing: {
|
|
54
|
+
standing: 'permitted',
|
|
55
|
+
because: 'Unrestricted cents already carry a cash-out right, so selling them on at a quoted price ' +
|
|
56
|
+
'realises nothing the holder could not realise through the Treasury. The venue’s own ' +
|
|
57
|
+
'admission rules are a separate matter from this table.',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
'cash-out-through-the-treasury': {
|
|
61
|
+
kind: 'purchased',
|
|
62
|
+
capability: 'cash-out-through-the-treasury',
|
|
63
|
+
standing: {
|
|
64
|
+
standing: 'permitted',
|
|
65
|
+
because: 'Purchases land unrestricted and bring their own sterling with them, so these are exactly ' +
|
|
66
|
+
'the cents the backing ceiling is measured against and the cents eligible sterling is ' +
|
|
67
|
+
'held for (CAY-0013).',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
// Granted Cayso a citizen did not pay for and did not earn. It lands RESTRICTED
|
|
72
|
+
// and carries a promised expiry, and it consumes no backing while it does.
|
|
73
|
+
'promotional-reward': {
|
|
74
|
+
'spend-on-community-services': {
|
|
75
|
+
kind: 'promotional-reward',
|
|
76
|
+
capability: 'spend-on-community-services',
|
|
77
|
+
standing: {
|
|
78
|
+
standing: 'permitted',
|
|
79
|
+
because: 'This is the reconciliation, stated where it bites hardest: restricted means restricted ' +
|
|
80
|
+
'from CASH-OUT, and a reward is spendable on Community services from the day it lands. A ' +
|
|
81
|
+
'citizen who joined today can pay for storage and services with it. The rejected reading ' +
|
|
82
|
+
'would hand a new citizen a balance they could only look at, and would break the ' +
|
|
83
|
+
'accounting the ceiling rests on — no-liability cents that cannot circulate are not ' +
|
|
84
|
+
'a service credit at all.',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
'transfer-to-another-holder': {
|
|
88
|
+
kind: 'promotional-reward',
|
|
89
|
+
capability: 'transfer-to-another-holder',
|
|
90
|
+
standing: {
|
|
91
|
+
standing: 'refused',
|
|
92
|
+
because: 'A carve does not move restriction (CAY-0074): what arrives is decided by what the ' +
|
|
93
|
+
'arrival is, earned or granted. A plain transfer of a restricted reward would therefore ' +
|
|
94
|
+
'land unrestricted, laundering the restriction off through a second wallet and turning ' +
|
|
95
|
+
'no-liability cents into backed cents on nobody’s schedule. So it is refused. The reward ' +
|
|
96
|
+
'still pays for Community services, and the provider paid with it earns those cents ' +
|
|
97
|
+
'unrestricted — that is the earning the rule exists for.',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
'sell-at-a-quoted-price': {
|
|
101
|
+
kind: 'promotional-reward',
|
|
102
|
+
capability: 'sell-at-a-quoted-price',
|
|
103
|
+
standing: {
|
|
104
|
+
standing: 'refused',
|
|
105
|
+
because: 'Selling restricted cents at a quoted price is a cash-out by another route: the holder ' +
|
|
106
|
+
'walks away with sterling for cents that carry no backing and no liability. That is the ' +
|
|
107
|
+
'exact substance the restriction exists to prevent, so it is refused for as long as the ' +
|
|
108
|
+
'lot reads restricted — whichever way the transfer cell above is eventually settled.',
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
'cash-out-through-the-treasury': {
|
|
112
|
+
kind: 'promotional-reward',
|
|
113
|
+
capability: 'cash-out-through-the-treasury',
|
|
114
|
+
standing: {
|
|
115
|
+
standing: 'refused',
|
|
116
|
+
because: 'This is what "restricted" means. A granted lot consumes no backing precisely because it ' +
|
|
117
|
+
'cannot be cashed out yet, and the ceiling is measured against unrestricted cents only ' +
|
|
118
|
+
'(CAY-0013). Permitting it would let a holder draw eligible sterling that was never held ' +
|
|
119
|
+
'for these cents.',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
// Paid for verifiable work — a storage payout, a bounty, a settled invoice.
|
|
124
|
+
// Earned income lands UNRESTRICTED, which is the half of CAY-0001 that survived
|
|
125
|
+
// its own supersession.
|
|
126
|
+
earned: {
|
|
127
|
+
'spend-on-community-services': {
|
|
128
|
+
kind: 'earned',
|
|
129
|
+
capability: 'spend-on-community-services',
|
|
130
|
+
standing: {
|
|
131
|
+
standing: 'permitted',
|
|
132
|
+
because: 'Earned cents land unrestricted (CAY-0001). A lock on earned income was considered and ' +
|
|
133
|
+
'rejected when that record was written: it makes contributing feel unrewarding, and ' +
|
|
134
|
+
'gaming is answered by payout eligibility rather than by vesting.',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
'transfer-to-another-holder': {
|
|
138
|
+
kind: 'earned',
|
|
139
|
+
capability: 'transfer-to-another-holder',
|
|
140
|
+
standing: {
|
|
141
|
+
standing: 'permitted',
|
|
142
|
+
because: 'An unrestricted lot carries no restriction, so the refusal that applies to a restricted ' +
|
|
143
|
+
'lot does not arise (CAY-0074). Who may be on the other end is a separate question about ' +
|
|
144
|
+
'counterparties.',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
'sell-at-a-quoted-price': {
|
|
148
|
+
kind: 'earned',
|
|
149
|
+
capability: 'sell-at-a-quoted-price',
|
|
150
|
+
standing: {
|
|
151
|
+
standing: 'permitted',
|
|
152
|
+
because: 'Earned cents already carry a cash-out right, so a quoted sale realises nothing the ' +
|
|
153
|
+
'holder could not realise through the Treasury.',
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
'cash-out-through-the-treasury': {
|
|
157
|
+
kind: 'earned',
|
|
158
|
+
capability: 'cash-out-through-the-treasury',
|
|
159
|
+
standing: {
|
|
160
|
+
standing: 'permitted',
|
|
161
|
+
because: 'Earned cents are unrestricted, so they sit inside the figure the backing ceiling is ' +
|
|
162
|
+
'measured against and eligible sterling is held for them from the moment they land.',
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
// The founder pool, allocated once at genesis to a fixed schedule (CAY-0002). It
|
|
167
|
+
// is granted rather than earned or purchased, so it lands RESTRICTED and vests on
|
|
168
|
+
// its own clock.
|
|
169
|
+
'founder-allocation': {
|
|
170
|
+
'spend-on-community-services': {
|
|
171
|
+
kind: 'founder-allocation',
|
|
172
|
+
capability: 'spend-on-community-services',
|
|
173
|
+
standing: {
|
|
174
|
+
standing: 'permitted',
|
|
175
|
+
because: 'Restricted means restricted from cash-out for a founder allocation exactly as it does ' +
|
|
176
|
+
'for a reward. A founder paying for Community services with an unvested allocation ' +
|
|
177
|
+
'spends a service credit inside the economy; nothing leaves it.',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
'transfer-to-another-holder': {
|
|
181
|
+
kind: 'founder-allocation',
|
|
182
|
+
capability: 'transfer-to-another-holder',
|
|
183
|
+
standing: {
|
|
184
|
+
standing: 'refused',
|
|
185
|
+
because: 'The same decision as the reward above (CAY-0074), and it matters more here: a carve does ' +
|
|
186
|
+
'not move restriction, so a transfer out of an unvested allocation would land ' +
|
|
187
|
+
'unrestricted and short-circuit the vesting schedule, against a pool whose whole point is ' +
|
|
188
|
+
'that it vests slowly. Refused. The allocation still pays for Community services, and ' +
|
|
189
|
+
'what the provider earns lands unrestricted.',
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
'sell-at-a-quoted-price': {
|
|
193
|
+
kind: 'founder-allocation',
|
|
194
|
+
capability: 'sell-at-a-quoted-price',
|
|
195
|
+
standing: {
|
|
196
|
+
standing: 'refused',
|
|
197
|
+
because: 'A quoted sale of unvested cents realises their value in sterling and empties the vesting ' +
|
|
198
|
+
'schedule of meaning. Refused for as long as the lot reads restricted, on the same ' +
|
|
199
|
+
'reasoning as the reward row and with more at stake.',
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
'cash-out-through-the-treasury': {
|
|
203
|
+
kind: 'founder-allocation',
|
|
204
|
+
capability: 'cash-out-through-the-treasury',
|
|
205
|
+
standing: {
|
|
206
|
+
standing: 'refused',
|
|
207
|
+
because: 'The allocation is restricted until it vests, and restricted cents cannot be cashed out. ' +
|
|
208
|
+
'They consume no backing while they are restricted, so there is no eligible sterling held ' +
|
|
209
|
+
'against them to draw.',
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* What each kind arrives as, and what expiry means for it.
|
|
216
|
+
*
|
|
217
|
+
* **Expiry is not uniform across the four kinds, and the answer has three parts.**
|
|
218
|
+
* Two kinds have no expiry at all, because they never land restricted. The two
|
|
219
|
+
* that do run on their own clocks — a reward's and a founder allocation's are
|
|
220
|
+
* different periods, held in the canon. And the DESTINATION is uniform: whichever
|
|
221
|
+
* clock ran, a lapsed lot reads unrestricted and stands exactly at
|
|
222
|
+
* {@link ONCE_UNRESTRICTED}, which is why that table is stated once rather than
|
|
223
|
+
* four times.
|
|
224
|
+
*
|
|
225
|
+
* Expiry is a condition evaluated on READ (CAY-0019), recorded twice with the
|
|
226
|
+
* promise governing (CAY-0020). Nothing sweeps, nothing vests on a job, and no
|
|
227
|
+
* local clock decides it.
|
|
228
|
+
*/
|
|
229
|
+
export const RESTRICTION_ON_ARRIVAL = {
|
|
230
|
+
purchased: {
|
|
231
|
+
kind: 'purchased',
|
|
232
|
+
onArrival: 'unrestricted',
|
|
233
|
+
expiry: 'never-restricted',
|
|
234
|
+
because: 'Purchases land unrestricted and bring their own sterling with them (CAY-0013). There is no ' +
|
|
235
|
+
'restricted phase, so there is nothing to expire and no promised date to record.',
|
|
236
|
+
},
|
|
237
|
+
'promotional-reward': {
|
|
238
|
+
kind: 'promotional-reward',
|
|
239
|
+
onArrival: 'restricted',
|
|
240
|
+
expiry: 'expires',
|
|
241
|
+
because: 'Granted Cayso lands restricted and carries an expiry (CAY-0031). Its lapse is a FUNDING ' +
|
|
242
|
+
'EVENT: the cents move from consuming no backing to consuming headroom, so the Treasury must ' +
|
|
243
|
+
'hold the sterling before the lapse rather than after it (CAY-0013).',
|
|
244
|
+
},
|
|
245
|
+
earned: {
|
|
246
|
+
kind: 'earned',
|
|
247
|
+
onArrival: 'unrestricted',
|
|
248
|
+
expiry: 'never-restricted',
|
|
249
|
+
because: 'Earned versus granted decides restriction status, and earned income lands unrestricted ' +
|
|
250
|
+
'(CAY-0001). There is no restricted phase and nothing to expire.',
|
|
251
|
+
},
|
|
252
|
+
'founder-allocation': {
|
|
253
|
+
kind: 'founder-allocation',
|
|
254
|
+
onArrival: 'restricted',
|
|
255
|
+
expiry: 'expires',
|
|
256
|
+
because: 'The founder pool is allocated at genesis to a fixed schedule and vests on its own clock, ' +
|
|
257
|
+
'which is not the reward clock (CAY-0002). Its lapse is a funding event on the same ' +
|
|
258
|
+
'reasoning as a reward’s, at the scale of the pool.',
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* What ANY lot may do once it reads unrestricted, whatever it arrived as.
|
|
263
|
+
*
|
|
264
|
+
* This is the answer to "what happens to a restricted lot's capabilities when the
|
|
265
|
+
* restriction expires", and it is stated once because it does not vary by kind: a
|
|
266
|
+
* lapsed reward and a vested founder allocation stand exactly where a purchase and
|
|
267
|
+
* an earned payment stood all along. The origin of the cents decided how long they
|
|
268
|
+
* were restricted; it does not go on deciding anything afterwards.
|
|
269
|
+
*
|
|
270
|
+
* The transfer refusal lifts here, and that is worth saying plainly: it refuses
|
|
271
|
+
* moving a RESTRICTED lot, because a carve does not move restriction and the
|
|
272
|
+
* transfer would launder it off (CAY-0074). Once there is no restriction, there is
|
|
273
|
+
* nothing to launder, and transferring is permitted.
|
|
274
|
+
*/
|
|
275
|
+
export const ONCE_UNRESTRICTED = {
|
|
276
|
+
'spend-on-community-services': {
|
|
277
|
+
standing: 'permitted',
|
|
278
|
+
because: 'Unchanged by expiry: restricted cents were already spendable on Community services.',
|
|
279
|
+
},
|
|
280
|
+
'transfer-to-another-holder': {
|
|
281
|
+
standing: 'permitted',
|
|
282
|
+
because: 'The refusal while restricted is that a transfer would launder the restriction off, because ' +
|
|
283
|
+
'a carve does not move it (CAY-0074). An unrestricted lot has none to launder, so ' +
|
|
284
|
+
'transferring is permitted.',
|
|
285
|
+
},
|
|
286
|
+
'sell-at-a-quoted-price': {
|
|
287
|
+
standing: 'permitted',
|
|
288
|
+
because: 'The refusal while restricted was that a quoted sale is a cash-out by another route. Once ' +
|
|
289
|
+
'the lot may be cashed out, that reasoning is spent.',
|
|
290
|
+
},
|
|
291
|
+
'cash-out-through-the-treasury': {
|
|
292
|
+
standing: 'permitted',
|
|
293
|
+
because: 'The lapse is exactly the event that makes these cents cashable, and it is the moment they ' +
|
|
294
|
+
'begin consuming headroom (CAY-0013).',
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
// --- Derived views -----------------------------------------------------------
|
|
298
|
+
/**
|
|
299
|
+
* Every holding kind, as a value. Derived from {@link CAPABILITY_POLICY} rather
|
|
300
|
+
* than hand-listed: the table is a mapped type over the union, so its keys ARE the
|
|
301
|
+
* union and this list cannot drift from it.
|
|
302
|
+
*/
|
|
303
|
+
export const HOLDING_KINDS = Object.keys(CAPABILITY_POLICY);
|
|
304
|
+
/** Every capability, derived from the table's own rows for the same reason. */
|
|
305
|
+
export const HOLDING_CAPABILITIES = Object.keys(CAPABILITY_POLICY.purchased);
|
|
306
|
+
/**
|
|
307
|
+
* Every cell nobody has decided, as coordinates. EMPTY since CAY-0074 answered the
|
|
308
|
+
* question that held the two transfer cells open; kept so the next undecided cell
|
|
309
|
+
* has somewhere to be listed.
|
|
310
|
+
*
|
|
311
|
+
* Derived from the table, so it cannot list a cell the table has settled or miss
|
|
312
|
+
* one it has not. It exists so that "which cells are open" is a question with a
|
|
313
|
+
* runtime answer: a consumer rendering the table, or a later enforcement slice
|
|
314
|
+
* auditing its own coverage, reads this rather than remembering.
|
|
315
|
+
*/
|
|
316
|
+
export const OPEN_CAPABILITIES = HOLDING_KINDS.flatMap((kind) => HOLDING_CAPABILITIES.filter((capability) => CAPABILITY_POLICY[kind][capability].standing.standing === 'open').map((capability) => ({ kind, capability })));
|