@carrot-foundation/schemas 0.5.1 → 2.0.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/dist/index.js CHANGED
@@ -28136,12 +28136,11 @@ var StringifiedTokenIdSchema = NonEmptyStringSchema.regex(
28136
28136
  examples: ["#456789", "#1000000"],
28137
28137
  example: "#456789"
28138
28138
  });
28139
- var Sha256HashSchema = z.hash("sha256", {
28140
- error: "Must be a SHA256 hash as 32-byte hex string"
28139
+ var Sha256HashSchema = z.string().regex(/^[a-f0-9]{64}$/, {
28140
+ error: "Must be a SHA-256 hash as 32-byte lowercase hex string (no 0x prefix)"
28141
28141
  }).meta({
28142
- format: void 0,
28143
28142
  title: "SHA-256 Hash",
28144
- description: "SHA-256 cryptographic hash as hexadecimal string",
28143
+ description: "SHA-256 cryptographic hash as canonical lowercase hexadecimal string",
28145
28144
  examples: [
28146
28145
  "87f633634cc4b02f628685651f0a29b7bfa22a0bd841f725c6772dd00a58d489"
28147
28146
  ]
@@ -29383,9 +29382,9 @@ var CreditPurchaseReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29383
29382
  title: "Total Amount (USDC)",
29384
29383
  description: "Total amount paid in USDC stablecoin for the credit purchase"
29385
29384
  }),
29386
- total_credits: CreditAmountSchema.meta({
29385
+ total_credits: CreditAmountSchema.gt(0).meta({
29387
29386
  title: "Total Credits",
29388
- description: "Total number of environmental impact credits purchased in this transaction"
29387
+ description: "Total number of environmental impact credits purchased in this transaction. Must be greater than 0."
29389
29388
  }),
29390
29389
  purchased_at: IsoDateTimeSchema.meta({
29391
29390
  title: "Purchased At",
@@ -29396,9 +29395,9 @@ var CreditPurchaseReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29396
29395
  description: "Summary totals for the credit purchase including payment amount, credit quantity, certificate count, and timestamp"
29397
29396
  });
29398
29397
  var CreditRetirementReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29399
- total_credits_retired: CreditAmountSchema.meta({
29398
+ total_credits_retired: CreditAmountSchema.gt(0).meta({
29400
29399
  title: "Total Credits Retired",
29401
- description: "Total number of environmental impact credits permanently retired (removed from circulation)"
29400
+ description: "Total number of environmental impact credits permanently retired (removed from circulation). Must be greater than 0."
29402
29401
  }),
29403
29402
  retired_at: IsoDateTimeSchema.meta({
29404
29403
  title: "Retired At",
@@ -29485,7 +29484,7 @@ function buildSchemaUrl(schemaPath) {
29485
29484
  return `${getSchemaBaseUrl()}/${cleanPath}`;
29486
29485
  }
29487
29486
  function getSchemaVersionOrDefault() {
29488
- return "0.5.1";
29487
+ return "2.0.0";
29489
29488
  }
29490
29489
 
29491
29490
  // src/shared/schema-validation.ts
@@ -31080,18 +31079,18 @@ var CreditPurchaseReceiptAttributesSchema = createOrderedAttributesSchema({
31080
31079
  });
31081
31080
  var CreditPurchaseReceiptIdentitySchema = ReceiptIdentitySchema;
31082
31081
  var CreditPurchaseReceiptBuyerSchema = z.strictObject({
31083
- wallet_address: EthereumAddressSchema.meta({
31082
+ id_hash: Sha256HashSchema.meta({
31083
+ title: "Buyer ID Hash",
31084
+ description: "Anonymized pseudonymous identifier linking this buyer to off-chain records via a keyed hash"
31085
+ }),
31086
+ wallet_address: EthereumAddressSchema.optional().meta({
31084
31087
  title: "Buyer Wallet Address",
31085
31088
  description: "Ethereum address receiving the credits"
31086
31089
  }),
31087
- id: ExternalIdSchema.optional().meta({
31088
- title: "Buyer ID",
31089
- description: "Unique identifier for the buyer"
31090
- }),
31091
31090
  identity: CreditPurchaseReceiptIdentitySchema.optional()
31092
31091
  }).meta({
31093
31092
  title: "Buyer",
31094
- description: "Buyer information including wallet address, optional ID, and optional identity"
31093
+ description: "Buyer information including hashed identifier, optional wallet address, and optional identity"
31095
31094
  });
31096
31095
  var CreditPurchaseReceiptCollectionSchema = createReceiptCollectionSchema({
31097
31096
  meta: {
@@ -31107,13 +31106,17 @@ var CreditPurchaseReceiptCertificateSchema = CertificateReferenceBaseSchema.safe
31107
31106
  credit_slug: CreditTokenSlugSchema.meta({
31108
31107
  description: "Slug of the credit type for this certificate"
31109
31108
  }),
31109
+ purchased_amount: CreditAmountSchema.meta({
31110
+ title: "Certificate Purchased Amount",
31111
+ description: "Total credits purchased from this certificate. Authoritative source for receipt totals; cross-checked against sum(collections[].purchased_amount) when collections are non-empty."
31112
+ }),
31110
31113
  collections: uniqueBy(
31111
31114
  CertificateCollectionItemPurchaseSchema,
31112
31115
  (item) => item.slug,
31113
31116
  "Collection slugs within certificate collections must be unique"
31114
- ).min(1).meta({
31117
+ ).meta({
31115
31118
  title: "Certificate Collections",
31116
- description: "Collections associated with this certificate, each with purchased and retired amounts"
31119
+ description: "Collections associated with this certificate, each with purchased and retired amounts. May be empty when this certificate is not assigned to any collection."
31117
31120
  })
31118
31121
  }).meta({
31119
31122
  title: "Certificate",
@@ -31127,9 +31130,9 @@ var CreditPurchaseReceiptDataSchema = z.strictObject({
31127
31130
  CreditPurchaseReceiptCollectionSchema,
31128
31131
  (collection) => collection.slug,
31129
31132
  "Collection slugs must be unique"
31130
- ).min(1).meta({
31133
+ ).meta({
31131
31134
  title: "Collections",
31132
- description: "Impact collections referenced by this purchase, each identified by a unique slug"
31135
+ description: "Impact collections referenced by this purchase, each identified by a unique slug. May be empty when no certificate is assigned to a collection."
31133
31136
  }),
31134
31137
  credits: uniqueBy(
31135
31138
  CreditPurchaseReceiptCreditSchema,
@@ -31159,12 +31162,10 @@ var CreditPurchaseReceiptDataSchema = z.strictObject({
31159
31162
  const collectionSlugs = new Set(
31160
31163
  data.collections.map((collection) => String(collection.slug))
31161
31164
  );
31165
+ const referencedCollectionSlugs = /* @__PURE__ */ new Set();
31162
31166
  const creditSlugs = new Set(
31163
31167
  data.credits.map((credit) => String(credit.slug))
31164
31168
  );
31165
- const creditPurchaseTotalsBySlug = /* @__PURE__ */ new Map();
31166
- const creditRetiredTotalsBySlug = /* @__PURE__ */ new Map();
31167
- const collectionPurchasedTotalsBySlug = /* @__PURE__ */ new Map();
31168
31169
  const collectionRetiredTotalsBySlug = /* @__PURE__ */ new Map();
31169
31170
  let totalCreditsFromCertificates = 0;
31170
31171
  data.certificates.forEach((certificate, index) => {
@@ -31175,7 +31176,6 @@ var CreditPurchaseReceiptDataSchema = z.strictObject({
31175
31176
  path: ["certificates", index, "credit_slug"]
31176
31177
  });
31177
31178
  let certificatePurchasedTotal = 0;
31178
- let certificateRetiredTotal = 0;
31179
31179
  validateCertificateCollectionSlugs({
31180
31180
  ctx,
31181
31181
  certificateCollections: certificate.collections,
@@ -31183,6 +31183,7 @@ var CreditPurchaseReceiptDataSchema = z.strictObject({
31183
31183
  certificateIndex: index
31184
31184
  });
31185
31185
  certificate.collections.forEach((collectionItem, collectionIndex) => {
31186
+ referencedCollectionSlugs.add(String(collectionItem.slug));
31186
31187
  if (collectionItem.retired_amount > collectionItem.purchased_amount) {
31187
31188
  ctx.addIssue({
31188
31189
  code: "custom",
@@ -31197,42 +31198,45 @@ var CreditPurchaseReceiptDataSchema = z.strictObject({
31197
31198
  });
31198
31199
  }
31199
31200
  certificatePurchasedTotal += Number(collectionItem.purchased_amount);
31200
- certificateRetiredTotal += Number(collectionItem.retired_amount);
31201
- collectionPurchasedTotalsBySlug.set(
31202
- collectionItem.slug,
31203
- (collectionPurchasedTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.purchased_amount)
31204
- );
31205
31201
  collectionRetiredTotalsBySlug.set(
31206
31202
  collectionItem.slug,
31207
31203
  (collectionRetiredTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.retired_amount)
31208
31204
  );
31209
31205
  });
31210
- if (certificatePurchasedTotal > certificate.total_amount) {
31206
+ if (certificate.purchased_amount > certificate.total_amount) {
31211
31207
  ctx.addIssue({
31212
31208
  code: "custom",
31213
- message: "Sum of certificate.collections[].purchased_amount cannot exceed certificate.total_amount",
31214
- path: ["certificates", index]
31209
+ message: "certificate.purchased_amount cannot exceed certificate.total_amount",
31210
+ path: ["certificates", index, "purchased_amount"]
31215
31211
  });
31216
31212
  }
31217
- totalCreditsFromCertificates += certificatePurchasedTotal;
31218
- creditPurchaseTotalsBySlug.set(
31219
- String(certificate.credit_slug),
31220
- (creditPurchaseTotalsBySlug.get(certificate.credit_slug) ?? 0) + certificatePurchasedTotal
31221
- );
31222
- creditRetiredTotalsBySlug.set(
31223
- String(certificate.credit_slug),
31224
- (creditRetiredTotalsBySlug.get(certificate.credit_slug) ?? 0) + certificateRetiredTotal
31225
- );
31213
+ if (certificate.collections.length > 0 && !nearlyEqual(certificatePurchasedTotal, certificate.purchased_amount)) {
31214
+ ctx.addIssue({
31215
+ code: "custom",
31216
+ message: "certificate.purchased_amount must equal sum of collections[].purchased_amount when collections are present",
31217
+ path: ["certificates", index, "purchased_amount"]
31218
+ });
31219
+ }
31220
+ totalCreditsFromCertificates += Number(certificate.purchased_amount);
31226
31221
  });
31227
31222
  const certificateCollectionRetiredTotal = Array.from(
31228
31223
  collectionRetiredTotalsBySlug.values()
31229
31224
  ).reduce((sum, amount) => sum + amount, 0);
31225
+ data.collections.forEach((collection, collectionIndex) => {
31226
+ if (!referencedCollectionSlugs.has(String(collection.slug))) {
31227
+ ctx.addIssue({
31228
+ code: "custom",
31229
+ message: "collections must only include slugs referenced by certificates[].collections",
31230
+ path: ["collections", collectionIndex, "slug"]
31231
+ });
31232
+ }
31233
+ });
31230
31234
  validateTotalMatches({
31231
31235
  ctx,
31232
31236
  actualTotal: totalCreditsFromCertificates,
31233
31237
  expectedTotal: data.summary.total_credits,
31234
31238
  path: ["summary", "total_credits"],
31235
- message: "summary.total_credits must equal sum of certificate.collections[].purchased_amount"
31239
+ message: "summary.total_credits must equal sum of certificates[].purchased_amount"
31236
31240
  });
31237
31241
  validateRetirementReceiptRequirement({
31238
31242
  ctx,
@@ -31245,7 +31249,7 @@ var CreditPurchaseReceiptDataSchema = z.strictObject({
31245
31249
  });
31246
31250
  var CreditPurchaseReceiptIpfsSchemaMeta = {
31247
31251
  title: "CreditPurchaseReceipt NFT IPFS Record",
31248
- description: "Complete CreditPurchaseReceipt NFT IPFS record including purchase summary, buyer details, credit breakdowns, certificate allocations, and NFT display attributes",
31252
+ description: "Complete CreditPurchaseReceipt NFT IPFS record including purchase summary, buyer details, credit breakdowns, certificate allocations, and NFT display attributes. Supports both collection-assigned and no-collection variants.",
31249
31253
  $id: buildSchemaUrl(
31250
31254
  "credit-purchase-receipt/credit-purchase-receipt.schema.json"
31251
31255
  ),
@@ -31383,14 +31387,10 @@ var CreditPurchaseReceiptIpfsSchema = NftIpfsSchema.safeExtend({
31383
31387
  });
31384
31388
  return;
31385
31389
  }
31386
- const certificatePurchasedTotal = certificate.collections.reduce(
31387
- (sum, collection) => sum + Number(collection.purchased_amount),
31388
- 0
31389
- );
31390
31390
  const currentTotal = creditTotalsBySymbol.get(credit.symbol) ?? 0;
31391
31391
  creditTotalsBySymbol.set(
31392
31392
  credit.symbol,
31393
- currentTotal + certificatePurchasedTotal
31393
+ currentTotal + Number(certificate.purchased_amount)
31394
31394
  );
31395
31395
  });
31396
31396
  data.credits.forEach((credit) => {
@@ -31405,7 +31405,7 @@ var CreditPurchaseReceiptIpfsSchema = NftIpfsSchema.safeExtend({
31405
31405
  } else if (Number(attribute.value) !== expectedTotal) {
31406
31406
  ctx.addIssue({
31407
31407
  code: "custom",
31408
- message: `Attribute for credit symbol ${credit.symbol} must match sum of certificate.collections[].purchased_amount for the credit symbol`,
31408
+ message: `Attribute for credit symbol ${credit.symbol} must match sum of certificates[].purchased_amount for the credit symbol`,
31409
31409
  path: ["attributes"]
31410
31410
  });
31411
31411
  }
@@ -31507,24 +31507,32 @@ var CreditRetirementReceiptAttributesSchema = createOrderedAttributesSchema({
31507
31507
  });
31508
31508
  var CreditRetirementReceiptIdentitySchema = ReceiptIdentitySchema;
31509
31509
  var CreditRetirementReceiptBeneficiarySchema = z.strictObject({
31510
- beneficiary_id: ExternalIdSchema.meta({
31511
- title: "Retirement Beneficiary ID",
31512
- description: "UUID identifying the beneficiary of the retirement within the Carrot platform"
31510
+ id_hash: Sha256HashSchema.meta({
31511
+ title: "Beneficiary ID Hash",
31512
+ description: "Anonymized pseudonymous identifier linking this beneficiary to off-chain records via a keyed hash"
31513
+ }),
31514
+ wallet_address: EthereumAddressSchema.optional().meta({
31515
+ title: "Beneficiary Wallet Address",
31516
+ description: "Ethereum address associated with the beneficiary, when available"
31513
31517
  }),
31514
31518
  identity: CreditRetirementReceiptIdentitySchema.optional()
31515
31519
  }).meta({
31516
31520
  title: "Beneficiary",
31517
- description: "Beneficiary receiving the retirement benefit"
31521
+ description: "Beneficiary receiving the retirement benefit, identified by a hashed identifier with optional wallet address and identity"
31518
31522
  });
31519
31523
  var CreditRetirementReceiptCreditHolderSchema = z.strictObject({
31520
- wallet_address: EthereumAddressSchema.meta({
31524
+ id_hash: Sha256HashSchema.meta({
31525
+ title: "Credit Holder ID Hash",
31526
+ description: "Anonymized pseudonymous identifier linking this credit holder to off-chain records via a keyed hash"
31527
+ }),
31528
+ wallet_address: EthereumAddressSchema.optional().meta({
31521
31529
  title: "Credit Holder Wallet Address",
31522
31530
  description: "Ethereum address of the wallet that held and surrendered the credits"
31523
31531
  }),
31524
31532
  identity: CreditRetirementReceiptIdentitySchema.optional()
31525
31533
  }).meta({
31526
31534
  title: "Credit Holder",
31527
- description: "Credit holder wallet and optional identity information"
31535
+ description: "Credit holder information including hashed identifier, optional wallet address, and optional identity"
31528
31536
  });
31529
31537
  var CreditRetirementReceiptCollectionSchema = createReceiptCollectionSchema({
31530
31538
  meta: {
@@ -31564,9 +31572,9 @@ var CreditRetirementReceiptCertificateSchema = CertificateReferenceBaseSchema.sa
31564
31572
  CertificateCollectionItemRetirementSchema,
31565
31573
  (item) => item.slug,
31566
31574
  "Collection slugs within certificate collections must be unique"
31567
- ).min(1).meta({
31575
+ ).meta({
31568
31576
  title: "Certificate Collections",
31569
- description: "Collections associated with this certificate, each with retired amounts"
31577
+ description: "Collections associated with this certificate, each with retired amounts. May be empty when this certificate is not assigned to any collection."
31570
31578
  }),
31571
31579
  credits_retired: uniqueBy(
31572
31580
  CreditRetirementReceiptCertificateCreditSchema,
@@ -31588,9 +31596,9 @@ var CreditRetirementReceiptDataSchema = z.strictObject({
31588
31596
  CreditRetirementReceiptCollectionSchema,
31589
31597
  (collection) => collection.slug,
31590
31598
  "Collection slugs must be unique"
31591
- ).min(1).meta({
31599
+ ).meta({
31592
31600
  title: "Collections",
31593
- description: "Impact collections referenced by this retirement, each identified by a unique slug"
31601
+ description: "Impact collections referenced by this retirement, each identified by a unique slug. May be empty when no certificate is assigned to a collection."
31594
31602
  }),
31595
31603
  credits: uniqueBy(
31596
31604
  CreditRetirementReceiptCreditSchema,
@@ -31644,6 +31652,17 @@ var CreditRetirementReceiptDataSchema = z.strictObject({
31644
31652
  path: ["certificates", index]
31645
31653
  });
31646
31654
  }
31655
+ const creditsRetiredTotal = certificate.credits_retired.reduce(
31656
+ (sum, credit) => sum + Number(credit.amount),
31657
+ 0
31658
+ );
31659
+ if (creditsRetiredTotal > certificate.total_amount) {
31660
+ ctx.addIssue({
31661
+ code: "custom",
31662
+ message: "Sum of certificate.credits_retired[].amount cannot exceed certificate.total_amount",
31663
+ path: ["certificates", index, "credits_retired"]
31664
+ });
31665
+ }
31647
31666
  validateCertificateCollectionSlugs({
31648
31667
  ctx,
31649
31668
  certificateCollections: certificate.collections,
@@ -31656,11 +31675,7 @@ var CreditRetirementReceiptDataSchema = z.strictObject({
31656
31675
  (collectionRetiredTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.retired_amount)
31657
31676
  );
31658
31677
  });
31659
- const creditsRetiredTotal = certificate.credits_retired.reduce(
31660
- (sum, credit) => sum + Number(credit.amount),
31661
- 0
31662
- );
31663
- if (!nearlyEqual(creditsRetiredTotal, certificateCollectionRetiredTotal)) {
31678
+ if (certificate.collections.length > 0 && !nearlyEqual(creditsRetiredTotal, certificateCollectionRetiredTotal)) {
31664
31679
  ctx.addIssue({
31665
31680
  code: "custom",
31666
31681
  message: "certificates.credits_retired amounts must sum to certificate.collections[].retired_amount",
@@ -31711,27 +31726,29 @@ var CreditRetirementReceiptDataSchema = z.strictObject({
31711
31726
  (creditTotalsBySymbol.get(credit.credit_symbol) ?? 0) + credit.amount
31712
31727
  );
31713
31728
  });
31714
- totalRetiredFromCertificates += certificateCollectionRetiredTotal;
31729
+ totalRetiredFromCertificates += creditsRetiredTotal;
31715
31730
  });
31716
31731
  validateTotalMatches({
31717
31732
  ctx,
31718
31733
  actualTotal: totalRetiredFromCertificates,
31719
31734
  expectedTotal: data.summary.total_credits_retired,
31720
31735
  path: ["summary", "total_credits_retired"],
31721
- message: "summary.total_credits_retired must equal sum of certificate.collections[].retired_amount"
31722
- });
31723
- validateCollectionsHaveRetiredAmounts({
31724
- ctx,
31725
- collections: data.collections,
31726
- retiredTotalsBySlug: collectionRetiredTotalsBySlug
31736
+ message: "summary.total_credits_retired must equal sum of certificates[].credits_retired[].amount"
31727
31737
  });
31738
+ if (data.collections.length > 0) {
31739
+ validateCollectionsHaveRetiredAmounts({
31740
+ ctx,
31741
+ collections: data.collections,
31742
+ retiredTotalsBySlug: collectionRetiredTotalsBySlug
31743
+ });
31744
+ }
31728
31745
  }).meta({
31729
31746
  title: "Credit Retirement Receipt Data",
31730
31747
  description: "Complete data structure for a credit retirement receipt"
31731
31748
  });
31732
31749
  var CreditRetirementReceiptIpfsSchemaMeta = {
31733
31750
  title: "CreditRetirementReceipt NFT IPFS Record",
31734
- description: "Complete CreditRetirementReceipt NFT IPFS record including retirement summary, beneficiary and credit holder details (identity optional), credit breakdowns, certificate allocations, and NFT display attributes",
31751
+ description: "Complete CreditRetirementReceipt NFT IPFS record including retirement summary, beneficiary and credit holder details (identity optional), credit breakdowns, certificate allocations, and NFT display attributes. Supports both collection-assigned and no-collection variants.",
31735
31752
  $id: buildSchemaUrl(
31736
31753
  "credit-retirement-receipt/credit-retirement-receipt.schema.json"
31737
31754
  ),