@plasius/economy 0.1.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 +203 -0
- package/README.md +181 -0
- package/dist/index.cjs +1711 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +558 -0
- package/dist/index.d.ts +558 -0
- package/dist/index.js +1628 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
/** Number of indivisible subunits in one public Token. */
|
|
2
|
+
declare const TOKEN_SUBUNITS_PER_TOKEN = 1000n;
|
|
3
|
+
/** PostgreSQL-compatible signed bigint bounds. */
|
|
4
|
+
declare const TOKEN_SUBUNITS_MIN: bigint;
|
|
5
|
+
declare const TOKEN_SUBUNITS_MAX: bigint;
|
|
6
|
+
declare const tokenSubunitStringBrand: unique symbol;
|
|
7
|
+
/** Canonical base-10 representation used at JSON and persistence boundaries. */
|
|
8
|
+
type TokenSubunitString = string & {
|
|
9
|
+
readonly [tokenSubunitStringBrand]: "TokenSubunitString";
|
|
10
|
+
};
|
|
11
|
+
/** Parses a canonical base-10 TokenSubunit string without floating point. */
|
|
12
|
+
declare function parseTokenSubunits(value: string): bigint;
|
|
13
|
+
/** Serializes a signed 64-bit TokenSubunit amount to canonical base-10. */
|
|
14
|
+
declare function serializeTokenSubunits(amount: bigint): TokenSubunitString;
|
|
15
|
+
/** Converts an exact whole-Token integer into TokenSubunits. */
|
|
16
|
+
declare function wholeTokensToSubunits(tokens: bigint): bigint;
|
|
17
|
+
/** Returns true when a subunit amount represents an exact whole Token. */
|
|
18
|
+
declare function isWholeTokenAmount(amount: bigint): boolean;
|
|
19
|
+
/** Converts TokenSubunits to whole Tokens, rejecting fractional progress. */
|
|
20
|
+
declare function subunitsToWholeTokens(amount: bigint): bigint;
|
|
21
|
+
/** Sums amount strings with signed 64-bit overflow protection. */
|
|
22
|
+
declare function sumTokenSubunits(values: readonly string[]): bigint;
|
|
23
|
+
|
|
24
|
+
declare const ECONOMY_CONTRACT_VERSION: "1";
|
|
25
|
+
type EconomyContractVersion = typeof ECONOMY_CONTRACT_VERSION;
|
|
26
|
+
type AccountId = string;
|
|
27
|
+
type AllocationId = string;
|
|
28
|
+
type HouseholdId = string;
|
|
29
|
+
type LotId = string;
|
|
30
|
+
type PostingId = string;
|
|
31
|
+
type ProviderEventId = string;
|
|
32
|
+
type TransactionId = string;
|
|
33
|
+
type WalletId = string;
|
|
34
|
+
type IsoTimestamp = string;
|
|
35
|
+
/** Validates opaque pseudonymous domain identifiers. */
|
|
36
|
+
declare function assertEconomyIdentifier(value: string, label?: string): void;
|
|
37
|
+
/** Parses a bounded ISO timestamp and returns milliseconds since epoch. */
|
|
38
|
+
declare function parseIsoTimestamp(value: IsoTimestamp): number;
|
|
39
|
+
/** Returns a new record with keys sorted for canonical serialization. */
|
|
40
|
+
declare function sortStringRecord(value: Readonly<Record<string, string>>): Readonly<Record<string, string>>;
|
|
41
|
+
|
|
42
|
+
type ActivityType = "purchase" | "subscription" | "rewarded-ad" | "offerwall" | "allocation" | "boost" | "reclaim" | "spend" | "hold" | "refund" | "chargeback" | "adjustment" | "reversal" | "event" | "competition";
|
|
43
|
+
type ActivityStatus = "pending" | "held" | "settled" | "reversed" | "failed";
|
|
44
|
+
/** One signed side of an immutable double-entry transaction. */
|
|
45
|
+
interface LedgerPostingV1 {
|
|
46
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
47
|
+
readonly postingId: PostingId;
|
|
48
|
+
readonly transactionId: TransactionId;
|
|
49
|
+
readonly accountId: AccountId;
|
|
50
|
+
readonly walletId?: WalletId;
|
|
51
|
+
readonly lotId?: LotId;
|
|
52
|
+
readonly amount: TokenSubunitString;
|
|
53
|
+
}
|
|
54
|
+
/** Immutable transaction envelope. Posting amounts must sum to exactly zero. */
|
|
55
|
+
interface LedgerTransactionV1 {
|
|
56
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
57
|
+
readonly transactionId: TransactionId;
|
|
58
|
+
readonly activityType: ActivityType;
|
|
59
|
+
readonly status: ActivityStatus;
|
|
60
|
+
readonly idempotencyKey: string;
|
|
61
|
+
readonly providerEventId?: ProviderEventId;
|
|
62
|
+
readonly reversesTransactionId?: TransactionId;
|
|
63
|
+
readonly effectiveAt: IsoTimestamp;
|
|
64
|
+
readonly recordedAt: IsoTimestamp;
|
|
65
|
+
readonly previousCanonicalHash?: string;
|
|
66
|
+
readonly canonicalHash?: string;
|
|
67
|
+
readonly metadata: Readonly<Record<string, string>>;
|
|
68
|
+
readonly postings: readonly LedgerPostingV1[];
|
|
69
|
+
}
|
|
70
|
+
/** Privacy-safe, display-oriented journal entry returned by an API adapter. */
|
|
71
|
+
interface ActivityEntryV1 {
|
|
72
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
73
|
+
readonly transactionId: TransactionId;
|
|
74
|
+
readonly activityType: ActivityType;
|
|
75
|
+
readonly status: ActivityStatus;
|
|
76
|
+
readonly occurredAt: IsoTimestamp;
|
|
77
|
+
readonly amount: TokenSubunitString;
|
|
78
|
+
readonly beneficiaryAccountId?: AccountId;
|
|
79
|
+
readonly maskedReference?: string;
|
|
80
|
+
readonly sourceLabel: string;
|
|
81
|
+
}
|
|
82
|
+
/** Validates one privacy-safe activity row returned by an HTTP adapter. */
|
|
83
|
+
declare function assertActivityEntry(entry: ActivityEntryV1): void;
|
|
84
|
+
/** Proves the structural and arithmetic invariants of one transaction. */
|
|
85
|
+
declare function assertBalancedTransaction(transaction: LedgerTransactionV1): void;
|
|
86
|
+
/**
|
|
87
|
+
* Produces deterministic UTF-8 JSON for an approved SHA-256/HSM adapter.
|
|
88
|
+
* Hashing itself deliberately remains outside this provider-neutral package.
|
|
89
|
+
*/
|
|
90
|
+
declare function canonicalTransactionPayload(transaction: LedgerTransactionV1): string;
|
|
91
|
+
interface ReversalInputV1 {
|
|
92
|
+
readonly transactionId: TransactionId;
|
|
93
|
+
readonly postingIds: readonly PostingId[];
|
|
94
|
+
readonly idempotencyKey: string;
|
|
95
|
+
readonly effectiveAt: IsoTimestamp;
|
|
96
|
+
readonly recordedAt: IsoTimestamp;
|
|
97
|
+
readonly metadata?: Readonly<Record<string, string>>;
|
|
98
|
+
readonly previousCanonicalHash?: string;
|
|
99
|
+
}
|
|
100
|
+
/** Creates an immutable compensating transaction; it never edits the original. */
|
|
101
|
+
declare function createReversalTransaction(original: LedgerTransactionV1, input: ReversalInputV1): LedgerTransactionV1;
|
|
102
|
+
/** Rejects a second effective reversal for the same original transaction. */
|
|
103
|
+
declare function assertReversalAvailable(originalTransactionId: TransactionId, existingTransactions: readonly LedgerTransactionV1[]): void;
|
|
104
|
+
|
|
105
|
+
interface TokenPackV1 {
|
|
106
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
107
|
+
readonly packId: string;
|
|
108
|
+
readonly catalogVersion: string;
|
|
109
|
+
readonly currency: "GBP";
|
|
110
|
+
readonly priceMinorUnits: string;
|
|
111
|
+
readonly grantAmount: TokenSubunitString;
|
|
112
|
+
readonly active: boolean;
|
|
113
|
+
}
|
|
114
|
+
/** Product-copy reference only; it is not a redemption or exchange promise. */
|
|
115
|
+
interface TokenReferenceRateV1 {
|
|
116
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
117
|
+
readonly rateVersion: string;
|
|
118
|
+
readonly currency: "GBP";
|
|
119
|
+
readonly referenceMinorUnitsPerToken: string;
|
|
120
|
+
readonly tokenSubunitsPerToken: string;
|
|
121
|
+
readonly cashRedemptionAllowed: false;
|
|
122
|
+
}
|
|
123
|
+
declare const BASELINE_GBP_REFERENCE_RATE: TokenReferenceRateV1;
|
|
124
|
+
declare const BASELINE_GBP_CATALOG_VERSION: "gbp-v1";
|
|
125
|
+
/** Initial flat-price public catalog. Changes require a new catalog version. */
|
|
126
|
+
declare const BASELINE_GBP_TOKEN_PACKS: readonly TokenPackV1[];
|
|
127
|
+
interface PurchaseLimitPolicyV1 {
|
|
128
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
129
|
+
readonly maxOrderPriceMinorUnits: string;
|
|
130
|
+
readonly rollingPayerPriceMinorUnits: string;
|
|
131
|
+
readonly rollingHouseholdPriceMinorUnits: string;
|
|
132
|
+
readonly rollingDays: number;
|
|
133
|
+
}
|
|
134
|
+
/** Default server limits; household controls may only lower these values. */
|
|
135
|
+
declare const BASELINE_PURCHASE_LIMIT_POLICY: PurchaseLimitPolicyV1;
|
|
136
|
+
type PurchaseIntentStatus = "created" | "checkout-created" | "paid-unreconciled" | "credited" | "expired" | "cancelled" | "disputed";
|
|
137
|
+
/** Short-lived server authority binding checkout facts to a payer/household. */
|
|
138
|
+
interface PurchaseIntentV1 {
|
|
139
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
140
|
+
readonly intentId: string;
|
|
141
|
+
readonly payerAccountId: AccountId;
|
|
142
|
+
readonly receivingHouseholdId: HouseholdId;
|
|
143
|
+
readonly receivingWalletId: WalletId;
|
|
144
|
+
readonly packId: string;
|
|
145
|
+
readonly catalogVersion: string;
|
|
146
|
+
readonly expectedCurrency: "GBP";
|
|
147
|
+
readonly expectedPriceMinorUnits: string;
|
|
148
|
+
readonly grantAmount: TokenSubunitString;
|
|
149
|
+
readonly status: PurchaseIntentStatus;
|
|
150
|
+
readonly createdAt: IsoTimestamp;
|
|
151
|
+
readonly expiresAt: IsoTimestamp;
|
|
152
|
+
readonly providerCheckoutReferenceHash?: string;
|
|
153
|
+
}
|
|
154
|
+
interface RewardRateV1 {
|
|
155
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
156
|
+
readonly rateVersion: string;
|
|
157
|
+
readonly provider: "ayet" | "bitlabs";
|
|
158
|
+
/** TokenSubunits granted per denominator of authoritative GBP minor units. */
|
|
159
|
+
readonly tokenSubunitsNumerator: string;
|
|
160
|
+
readonly gbpMinorUnitsDenominator: string;
|
|
161
|
+
readonly effectiveFrom: IsoTimestamp;
|
|
162
|
+
readonly effectiveUntil?: IsoTimestamp;
|
|
163
|
+
}
|
|
164
|
+
interface FxSnapshotV1 {
|
|
165
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
166
|
+
readonly snapshotId: string;
|
|
167
|
+
readonly sourceCurrency: string;
|
|
168
|
+
readonly quoteCurrency: "GBP";
|
|
169
|
+
/** Exact rational conversion to GBP minor units. */
|
|
170
|
+
readonly gbpMinorUnitsNumerator: string;
|
|
171
|
+
readonly sourceMinorUnitsDenominator: string;
|
|
172
|
+
readonly capturedAt: IsoTimestamp;
|
|
173
|
+
}
|
|
174
|
+
type RewardConversionStatus = "pending" | "completed" | "reconciled";
|
|
175
|
+
type BitLabsConversionState = "PENDING" | "COMPLETED" | "RECONCILED";
|
|
176
|
+
interface RewardActivityTransitionV1 {
|
|
177
|
+
readonly conversionStatus: RewardConversionStatus;
|
|
178
|
+
readonly activityType: ActivityType;
|
|
179
|
+
readonly activityStatus: ActivityStatus;
|
|
180
|
+
}
|
|
181
|
+
interface RewardConversionV1 {
|
|
182
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
183
|
+
readonly provider: "ayet" | "bitlabs";
|
|
184
|
+
readonly providerEventId: ProviderEventId;
|
|
185
|
+
readonly beneficiaryAccountId: AccountId;
|
|
186
|
+
readonly walletId: WalletId;
|
|
187
|
+
readonly status: RewardConversionStatus;
|
|
188
|
+
readonly providerPayoutMinorUnits: string;
|
|
189
|
+
readonly providerCurrency: string;
|
|
190
|
+
readonly gbpMinorUnits: string;
|
|
191
|
+
readonly tokenSubunits: TokenSubunitString;
|
|
192
|
+
readonly rateVersion: string;
|
|
193
|
+
readonly fxSnapshotId: string;
|
|
194
|
+
readonly occurredAt: IsoTimestamp;
|
|
195
|
+
}
|
|
196
|
+
/** Validates a minimized, signed-provider conversion record before journaling. */
|
|
197
|
+
declare function assertRewardConversion(conversion: RewardConversionV1): void;
|
|
198
|
+
/** Validates a paid pack; pack grants are whole Tokens in the baseline. */
|
|
199
|
+
declare function assertTokenPack(pack: TokenPackV1): void;
|
|
200
|
+
/** Validates nominal reference metadata without creating redemption rights. */
|
|
201
|
+
declare function assertTokenReferenceRate(rate: TokenReferenceRateV1): void;
|
|
202
|
+
/** Proves that every pack in one catalog uses the same flat nominal ratio. */
|
|
203
|
+
declare function assertFlatTokenCatalog(packs: readonly TokenPackV1[], referenceRate: TokenReferenceRateV1): void;
|
|
204
|
+
/** Validates server-side order and rolling acquisition ceilings. */
|
|
205
|
+
declare function assertPurchaseLimitPolicy(policy: PurchaseLimitPolicyV1): void;
|
|
206
|
+
/** Validates the immutable server-side facts bound into a purchase intent. */
|
|
207
|
+
declare function assertPurchaseIntentBinding(intent: PurchaseIntentV1, pack: TokenPackV1): void;
|
|
208
|
+
/** Rejects stale, inactive, or already-consumed intents before checkout creation. */
|
|
209
|
+
declare function assertOpenPurchaseIntent(intent: PurchaseIntentV1, pack: TokenPackV1, checkedAt: IsoTimestamp): void;
|
|
210
|
+
declare function assertRewardRate(rate: RewardRateV1): void;
|
|
211
|
+
declare function assertFxSnapshot(fx: FxSnapshotV1): void;
|
|
212
|
+
/** Converts provider payout using exact rational FX and rate snapshots. */
|
|
213
|
+
declare function convertRewardPayout(providerPayoutMinorUnits: string, rate: RewardRateV1, fx: FxSnapshotV1): {
|
|
214
|
+
readonly gbpMinorUnits: string;
|
|
215
|
+
readonly tokenSubunits: TokenSubunitString;
|
|
216
|
+
};
|
|
217
|
+
/** Maps BitLabs' signed callback state into an immutable journal transition. */
|
|
218
|
+
declare function mapBitLabsConversionState(state: string): RewardActivityTransitionV1;
|
|
219
|
+
|
|
220
|
+
type TokenSource = "shopify" | "ayet" | "bitlabs" | "subscription" | "event" | "competition" | "adjustment";
|
|
221
|
+
type LotTransferPolicy = "household-allocatable" | "same-user-only" | "non-transferable";
|
|
222
|
+
type LotRefundState = "none" | "partial" | "refunded" | "disputed" | "chargeback-lost";
|
|
223
|
+
interface SourceLotV1 {
|
|
224
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
225
|
+
readonly lotId: LotId;
|
|
226
|
+
readonly walletId: WalletId;
|
|
227
|
+
readonly beneficiaryAccountId: AccountId;
|
|
228
|
+
readonly householdId?: HouseholdId;
|
|
229
|
+
readonly payerAccountId?: AccountId;
|
|
230
|
+
readonly source: TokenSource;
|
|
231
|
+
readonly providerEventId?: ProviderEventId;
|
|
232
|
+
readonly rateVersion: string;
|
|
233
|
+
readonly settlementEvidenceHash: string;
|
|
234
|
+
readonly transferPolicy: LotTransferPolicy;
|
|
235
|
+
readonly refundState: LotRefundState;
|
|
236
|
+
readonly originalAmount: TokenSubunitString;
|
|
237
|
+
readonly remainingAmount: TokenSubunitString;
|
|
238
|
+
readonly heldAmount: TokenSubunitString;
|
|
239
|
+
readonly reversedAmount: TokenSubunitString;
|
|
240
|
+
readonly settledAt: IsoTimestamp;
|
|
241
|
+
readonly creditedAt: IsoTimestamp;
|
|
242
|
+
readonly cohortKey?: string;
|
|
243
|
+
}
|
|
244
|
+
type LotUseOperation = "allocate" | "spend" | "reclaim" | "refund";
|
|
245
|
+
interface LotUseContextV1 {
|
|
246
|
+
readonly operation: LotUseOperation;
|
|
247
|
+
readonly beneficiaryAccountId: AccountId;
|
|
248
|
+
readonly householdId?: HouseholdId;
|
|
249
|
+
}
|
|
250
|
+
interface SourceLotSliceV1 {
|
|
251
|
+
readonly lotId: LotId;
|
|
252
|
+
readonly amount: TokenSubunitString;
|
|
253
|
+
}
|
|
254
|
+
/** Validates source-lot arithmetic, evidence, and immutable provenance fields. */
|
|
255
|
+
declare function assertSourceLot(lot: SourceLotV1): void;
|
|
256
|
+
/** Returns unheld, unconsumed TokenSubunits in a source lot. */
|
|
257
|
+
declare function availableSourceLotAmount(lot: SourceLotV1): bigint;
|
|
258
|
+
/** Evaluates provider/source restrictions without trusting a browser claim. */
|
|
259
|
+
declare function canUseSourceLot(lot: SourceLotV1, context: LotUseContextV1): boolean;
|
|
260
|
+
/**
|
|
261
|
+
* Selects eligible lots deterministically in credited-time/lot-id order.
|
|
262
|
+
* The returned slices are a proposal; persistence must consume them atomically.
|
|
263
|
+
*/
|
|
264
|
+
declare function selectSourceLots(lots: readonly SourceLotV1[], requestedAmount: TokenSubunitString, context: LotUseContextV1): readonly SourceLotSliceV1[];
|
|
265
|
+
|
|
266
|
+
type AllocationStatus = "active" | "closed";
|
|
267
|
+
type PeriodicLimitWindow = "daily" | "weekly" | "monthly";
|
|
268
|
+
interface GameplayLimitsV1 {
|
|
269
|
+
readonly perTransaction: TokenSubunitString;
|
|
270
|
+
readonly periodic: TokenSubunitString;
|
|
271
|
+
readonly periodicWindow: PeriodicLimitWindow;
|
|
272
|
+
}
|
|
273
|
+
/** Guardian-funded, child-visible reservation. This is not a gameplay ledger. */
|
|
274
|
+
interface GameplayAllocationV1 {
|
|
275
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
276
|
+
readonly allocationId: AllocationId;
|
|
277
|
+
readonly householdId: HouseholdId;
|
|
278
|
+
readonly hostWalletId: WalletId;
|
|
279
|
+
readonly childWalletId: WalletId;
|
|
280
|
+
readonly childAccountId: AccountId;
|
|
281
|
+
readonly status: AllocationStatus;
|
|
282
|
+
readonly reservedAmount: TokenSubunitString;
|
|
283
|
+
readonly remainingAmount: TokenSubunitString;
|
|
284
|
+
readonly reclaimedAmount: TokenSubunitString;
|
|
285
|
+
readonly fundingSlices: readonly SourceLotSliceV1[];
|
|
286
|
+
readonly reclaimedSlices: readonly SourceLotSliceV1[];
|
|
287
|
+
readonly limits: GameplayLimitsV1;
|
|
288
|
+
readonly version: number;
|
|
289
|
+
readonly createdAt: IsoTimestamp;
|
|
290
|
+
readonly updatedAt: IsoTimestamp;
|
|
291
|
+
}
|
|
292
|
+
/** Validates allocation arithmetic and whole-Token reservation policy. */
|
|
293
|
+
declare function assertGameplayAllocation(allocation: GameplayAllocationV1): void;
|
|
294
|
+
interface CreateGameplayAllocationInputV1 {
|
|
295
|
+
readonly allocationId: AllocationId;
|
|
296
|
+
readonly householdId: HouseholdId;
|
|
297
|
+
readonly hostWalletId: WalletId;
|
|
298
|
+
readonly childWalletId: WalletId;
|
|
299
|
+
readonly childAccountId: AccountId;
|
|
300
|
+
readonly amount: TokenSubunitString;
|
|
301
|
+
readonly fundingSlices: readonly SourceLotSliceV1[];
|
|
302
|
+
readonly limits: GameplayLimitsV1;
|
|
303
|
+
readonly occurredAt: IsoTimestamp;
|
|
304
|
+
}
|
|
305
|
+
/** Creates a new whole-Token reservation from already-selected source lots. */
|
|
306
|
+
declare function createGameplayAllocation(input: CreateGameplayAllocationInputV1): GameplayAllocationV1;
|
|
307
|
+
interface BoostGameplayAllocationInputV1 {
|
|
308
|
+
readonly amount: TokenSubunitString;
|
|
309
|
+
readonly fundingSlices: readonly SourceLotSliceV1[];
|
|
310
|
+
readonly expectedVersion: number;
|
|
311
|
+
readonly occurredAt: IsoTimestamp;
|
|
312
|
+
}
|
|
313
|
+
/** Returns the next immutable allocation state after a whole-Token boost. */
|
|
314
|
+
declare function boostGameplayAllocation(allocation: GameplayAllocationV1, input: BoostGameplayAllocationInputV1): GameplayAllocationV1;
|
|
315
|
+
interface ReclaimGameplayAllocationInputV1 {
|
|
316
|
+
readonly amount: TokenSubunitString;
|
|
317
|
+
readonly sourceSlices: readonly SourceLotSliceV1[];
|
|
318
|
+
readonly expectedVersion: number;
|
|
319
|
+
readonly occurredAt: IsoTimestamp;
|
|
320
|
+
}
|
|
321
|
+
/** Reclaims unused whole Tokens without changing spent history. */
|
|
322
|
+
declare function reclaimGameplayAllocation(allocation: GameplayAllocationV1, input: ReclaimGameplayAllocationInputV1): GameplayAllocationV1;
|
|
323
|
+
|
|
324
|
+
declare const PRE_UTILITY_BACKER_COHORT: "pre_utility_backer_v1";
|
|
325
|
+
interface EarlyBackerWindowV1 {
|
|
326
|
+
readonly publicTokensLaunchAt: IsoTimestamp;
|
|
327
|
+
readonly firstPublicSpendLiveAt?: IsoTimestamp;
|
|
328
|
+
}
|
|
329
|
+
/** Paid-lot facts required to recalculate retained early-backer basis. */
|
|
330
|
+
interface PaidLotRetentionV1 {
|
|
331
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
332
|
+
readonly lotId: LotId;
|
|
333
|
+
readonly payerAccountId: AccountId;
|
|
334
|
+
readonly receivingHouseholdId: HouseholdId;
|
|
335
|
+
readonly purchaseId: string;
|
|
336
|
+
readonly catalogVersion: string;
|
|
337
|
+
readonly purchasedAt: IsoTimestamp;
|
|
338
|
+
readonly settledAt: IsoTimestamp;
|
|
339
|
+
readonly creditedAt: IsoTimestamp;
|
|
340
|
+
readonly retainedAmount: TokenSubunitString;
|
|
341
|
+
}
|
|
342
|
+
interface EarlyBackerEvaluationV1 {
|
|
343
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
344
|
+
readonly cohortKey: typeof PRE_UTILITY_BACKER_COHORT;
|
|
345
|
+
readonly status: "not-qualified" | "provisional";
|
|
346
|
+
readonly payerAccountId: AccountId;
|
|
347
|
+
readonly receivingHouseholdId: HouseholdId;
|
|
348
|
+
readonly netRetainedAmount: TokenSubunitString;
|
|
349
|
+
readonly contributingLotIds: readonly LotId[];
|
|
350
|
+
readonly evaluatedAt: IsoTimestamp;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Recalculates provisional cohort basis from retained paid lots. It promises no
|
|
354
|
+
* future reward and deliberately has no permanent entitled state.
|
|
355
|
+
*/
|
|
356
|
+
declare function evaluateEarlyBacker(payerAccountId: AccountId, receivingHouseholdId: HouseholdId, paidLots: readonly PaidLotRetentionV1[], window: EarlyBackerWindowV1, evaluatedAt: IsoTimestamp): EarlyBackerEvaluationV1;
|
|
357
|
+
|
|
358
|
+
/** Stable machine-readable failures exposed by the economy domain. */
|
|
359
|
+
type EconomyErrorCode = "INVALID_AMOUNT" | "AMOUNT_OUT_OF_RANGE" | "AMOUNT_NOT_WHOLE_TOKEN" | "INVALID_CONTRACT" | "UNBALANCED_TRANSACTION" | "DUPLICATE_IDENTIFIER" | "DUPLICATE_TRANSACTION" | "INSUFFICIENT_BALANCE" | "INSUFFICIENT_ELIGIBLE_LOTS" | "SOURCE_LOT_RESTRICTED" | "REVERSAL_ALREADY_EXISTS" | "NEGATIVE_PROJECTION" | "INVALID_TIME_WINDOW";
|
|
360
|
+
interface EconomyErrorEnvelopeV1 {
|
|
361
|
+
readonly schemaVersion: "1";
|
|
362
|
+
readonly error: {
|
|
363
|
+
readonly code: EconomyErrorCode;
|
|
364
|
+
readonly message: string;
|
|
365
|
+
readonly requestId?: string;
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Domain error with a stable code and deliberately non-sensitive message.
|
|
370
|
+
* Provider evidence, identities, and payment details must not be included.
|
|
371
|
+
*/
|
|
372
|
+
declare class EconomyError extends Error {
|
|
373
|
+
readonly code: EconomyErrorCode;
|
|
374
|
+
constructor(code: EconomyErrorCode, message: string);
|
|
375
|
+
}
|
|
376
|
+
/** Throws a stable economy error when a domain assertion fails. */
|
|
377
|
+
declare function economyAssert(condition: unknown, code: EconomyErrorCode, message: string): asserts condition;
|
|
378
|
+
/** Creates a bounded, non-sensitive error contract for an HTTP adapter. */
|
|
379
|
+
declare function toEconomyErrorEnvelope(error: EconomyError, requestId?: string): EconomyErrorEnvelopeV1;
|
|
380
|
+
|
|
381
|
+
type AdjustmentStatus = "draft" | "pending-approval" | "approved" | "rejected" | "executed" | "expired";
|
|
382
|
+
/** Dual-control request; the resulting change is always a ledger transaction. */
|
|
383
|
+
interface AdjustmentRequestV1 {
|
|
384
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
385
|
+
readonly adjustmentId: string;
|
|
386
|
+
readonly walletId: WalletId;
|
|
387
|
+
readonly amount: TokenSubunitString;
|
|
388
|
+
readonly reason: string;
|
|
389
|
+
readonly ticketReference: string;
|
|
390
|
+
readonly status: AdjustmentStatus;
|
|
391
|
+
readonly initiatedByAccountId: AccountId;
|
|
392
|
+
readonly initiatedAt: IsoTimestamp;
|
|
393
|
+
readonly approvedByAccountId?: AccountId;
|
|
394
|
+
readonly approvedAt?: IsoTimestamp;
|
|
395
|
+
readonly resultingTransactionId?: TransactionId;
|
|
396
|
+
}
|
|
397
|
+
/** Proves that production adjustment approval was performed by another actor. */
|
|
398
|
+
declare function assertDistinctAdjustmentApproval(request: AdjustmentRequestV1): void;
|
|
399
|
+
|
|
400
|
+
type BalanceProjection = Readonly<Record<AccountId, TokenSubunitString>>;
|
|
401
|
+
interface BalanceProjectionSnapshotV1 {
|
|
402
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
403
|
+
readonly balances: BalanceProjection;
|
|
404
|
+
readonly lastTransactionId?: TransactionId;
|
|
405
|
+
readonly rebuiltAt: IsoTimestamp;
|
|
406
|
+
}
|
|
407
|
+
/** Applies one balanced transaction without mutating the existing projection. */
|
|
408
|
+
declare function applyTransactionToProjection(current: BalanceProjection, transaction: LedgerTransactionV1): BalanceProjection;
|
|
409
|
+
/**
|
|
410
|
+
* Rebuilds balances deterministically and rejects duplicate economic envelopes.
|
|
411
|
+
*/
|
|
412
|
+
declare function rebuildBalanceProjection(transactions: readonly LedgerTransactionV1[]): BalanceProjection;
|
|
413
|
+
/** Enforces non-negative balances only for spendable/reservable accounts. */
|
|
414
|
+
declare function assertNonNegativeAccounts(projection: BalanceProjection, protectedAccountIds: readonly AccountId[]): void;
|
|
415
|
+
/** Creates a versioned snapshot after a deterministic rebuild. */
|
|
416
|
+
declare function createProjectionSnapshot(transactions: readonly LedgerTransactionV1[], rebuiltAt: IsoTimestamp): BalanceProjectionSnapshotV1;
|
|
417
|
+
|
|
418
|
+
type WalletKind = "household-treasury" | "personal" | "gameplay-allocation" | "hold" | "purchase-clearing" | "provider-clearing" | "system";
|
|
419
|
+
type WalletStatus = "active" | "restricted" | "closed";
|
|
420
|
+
type WalletOwnerType = "account" | "household" | "system";
|
|
421
|
+
/** Versioned authoritative wallet descriptor. */
|
|
422
|
+
interface WalletV1 {
|
|
423
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
424
|
+
readonly walletId: WalletId;
|
|
425
|
+
readonly accountId: AccountId;
|
|
426
|
+
readonly kind: WalletKind;
|
|
427
|
+
readonly ownerType: WalletOwnerType;
|
|
428
|
+
readonly ownerId: AccountId | HouseholdId | "system";
|
|
429
|
+
readonly householdId?: HouseholdId;
|
|
430
|
+
readonly status: WalletStatus;
|
|
431
|
+
readonly version: number;
|
|
432
|
+
readonly createdAt: IsoTimestamp;
|
|
433
|
+
readonly closedAt?: IsoTimestamp;
|
|
434
|
+
}
|
|
435
|
+
/** Server-computed wallet totals. Clients must never derive authority from it. */
|
|
436
|
+
interface WalletBalanceSummaryV1 {
|
|
437
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
438
|
+
readonly walletId: WalletId;
|
|
439
|
+
readonly available: TokenSubunitString;
|
|
440
|
+
readonly reserved: TokenSubunitString;
|
|
441
|
+
readonly held: TokenSubunitString;
|
|
442
|
+
readonly rewardProgress: TokenSubunitString;
|
|
443
|
+
readonly version: number;
|
|
444
|
+
readonly asOf: IsoTimestamp;
|
|
445
|
+
}
|
|
446
|
+
/** Lifetime aggregates used by the Token profile. */
|
|
447
|
+
interface WalletLifetimeTotalsV1 {
|
|
448
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
449
|
+
readonly bought: TokenSubunitString;
|
|
450
|
+
readonly earned: TokenSubunitString;
|
|
451
|
+
readonly allocated: TokenSubunitString;
|
|
452
|
+
readonly reclaimed: TokenSubunitString;
|
|
453
|
+
readonly spent: TokenSubunitString;
|
|
454
|
+
readonly reversed: TokenSubunitString;
|
|
455
|
+
}
|
|
456
|
+
/** Validates wallet ownership, lifecycle, and optimistic-version facts. */
|
|
457
|
+
declare function assertWallet(wallet: WalletV1): void;
|
|
458
|
+
/** Validates non-negative server-computed wallet balance categories. */
|
|
459
|
+
declare function assertWalletBalanceSummary(summary: WalletBalanceSummaryV1): void;
|
|
460
|
+
/** Validates non-negative lifetime aggregates exposed by the profile API. */
|
|
461
|
+
declare function assertWalletLifetimeTotals(totals: WalletLifetimeTotalsV1): void;
|
|
462
|
+
|
|
463
|
+
type EconomyCommandType = "credit-purchase" | "credit-reward" | "allocate" | "boost" | "reclaim" | "hold" | "refund" | "chargeback" | "adjust";
|
|
464
|
+
interface EconomyCommandEnvelopeV1 {
|
|
465
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
466
|
+
readonly commandId: string;
|
|
467
|
+
readonly commandType: EconomyCommandType;
|
|
468
|
+
readonly idempotencyKey: string;
|
|
469
|
+
readonly actorAccountId: AccountId;
|
|
470
|
+
readonly subjectAccountId: AccountId;
|
|
471
|
+
readonly relationshipId?: string;
|
|
472
|
+
readonly authorizationVersion?: number;
|
|
473
|
+
readonly payloadHash: string;
|
|
474
|
+
readonly acceptedAt: IsoTimestamp;
|
|
475
|
+
readonly acceptedRegion: string;
|
|
476
|
+
readonly writerFencingToken: string;
|
|
477
|
+
}
|
|
478
|
+
interface EconomyOutboxEventV1 {
|
|
479
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
480
|
+
readonly eventId: string;
|
|
481
|
+
readonly transactionId: TransactionId;
|
|
482
|
+
readonly eventType: string;
|
|
483
|
+
readonly payloadHash: string;
|
|
484
|
+
readonly occurredAt: IsoTimestamp;
|
|
485
|
+
}
|
|
486
|
+
interface PersistedIdempotencyResultV1 {
|
|
487
|
+
readonly transactionId: TransactionId;
|
|
488
|
+
readonly responseHash: string;
|
|
489
|
+
readonly recordedAt: IsoTimestamp;
|
|
490
|
+
}
|
|
491
|
+
/** Operations available only inside one serializable database transaction. */
|
|
492
|
+
interface EconomyUnitOfWork {
|
|
493
|
+
getWalletForUpdate(walletId: WalletId): Promise<WalletV1 | null>;
|
|
494
|
+
listSpendableLotsForUpdate(walletId: WalletId): Promise<readonly SourceLotV1[]>;
|
|
495
|
+
findLotForUpdate(lotId: LotId): Promise<SourceLotV1 | null>;
|
|
496
|
+
findIdempotencyResult(idempotencyKey: string): Promise<PersistedIdempotencyResultV1 | null>;
|
|
497
|
+
appendTransaction(transaction: LedgerTransactionV1): Promise<void>;
|
|
498
|
+
appendSourceLot(lot: SourceLotV1): Promise<void>;
|
|
499
|
+
updateSourceLotProjection(lotId: LotId, remainingAmount: TokenSubunitString, heldAmount: TokenSubunitString, reversedAmount: TokenSubunitString): Promise<void>;
|
|
500
|
+
saveBalanceProjection(projection: BalanceProjection, transactionId: TransactionId): Promise<void>;
|
|
501
|
+
saveIdempotencyResult(idempotencyKey: string, result: PersistedIdempotencyResultV1): Promise<void>;
|
|
502
|
+
appendOutboxEvent(event: EconomyOutboxEventV1): Promise<void>;
|
|
503
|
+
}
|
|
504
|
+
/** Adapter boundary for serializable, ACID economy mutations. */
|
|
505
|
+
interface EconomyPersistencePort {
|
|
506
|
+
runSerializable<T>(operation: (unitOfWork: EconomyUnitOfWork) => Promise<T>): Promise<T>;
|
|
507
|
+
getWallet(walletId: WalletId): Promise<WalletV1 | null>;
|
|
508
|
+
getBalanceProjection(walletId: WalletId): Promise<BalanceProjectionSnapshotV1 | null>;
|
|
509
|
+
getTransaction(transactionId: TransactionId): Promise<LedgerTransactionV1 | null>;
|
|
510
|
+
rebuildProjection(): Promise<BalanceProjection>;
|
|
511
|
+
}
|
|
512
|
+
interface EconomyClockPort {
|
|
513
|
+
now(): IsoTimestamp;
|
|
514
|
+
}
|
|
515
|
+
interface EconomyIdPort {
|
|
516
|
+
nextId(namespace: string): string;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
type RecurrenceInterval = "daily" | "weekly" | "monthly";
|
|
520
|
+
/** Provider-neutral subscription contract. Site policy controls enablement. */
|
|
521
|
+
interface TokenSubscriptionPlanV1 {
|
|
522
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
523
|
+
readonly planId: string;
|
|
524
|
+
readonly catalogVersion: string;
|
|
525
|
+
readonly interval: RecurrenceInterval;
|
|
526
|
+
readonly priceMinorUnits: string;
|
|
527
|
+
readonly currency: "GBP";
|
|
528
|
+
readonly grantAmount: TokenSubunitString;
|
|
529
|
+
readonly enabled: boolean;
|
|
530
|
+
readonly effectiveFrom: IsoTimestamp;
|
|
531
|
+
}
|
|
532
|
+
/** Future convenience plan contract; disabled until public spend is live. */
|
|
533
|
+
declare const BASELINE_MONTHLY_SUBSCRIPTION_PLAN: TokenSubscriptionPlanV1;
|
|
534
|
+
/** Validates future provider-neutral recurrence without enabling it. */
|
|
535
|
+
declare function assertTokenSubscriptionPlan(plan: TokenSubscriptionPlanV1): void;
|
|
536
|
+
|
|
537
|
+
type SpendRequestPurpose = "gameplay-conversion" | "non-gameplay";
|
|
538
|
+
type SpendRequestStatus = "requested" | "approved" | "rejected" | "expired" | "executed" | "cancelled";
|
|
539
|
+
/** Future child request; baseline adapters must reject creation while disabled. */
|
|
540
|
+
interface SpendRequestV1 {
|
|
541
|
+
readonly schemaVersion: EconomyContractVersion;
|
|
542
|
+
readonly requestId: string;
|
|
543
|
+
readonly childAccountId: AccountId;
|
|
544
|
+
readonly allocationId: AllocationId;
|
|
545
|
+
readonly purpose: SpendRequestPurpose;
|
|
546
|
+
readonly amount: TokenSubunitString;
|
|
547
|
+
readonly status: SpendRequestStatus;
|
|
548
|
+
readonly requestedAt: IsoTimestamp;
|
|
549
|
+
readonly expiresAt: IsoTimestamp;
|
|
550
|
+
readonly decidedByAccountId?: AccountId;
|
|
551
|
+
readonly decidedAt?: IsoTimestamp;
|
|
552
|
+
readonly approvalNonceHash?: string;
|
|
553
|
+
readonly resultingTransactionId?: TransactionId;
|
|
554
|
+
}
|
|
555
|
+
/** Validates future child requests while creation remains policy-disabled. */
|
|
556
|
+
declare function assertSpendRequest(request: SpendRequestV1): void;
|
|
557
|
+
|
|
558
|
+
export { type AccountId, type ActivityEntryV1, type ActivityStatus, type ActivityType, type AdjustmentRequestV1, type AdjustmentStatus, type AllocationId, type AllocationStatus, BASELINE_GBP_CATALOG_VERSION, BASELINE_GBP_REFERENCE_RATE, BASELINE_GBP_TOKEN_PACKS, BASELINE_MONTHLY_SUBSCRIPTION_PLAN, BASELINE_PURCHASE_LIMIT_POLICY, type BalanceProjection, type BalanceProjectionSnapshotV1, type BitLabsConversionState, type BoostGameplayAllocationInputV1, type CreateGameplayAllocationInputV1, ECONOMY_CONTRACT_VERSION, type EarlyBackerEvaluationV1, type EarlyBackerWindowV1, type EconomyClockPort, type EconomyCommandEnvelopeV1, type EconomyCommandType, type EconomyContractVersion, EconomyError, type EconomyErrorCode, type EconomyErrorEnvelopeV1, type EconomyIdPort, type EconomyOutboxEventV1, type EconomyPersistencePort, type EconomyUnitOfWork, type FxSnapshotV1, type GameplayAllocationV1, type GameplayLimitsV1, type HouseholdId, type IsoTimestamp, type LedgerPostingV1, type LedgerTransactionV1, type LotId, type LotRefundState, type LotTransferPolicy, type LotUseContextV1, type LotUseOperation, PRE_UTILITY_BACKER_COHORT, type PaidLotRetentionV1, type PeriodicLimitWindow, type PersistedIdempotencyResultV1, type PostingId, type ProviderEventId, type PurchaseIntentStatus, type PurchaseIntentV1, type PurchaseLimitPolicyV1, type ReclaimGameplayAllocationInputV1, type RecurrenceInterval, type ReversalInputV1, type RewardActivityTransitionV1, type RewardConversionStatus, type RewardConversionV1, type RewardRateV1, type SourceLotSliceV1, type SourceLotV1, type SpendRequestPurpose, type SpendRequestStatus, type SpendRequestV1, TOKEN_SUBUNITS_MAX, TOKEN_SUBUNITS_MIN, TOKEN_SUBUNITS_PER_TOKEN, type TokenPackV1, type TokenReferenceRateV1, type TokenSource, type TokenSubscriptionPlanV1, type TokenSubunitString, type TransactionId, type WalletBalanceSummaryV1, type WalletId, type WalletKind, type WalletLifetimeTotalsV1, type WalletOwnerType, type WalletStatus, type WalletV1, applyTransactionToProjection, assertActivityEntry, assertBalancedTransaction, assertDistinctAdjustmentApproval, assertEconomyIdentifier, assertFlatTokenCatalog, assertFxSnapshot, assertGameplayAllocation, assertNonNegativeAccounts, assertOpenPurchaseIntent, assertPurchaseIntentBinding, assertPurchaseLimitPolicy, assertReversalAvailable, assertRewardConversion, assertRewardRate, assertSourceLot, assertSpendRequest, assertTokenPack, assertTokenReferenceRate, assertTokenSubscriptionPlan, assertWallet, assertWalletBalanceSummary, assertWalletLifetimeTotals, availableSourceLotAmount, boostGameplayAllocation, canUseSourceLot, canonicalTransactionPayload, convertRewardPayout, createGameplayAllocation, createProjectionSnapshot, createReversalTransaction, economyAssert, evaluateEarlyBacker, isWholeTokenAmount, mapBitLabsConversionState, parseIsoTimestamp, parseTokenSubunits, rebuildBalanceProjection, reclaimGameplayAllocation, selectSourceLots, serializeTokenSubunits, sortStringRecord, subunitsToWholeTokens, sumTokenSubunits, toEconomyErrorEnvelope, wholeTokensToSubunits };
|