@plasius/economy 0.5.0 → 0.6.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/README.md CHANGED
@@ -399,6 +399,21 @@ across wallets, even if the sum reaches 1,000 TokenSubunits, because source and
399
399
  transfer restrictions may differ. Authorization must happen before the server
400
400
  constructs a portfolio scope.
401
401
 
402
+ `createPersonalWalletInitializationState()` supplies the exact baseline needed
403
+ before a self account can read a portfolio: deterministic document IDs, one
404
+ account-owned personal `WalletV1`, a single-component scope, exact-zero balance
405
+ and lifetime projections, no activity entries, and a privacy-minimized
406
+ `wallet.initialized.v1` outbox intent. The caller provides SHA-256 and must
407
+ commit the state with audited receipts, idempotency, the authority manifest and
408
+ conditional head advance in one ACID boundary.
409
+
410
+ `initialize-wallet` exists only in `EconomyAuditedCommandTypeV1`; legacy V1/V2
411
+ command unions remain unchanged. It accepts a direct self-account browser
412
+ principal only and terminates with `WALLET_INITIALIZED` or
413
+ `WALLET_ALREADY_INITIALIZED` as a no-economic result. It never creates a
414
+ double-entry transaction or Token activity row and does not authorize value or
415
+ provider writes.
416
+
402
417
  Activity reads use bounded opaque cursor pagination in stable descending
403
418
  `(occurredAt, activityId)` order. Each row retains the component `walletId`
404
419
  whose signed display amount it represents. Cursor contents and signatures are
package/dist/index.cjs CHANGED
@@ -103,6 +103,9 @@ __export(index_exports, {
103
103
  assertPaidLotLifecycle: () => assertPaidLotLifecycle,
104
104
  assertPaidLotLifecycleEvent: () => assertPaidLotLifecycleEvent,
105
105
  assertPaidLotPurchaseProvenance: () => assertPaidLotPurchaseProvenance,
106
+ assertPersonalWalletInitializationCommand: () => assertPersonalWalletInitializationCommand,
107
+ assertPersonalWalletInitializationOutboxIntent: () => assertPersonalWalletInitializationOutboxIntent,
108
+ assertPersonalWalletInitializationState: () => assertPersonalWalletInitializationState,
106
109
  assertPurchaseCapReservation: () => assertPurchaseCapReservation,
107
110
  assertPurchaseCapReservationTransition: () => assertPurchaseCapReservationTransition,
108
111
  assertPurchaseIntentBinding: () => assertPurchaseIntentBinding,
@@ -160,6 +163,8 @@ __export(index_exports, {
160
163
  canonicalEconomyRegionalEvidenceReceiptBodyPayload: () => canonicalEconomyRegionalEvidenceReceiptBodyPayload,
161
164
  canonicalEconomyRegionalEvidenceReceiptPayload: () => canonicalEconomyRegionalEvidenceReceiptPayload,
162
165
  canonicalEconomyRegionalEvidenceReceiptSigningPayload: () => canonicalEconomyRegionalEvidenceReceiptSigningPayload,
166
+ canonicalPersonalWalletInitializationCommandPayload: () => canonicalPersonalWalletInitializationCommandPayload,
167
+ canonicalPersonalWalletInitializationOutboxPayload: () => canonicalPersonalWalletInitializationOutboxPayload,
163
168
  canonicalTransactionPayload: () => canonicalTransactionPayload,
164
169
  closeModuleAllowance: () => closeModuleAllowance,
165
170
  convertRewardPayout: () => convertRewardPayout,
@@ -175,6 +180,8 @@ __export(index_exports, {
175
180
  createModuleSpendHold: () => createModuleSpendHold,
176
181
  createModuleSpendQuote: () => createModuleSpendQuote,
177
182
  createPaidLotLifecycle: () => createPaidLotLifecycle,
183
+ createPersonalWalletInitializationDocumentIds: () => createPersonalWalletInitializationDocumentIds,
184
+ createPersonalWalletInitializationState: () => createPersonalWalletInitializationState,
178
185
  createProjectionSnapshot: () => createProjectionSnapshot,
179
186
  createPurchaseIntentLifecycle: () => createPurchaseIntentLifecycle,
180
187
  createReversalTransaction: () => createReversalTransaction,
@@ -965,7 +972,8 @@ var COMMAND_TYPES = /* @__PURE__ */ new Set([
965
972
  "refund",
966
973
  "chargeback",
967
974
  "reverse",
968
- "adjust"
975
+ "adjust",
976
+ "initialize-wallet"
969
977
  ]);
970
978
  var COMMAND_SOURCES = /* @__PURE__ */ new Set([
971
979
  "browser",
@@ -1012,7 +1020,8 @@ var SOURCE_COMMAND_TYPES = {
1012
1020
  "reclaim",
1013
1021
  "spend",
1014
1022
  "hold",
1015
- "release-hold"
1023
+ "release-hold",
1024
+ "initialize-wallet"
1016
1025
  ]),
1017
1026
  shopify: /* @__PURE__ */ new Set([
1018
1027
  "credit-purchase",
@@ -1207,6 +1216,13 @@ function assertCommandSourcePrincipal(envelope) {
1207
1216
  "Delegated-child commands require a distinct actor and relationship"
1208
1217
  );
1209
1218
  }
1219
+ if (envelope.commandType === "initialize-wallet") {
1220
+ economyAssert(
1221
+ envelope.commandSource === "browser" && envelope.principalType === "account" && envelope.actorAccountId === envelope.subjectAccountId && envelope.relationshipId === void 0 && envelope.authorizationVersion === void 0,
1222
+ "INVALID_CONTRACT",
1223
+ "Wallet initialization requires a direct self-account browser command"
1224
+ );
1225
+ }
1210
1226
  }
1211
1227
  function assertAuditedEconomyCommandEnvelope(envelope) {
1212
1228
  assertExactKeys(
@@ -1808,6 +1824,7 @@ var RECORD_KINDS = /* @__PURE__ */ new Set([
1808
1824
  "source-lot",
1809
1825
  "source-lot-movement",
1810
1826
  "gameplay-allocation",
1827
+ "wallet",
1811
1828
  "balance-projection",
1812
1829
  "lifetime-projection",
1813
1830
  "idempotency-result",
@@ -2995,6 +3012,13 @@ function assertReceiptGraph(graph, commandEnvelopeHash, hash, acceptanceCommit,
2995
3012
  result.receiptId,
2996
3013
  resultHash
2997
3014
  );
3015
+ if (envelope.commandType === "initialize-wallet") {
3016
+ economyAssert(
3017
+ result.outcome === "no-op" && (result.noOpCode === "WALLET_INITIALIZED" || result.noOpCode === "WALLET_ALREADY_INITIALIZED"),
3018
+ "INVALID_CONTRACT",
3019
+ "Wallet initialization must terminate without an economic transaction"
3020
+ );
3021
+ }
2998
3022
  if (result.outcome !== "completed") {
2999
3023
  economyAssert(
3000
3024
  graph.transaction === void 0,
@@ -8331,6 +8355,241 @@ function assertSpendRequest(request) {
8331
8355
  );
8332
8356
  }
8333
8357
  }
8358
+
8359
+ // src/wallet-initialization.ts
8360
+ var INITIALIZATION_IDENTITY_DOMAIN = "economy.personal-wallet-initialization.identity.v1";
8361
+ var ZERO_TOKEN_SUBUNITS = serializeTokenSubunits(0n);
8362
+ var STATE_KEYS = /* @__PURE__ */ new Set([
8363
+ "schemaVersion",
8364
+ "documentIds",
8365
+ "command",
8366
+ "wallet",
8367
+ "scope",
8368
+ "balance",
8369
+ "lifetime",
8370
+ "activityEntries",
8371
+ "outboxIntent"
8372
+ ]);
8373
+ var COMMAND_KEYS = /* @__PURE__ */ new Set([
8374
+ "schemaVersion",
8375
+ "commandType",
8376
+ "walletKind",
8377
+ "subjectAccountId",
8378
+ "walletId",
8379
+ "portfolioId",
8380
+ "initializedAt"
8381
+ ]);
8382
+ var OUTBOX_KEYS = /* @__PURE__ */ new Set([
8383
+ "schemaVersion",
8384
+ "eventType",
8385
+ "eventId",
8386
+ "subjectAccountId",
8387
+ "walletId",
8388
+ "portfolioId",
8389
+ "occurredAt"
8390
+ ]);
8391
+ var DOCUMENT_ID_KEYS = /* @__PURE__ */ new Set([
8392
+ "walletDescriptorId",
8393
+ "accessProjectionId",
8394
+ "balanceProjectionId",
8395
+ "lifetimeProjectionId",
8396
+ "outboxEventId"
8397
+ ]);
8398
+ function assertExactKeys4(value, allowed, label) {
8399
+ economyAssert(
8400
+ value !== null && typeof value === "object" && !Array.isArray(value),
8401
+ "INVALID_CONTRACT",
8402
+ `${label} must be an object`
8403
+ );
8404
+ economyAssert(
8405
+ Object.keys(value).every((key) => allowed.has(key)),
8406
+ "INVALID_CONTRACT",
8407
+ `${label} contains an unsupported field`
8408
+ );
8409
+ }
8410
+ function identityDigest(subjectAccountId, hash) {
8411
+ assertEconomyIdentifier(subjectAccountId, "subjectAccountId");
8412
+ const canonicalIdentity = JSON.stringify({
8413
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8414
+ domain: INITIALIZATION_IDENTITY_DOMAIN,
8415
+ subjectAccountId
8416
+ });
8417
+ const canonicalHash2 = hash(canonicalIdentity);
8418
+ economyAssert(
8419
+ /^sha256:[a-f0-9]{64}$/u.test(canonicalHash2),
8420
+ "INVALID_CONTRACT",
8421
+ "Wallet initialization identity hash must be canonical SHA-256"
8422
+ );
8423
+ return canonicalHash2.slice("sha256:".length);
8424
+ }
8425
+ function createPersonalWalletInitializationDocumentIds(subjectAccountId, hash) {
8426
+ const digest = identityDigest(subjectAccountId, hash);
8427
+ return {
8428
+ walletId: `wallet:personal:${digest}`,
8429
+ portfolioId: `portfolio:self:${digest}`,
8430
+ walletDescriptorId: `wallet-descriptor:${digest}`,
8431
+ accessProjectionId: `wallet-access:${digest}`,
8432
+ balanceProjectionId: `wallet-balance:${digest}`,
8433
+ lifetimeProjectionId: `wallet-lifetime:${digest}`,
8434
+ outboxEventId: `outbox:wallet-initialized:${digest}`
8435
+ };
8436
+ }
8437
+ function assertPersonalWalletInitializationCommand(command) {
8438
+ assertExactKeys4(command, COMMAND_KEYS, "Wallet initialization command");
8439
+ economyAssert(
8440
+ command.schemaVersion === ECONOMY_CONTRACT_VERSION && command.commandType === "initialize-wallet" && command.walletKind === "personal",
8441
+ "INVALID_CONTRACT",
8442
+ "Unsupported personal-wallet initialization command"
8443
+ );
8444
+ assertEconomyIdentifier(command.subjectAccountId, "subjectAccountId");
8445
+ assertEconomyIdentifier(command.walletId, "walletId");
8446
+ assertEconomyIdentifier(command.portfolioId, "portfolioId");
8447
+ parseIsoTimestamp(command.initializedAt);
8448
+ }
8449
+ function canonicalPersonalWalletInitializationCommandPayload(command) {
8450
+ assertPersonalWalletInitializationCommand(command);
8451
+ return JSON.stringify({
8452
+ schemaVersion: command.schemaVersion,
8453
+ commandType: command.commandType,
8454
+ walletKind: command.walletKind,
8455
+ subjectAccountId: command.subjectAccountId,
8456
+ walletId: command.walletId,
8457
+ portfolioId: command.portfolioId,
8458
+ initializedAt: command.initializedAt
8459
+ });
8460
+ }
8461
+ function assertPersonalWalletInitializationOutboxIntent(intent) {
8462
+ assertExactKeys4(intent, OUTBOX_KEYS, "Wallet initialization outbox intent");
8463
+ economyAssert(
8464
+ intent.schemaVersion === ECONOMY_CONTRACT_VERSION && intent.eventType === "wallet.initialized.v1",
8465
+ "INVALID_CONTRACT",
8466
+ "Unsupported wallet initialization outbox intent"
8467
+ );
8468
+ assertEconomyIdentifier(intent.eventId, "eventId");
8469
+ assertEconomyIdentifier(intent.subjectAccountId, "subjectAccountId");
8470
+ assertEconomyIdentifier(intent.walletId, "walletId");
8471
+ assertEconomyIdentifier(intent.portfolioId, "portfolioId");
8472
+ parseIsoTimestamp(intent.occurredAt);
8473
+ }
8474
+ function canonicalPersonalWalletInitializationOutboxPayload(intent) {
8475
+ assertPersonalWalletInitializationOutboxIntent(intent);
8476
+ return JSON.stringify({
8477
+ schemaVersion: intent.schemaVersion,
8478
+ eventType: intent.eventType,
8479
+ eventId: intent.eventId,
8480
+ subjectAccountId: intent.subjectAccountId,
8481
+ walletId: intent.walletId,
8482
+ portfolioId: intent.portfolioId,
8483
+ occurredAt: intent.occurredAt
8484
+ });
8485
+ }
8486
+ function createPersonalWalletInitializationState(input, hash) {
8487
+ parseIsoTimestamp(input.initializedAt);
8488
+ const { walletId, portfolioId, ...documentIds } = createPersonalWalletInitializationDocumentIds(
8489
+ input.subjectAccountId,
8490
+ hash
8491
+ );
8492
+ const command = {
8493
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8494
+ commandType: "initialize-wallet",
8495
+ walletKind: "personal",
8496
+ subjectAccountId: input.subjectAccountId,
8497
+ walletId,
8498
+ portfolioId,
8499
+ initializedAt: input.initializedAt
8500
+ };
8501
+ const state = {
8502
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8503
+ documentIds,
8504
+ command,
8505
+ wallet: {
8506
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8507
+ walletId,
8508
+ accountId: input.subjectAccountId,
8509
+ kind: "personal",
8510
+ ownerType: "account",
8511
+ ownerId: input.subjectAccountId,
8512
+ status: "active",
8513
+ version: 1,
8514
+ createdAt: input.initializedAt
8515
+ },
8516
+ scope: {
8517
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8518
+ portfolioId,
8519
+ subjectAccountId: input.subjectAccountId,
8520
+ components: [{ walletId, role: "personal" }]
8521
+ },
8522
+ balance: {
8523
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8524
+ walletId,
8525
+ available: ZERO_TOKEN_SUBUNITS,
8526
+ reserved: ZERO_TOKEN_SUBUNITS,
8527
+ held: ZERO_TOKEN_SUBUNITS,
8528
+ rewardProgress: ZERO_TOKEN_SUBUNITS,
8529
+ version: 1,
8530
+ asOf: input.initializedAt
8531
+ },
8532
+ lifetime: {
8533
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8534
+ walletId,
8535
+ totals: {
8536
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8537
+ bought: ZERO_TOKEN_SUBUNITS,
8538
+ earned: ZERO_TOKEN_SUBUNITS,
8539
+ allocated: ZERO_TOKEN_SUBUNITS,
8540
+ reclaimed: ZERO_TOKEN_SUBUNITS,
8541
+ spent: ZERO_TOKEN_SUBUNITS,
8542
+ reversed: ZERO_TOKEN_SUBUNITS
8543
+ },
8544
+ version: 1,
8545
+ asOf: input.initializedAt
8546
+ },
8547
+ activityEntries: [],
8548
+ outboxIntent: {
8549
+ schemaVersion: ECONOMY_CONTRACT_VERSION,
8550
+ eventType: "wallet.initialized.v1",
8551
+ eventId: documentIds.outboxEventId,
8552
+ subjectAccountId: input.subjectAccountId,
8553
+ walletId,
8554
+ portfolioId,
8555
+ occurredAt: input.initializedAt
8556
+ }
8557
+ };
8558
+ assertPersonalWalletInitializationState(state);
8559
+ return state;
8560
+ }
8561
+ function assertPersonalWalletInitializationState(state) {
8562
+ assertExactKeys4(state, STATE_KEYS, "Wallet initialization state");
8563
+ assertExactKeys4(
8564
+ state.documentIds,
8565
+ DOCUMENT_ID_KEYS,
8566
+ "Wallet initialization document IDs"
8567
+ );
8568
+ economyAssert(
8569
+ state.schemaVersion === ECONOMY_CONTRACT_VERSION,
8570
+ "INVALID_CONTRACT",
8571
+ "Unsupported wallet initialization state"
8572
+ );
8573
+ for (const identifier of Object.values(state.documentIds)) {
8574
+ assertEconomyIdentifier(identifier, "wallet initialization document ID");
8575
+ }
8576
+ economyAssert(
8577
+ new Set(Object.values(state.documentIds)).size === Object.values(state.documentIds).length,
8578
+ "INVALID_CONTRACT",
8579
+ "Wallet initialization document IDs must be unique"
8580
+ );
8581
+ assertPersonalWalletInitializationCommand(state.command);
8582
+ assertWallet(state.wallet);
8583
+ assertWalletPortfolioReadScope(state.scope);
8584
+ assertWalletBalanceSummary(state.balance);
8585
+ assertWalletLifetimeSnapshot(state.lifetime);
8586
+ assertPersonalWalletInitializationOutboxIntent(state.outboxIntent);
8587
+ economyAssert(
8588
+ state.wallet.walletId === state.command.walletId && state.wallet.accountId === state.command.subjectAccountId && state.wallet.ownerId === state.command.subjectAccountId && state.wallet.kind === "personal" && state.wallet.ownerType === "account" && state.wallet.createdAt === state.command.initializedAt && state.scope.portfolioId === state.command.portfolioId && state.scope.subjectAccountId === state.command.subjectAccountId && state.scope.components.length === 1 && state.scope.components[0]?.walletId === state.command.walletId && state.scope.components[0]?.role === "personal" && state.scope.components[0]?.beneficiaryAccountId === void 0 && state.balance.walletId === state.command.walletId && state.balance.available === ZERO_TOKEN_SUBUNITS && state.balance.reserved === ZERO_TOKEN_SUBUNITS && state.balance.held === ZERO_TOKEN_SUBUNITS && state.balance.rewardProgress === ZERO_TOKEN_SUBUNITS && state.balance.version === 1 && state.balance.asOf === state.command.initializedAt && state.lifetime.walletId === state.command.walletId && state.lifetime.version === 1 && state.lifetime.asOf === state.command.initializedAt && state.lifetime.totals.bought === ZERO_TOKEN_SUBUNITS && state.lifetime.totals.earned === ZERO_TOKEN_SUBUNITS && state.lifetime.totals.allocated === ZERO_TOKEN_SUBUNITS && state.lifetime.totals.reclaimed === ZERO_TOKEN_SUBUNITS && state.lifetime.totals.spent === ZERO_TOKEN_SUBUNITS && state.lifetime.totals.reversed === ZERO_TOKEN_SUBUNITS && state.activityEntries.length === 0 && state.outboxIntent.eventId === state.documentIds.outboxEventId && state.outboxIntent.subjectAccountId === state.command.subjectAccountId && state.outboxIntent.walletId === state.command.walletId && state.outboxIntent.portfolioId === state.command.portfolioId && state.outboxIntent.occurredAt === state.command.initializedAt,
8589
+ "INVALID_CONTRACT",
8590
+ "Personal-wallet initialization state is incomplete or inconsistent"
8591
+ );
8592
+ }
8334
8593
  // Annotate the CommonJS export names for ESM import in node:
8335
8594
  0 && (module.exports = {
8336
8595
  BASELINE_GBP_CATALOG_VERSION,
@@ -8416,6 +8675,9 @@ function assertSpendRequest(request) {
8416
8675
  assertPaidLotLifecycle,
8417
8676
  assertPaidLotLifecycleEvent,
8418
8677
  assertPaidLotPurchaseProvenance,
8678
+ assertPersonalWalletInitializationCommand,
8679
+ assertPersonalWalletInitializationOutboxIntent,
8680
+ assertPersonalWalletInitializationState,
8419
8681
  assertPurchaseCapReservation,
8420
8682
  assertPurchaseCapReservationTransition,
8421
8683
  assertPurchaseIntentBinding,
@@ -8473,6 +8735,8 @@ function assertSpendRequest(request) {
8473
8735
  canonicalEconomyRegionalEvidenceReceiptBodyPayload,
8474
8736
  canonicalEconomyRegionalEvidenceReceiptPayload,
8475
8737
  canonicalEconomyRegionalEvidenceReceiptSigningPayload,
8738
+ canonicalPersonalWalletInitializationCommandPayload,
8739
+ canonicalPersonalWalletInitializationOutboxPayload,
8476
8740
  canonicalTransactionPayload,
8477
8741
  closeModuleAllowance,
8478
8742
  convertRewardPayout,
@@ -8488,6 +8752,8 @@ function assertSpendRequest(request) {
8488
8752
  createModuleSpendHold,
8489
8753
  createModuleSpendQuote,
8490
8754
  createPaidLotLifecycle,
8755
+ createPersonalWalletInitializationDocumentIds,
8756
+ createPersonalWalletInitializationState,
8491
8757
  createProjectionSnapshot,
8492
8758
  createPurchaseIntentLifecycle,
8493
8759
  createReversalTransaction,