@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.cjs CHANGED
@@ -28142,12 +28142,11 @@ var StringifiedTokenIdSchema = NonEmptyStringSchema.regex(
28142
28142
  examples: ["#456789", "#1000000"],
28143
28143
  example: "#456789"
28144
28144
  });
28145
- var Sha256HashSchema = zod.z.hash("sha256", {
28146
- error: "Must be a SHA256 hash as 32-byte hex string"
28145
+ var Sha256HashSchema = zod.z.string().regex(/^[a-f0-9]{64}$/, {
28146
+ error: "Must be a SHA-256 hash as 32-byte lowercase hex string (no 0x prefix)"
28147
28147
  }).meta({
28148
- format: void 0,
28149
28148
  title: "SHA-256 Hash",
28150
- description: "SHA-256 cryptographic hash as hexadecimal string",
28149
+ description: "SHA-256 cryptographic hash as canonical lowercase hexadecimal string",
28151
28150
  examples: [
28152
28151
  "87f633634cc4b02f628685651f0a29b7bfa22a0bd841f725c6772dd00a58d489"
28153
28152
  ]
@@ -29389,9 +29388,9 @@ var CreditPurchaseReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29389
29388
  title: "Total Amount (USDC)",
29390
29389
  description: "Total amount paid in USDC stablecoin for the credit purchase"
29391
29390
  }),
29392
- total_credits: CreditAmountSchema.meta({
29391
+ total_credits: CreditAmountSchema.gt(0).meta({
29393
29392
  title: "Total Credits",
29394
- description: "Total number of environmental impact credits purchased in this transaction"
29393
+ description: "Total number of environmental impact credits purchased in this transaction. Must be greater than 0."
29395
29394
  }),
29396
29395
  purchased_at: IsoDateTimeSchema.meta({
29397
29396
  title: "Purchased At",
@@ -29402,9 +29401,9 @@ var CreditPurchaseReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29402
29401
  description: "Summary totals for the credit purchase including payment amount, credit quantity, certificate count, and timestamp"
29403
29402
  });
29404
29403
  var CreditRetirementReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29405
- total_credits_retired: CreditAmountSchema.meta({
29404
+ total_credits_retired: CreditAmountSchema.gt(0).meta({
29406
29405
  title: "Total Credits Retired",
29407
- description: "Total number of environmental impact credits permanently retired (removed from circulation)"
29406
+ description: "Total number of environmental impact credits permanently retired (removed from circulation). Must be greater than 0."
29408
29407
  }),
29409
29408
  retired_at: IsoDateTimeSchema.meta({
29410
29409
  title: "Retired At",
@@ -29491,7 +29490,7 @@ function buildSchemaUrl(schemaPath) {
29491
29490
  return `${getSchemaBaseUrl()}/${cleanPath}`;
29492
29491
  }
29493
29492
  function getSchemaVersionOrDefault() {
29494
- return "0.5.1";
29493
+ return "2.0.0";
29495
29494
  }
29496
29495
 
29497
29496
  // src/shared/schema-validation.ts
@@ -31086,18 +31085,18 @@ var CreditPurchaseReceiptAttributesSchema = createOrderedAttributesSchema({
31086
31085
  });
31087
31086
  var CreditPurchaseReceiptIdentitySchema = ReceiptIdentitySchema;
31088
31087
  var CreditPurchaseReceiptBuyerSchema = zod.z.strictObject({
31089
- wallet_address: EthereumAddressSchema.meta({
31088
+ id_hash: Sha256HashSchema.meta({
31089
+ title: "Buyer ID Hash",
31090
+ description: "Anonymized pseudonymous identifier linking this buyer to off-chain records via a keyed hash"
31091
+ }),
31092
+ wallet_address: EthereumAddressSchema.optional().meta({
31090
31093
  title: "Buyer Wallet Address",
31091
31094
  description: "Ethereum address receiving the credits"
31092
31095
  }),
31093
- id: ExternalIdSchema.optional().meta({
31094
- title: "Buyer ID",
31095
- description: "Unique identifier for the buyer"
31096
- }),
31097
31096
  identity: CreditPurchaseReceiptIdentitySchema.optional()
31098
31097
  }).meta({
31099
31098
  title: "Buyer",
31100
- description: "Buyer information including wallet address, optional ID, and optional identity"
31099
+ description: "Buyer information including hashed identifier, optional wallet address, and optional identity"
31101
31100
  });
31102
31101
  var CreditPurchaseReceiptCollectionSchema = createReceiptCollectionSchema({
31103
31102
  meta: {
@@ -31113,13 +31112,17 @@ var CreditPurchaseReceiptCertificateSchema = CertificateReferenceBaseSchema.safe
31113
31112
  credit_slug: CreditTokenSlugSchema.meta({
31114
31113
  description: "Slug of the credit type for this certificate"
31115
31114
  }),
31115
+ purchased_amount: CreditAmountSchema.meta({
31116
+ title: "Certificate Purchased Amount",
31117
+ description: "Total credits purchased from this certificate. Authoritative source for receipt totals; cross-checked against sum(collections[].purchased_amount) when collections are non-empty."
31118
+ }),
31116
31119
  collections: uniqueBy(
31117
31120
  CertificateCollectionItemPurchaseSchema,
31118
31121
  (item) => item.slug,
31119
31122
  "Collection slugs within certificate collections must be unique"
31120
- ).min(1).meta({
31123
+ ).meta({
31121
31124
  title: "Certificate Collections",
31122
- description: "Collections associated with this certificate, each with purchased and retired amounts"
31125
+ description: "Collections associated with this certificate, each with purchased and retired amounts. May be empty when this certificate is not assigned to any collection."
31123
31126
  })
31124
31127
  }).meta({
31125
31128
  title: "Certificate",
@@ -31133,9 +31136,9 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31133
31136
  CreditPurchaseReceiptCollectionSchema,
31134
31137
  (collection) => collection.slug,
31135
31138
  "Collection slugs must be unique"
31136
- ).min(1).meta({
31139
+ ).meta({
31137
31140
  title: "Collections",
31138
- description: "Impact collections referenced by this purchase, each identified by a unique slug"
31141
+ description: "Impact collections referenced by this purchase, each identified by a unique slug. May be empty when no certificate is assigned to a collection."
31139
31142
  }),
31140
31143
  credits: uniqueBy(
31141
31144
  CreditPurchaseReceiptCreditSchema,
@@ -31165,12 +31168,10 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31165
31168
  const collectionSlugs = new Set(
31166
31169
  data.collections.map((collection) => String(collection.slug))
31167
31170
  );
31171
+ const referencedCollectionSlugs = /* @__PURE__ */ new Set();
31168
31172
  const creditSlugs = new Set(
31169
31173
  data.credits.map((credit) => String(credit.slug))
31170
31174
  );
31171
- const creditPurchaseTotalsBySlug = /* @__PURE__ */ new Map();
31172
- const creditRetiredTotalsBySlug = /* @__PURE__ */ new Map();
31173
- const collectionPurchasedTotalsBySlug = /* @__PURE__ */ new Map();
31174
31175
  const collectionRetiredTotalsBySlug = /* @__PURE__ */ new Map();
31175
31176
  let totalCreditsFromCertificates = 0;
31176
31177
  data.certificates.forEach((certificate, index) => {
@@ -31181,7 +31182,6 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31181
31182
  path: ["certificates", index, "credit_slug"]
31182
31183
  });
31183
31184
  let certificatePurchasedTotal = 0;
31184
- let certificateRetiredTotal = 0;
31185
31185
  validateCertificateCollectionSlugs({
31186
31186
  ctx,
31187
31187
  certificateCollections: certificate.collections,
@@ -31189,6 +31189,7 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31189
31189
  certificateIndex: index
31190
31190
  });
31191
31191
  certificate.collections.forEach((collectionItem, collectionIndex) => {
31192
+ referencedCollectionSlugs.add(String(collectionItem.slug));
31192
31193
  if (collectionItem.retired_amount > collectionItem.purchased_amount) {
31193
31194
  ctx.addIssue({
31194
31195
  code: "custom",
@@ -31203,42 +31204,45 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31203
31204
  });
31204
31205
  }
31205
31206
  certificatePurchasedTotal += Number(collectionItem.purchased_amount);
31206
- certificateRetiredTotal += Number(collectionItem.retired_amount);
31207
- collectionPurchasedTotalsBySlug.set(
31208
- collectionItem.slug,
31209
- (collectionPurchasedTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.purchased_amount)
31210
- );
31211
31207
  collectionRetiredTotalsBySlug.set(
31212
31208
  collectionItem.slug,
31213
31209
  (collectionRetiredTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.retired_amount)
31214
31210
  );
31215
31211
  });
31216
- if (certificatePurchasedTotal > certificate.total_amount) {
31212
+ if (certificate.purchased_amount > certificate.total_amount) {
31217
31213
  ctx.addIssue({
31218
31214
  code: "custom",
31219
- message: "Sum of certificate.collections[].purchased_amount cannot exceed certificate.total_amount",
31220
- path: ["certificates", index]
31215
+ message: "certificate.purchased_amount cannot exceed certificate.total_amount",
31216
+ path: ["certificates", index, "purchased_amount"]
31221
31217
  });
31222
31218
  }
31223
- totalCreditsFromCertificates += certificatePurchasedTotal;
31224
- creditPurchaseTotalsBySlug.set(
31225
- String(certificate.credit_slug),
31226
- (creditPurchaseTotalsBySlug.get(certificate.credit_slug) ?? 0) + certificatePurchasedTotal
31227
- );
31228
- creditRetiredTotalsBySlug.set(
31229
- String(certificate.credit_slug),
31230
- (creditRetiredTotalsBySlug.get(certificate.credit_slug) ?? 0) + certificateRetiredTotal
31231
- );
31219
+ if (certificate.collections.length > 0 && !nearlyEqual(certificatePurchasedTotal, certificate.purchased_amount)) {
31220
+ ctx.addIssue({
31221
+ code: "custom",
31222
+ message: "certificate.purchased_amount must equal sum of collections[].purchased_amount when collections are present",
31223
+ path: ["certificates", index, "purchased_amount"]
31224
+ });
31225
+ }
31226
+ totalCreditsFromCertificates += Number(certificate.purchased_amount);
31232
31227
  });
31233
31228
  const certificateCollectionRetiredTotal = Array.from(
31234
31229
  collectionRetiredTotalsBySlug.values()
31235
31230
  ).reduce((sum, amount) => sum + amount, 0);
31231
+ data.collections.forEach((collection, collectionIndex) => {
31232
+ if (!referencedCollectionSlugs.has(String(collection.slug))) {
31233
+ ctx.addIssue({
31234
+ code: "custom",
31235
+ message: "collections must only include slugs referenced by certificates[].collections",
31236
+ path: ["collections", collectionIndex, "slug"]
31237
+ });
31238
+ }
31239
+ });
31236
31240
  validateTotalMatches({
31237
31241
  ctx,
31238
31242
  actualTotal: totalCreditsFromCertificates,
31239
31243
  expectedTotal: data.summary.total_credits,
31240
31244
  path: ["summary", "total_credits"],
31241
- message: "summary.total_credits must equal sum of certificate.collections[].purchased_amount"
31245
+ message: "summary.total_credits must equal sum of certificates[].purchased_amount"
31242
31246
  });
31243
31247
  validateRetirementReceiptRequirement({
31244
31248
  ctx,
@@ -31251,7 +31255,7 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31251
31255
  });
31252
31256
  var CreditPurchaseReceiptIpfsSchemaMeta = {
31253
31257
  title: "CreditPurchaseReceipt NFT IPFS Record",
31254
- description: "Complete CreditPurchaseReceipt NFT IPFS record including purchase summary, buyer details, credit breakdowns, certificate allocations, and NFT display attributes",
31258
+ 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.",
31255
31259
  $id: buildSchemaUrl(
31256
31260
  "credit-purchase-receipt/credit-purchase-receipt.schema.json"
31257
31261
  ),
@@ -31389,14 +31393,10 @@ var CreditPurchaseReceiptIpfsSchema = NftIpfsSchema.safeExtend({
31389
31393
  });
31390
31394
  return;
31391
31395
  }
31392
- const certificatePurchasedTotal = certificate.collections.reduce(
31393
- (sum, collection) => sum + Number(collection.purchased_amount),
31394
- 0
31395
- );
31396
31396
  const currentTotal = creditTotalsBySymbol.get(credit.symbol) ?? 0;
31397
31397
  creditTotalsBySymbol.set(
31398
31398
  credit.symbol,
31399
- currentTotal + certificatePurchasedTotal
31399
+ currentTotal + Number(certificate.purchased_amount)
31400
31400
  );
31401
31401
  });
31402
31402
  data.credits.forEach((credit) => {
@@ -31411,7 +31411,7 @@ var CreditPurchaseReceiptIpfsSchema = NftIpfsSchema.safeExtend({
31411
31411
  } else if (Number(attribute.value) !== expectedTotal) {
31412
31412
  ctx.addIssue({
31413
31413
  code: "custom",
31414
- message: `Attribute for credit symbol ${credit.symbol} must match sum of certificate.collections[].purchased_amount for the credit symbol`,
31414
+ message: `Attribute for credit symbol ${credit.symbol} must match sum of certificates[].purchased_amount for the credit symbol`,
31415
31415
  path: ["attributes"]
31416
31416
  });
31417
31417
  }
@@ -31513,24 +31513,32 @@ var CreditRetirementReceiptAttributesSchema = createOrderedAttributesSchema({
31513
31513
  });
31514
31514
  var CreditRetirementReceiptIdentitySchema = ReceiptIdentitySchema;
31515
31515
  var CreditRetirementReceiptBeneficiarySchema = zod.z.strictObject({
31516
- beneficiary_id: ExternalIdSchema.meta({
31517
- title: "Retirement Beneficiary ID",
31518
- description: "UUID identifying the beneficiary of the retirement within the Carrot platform"
31516
+ id_hash: Sha256HashSchema.meta({
31517
+ title: "Beneficiary ID Hash",
31518
+ description: "Anonymized pseudonymous identifier linking this beneficiary to off-chain records via a keyed hash"
31519
+ }),
31520
+ wallet_address: EthereumAddressSchema.optional().meta({
31521
+ title: "Beneficiary Wallet Address",
31522
+ description: "Ethereum address associated with the beneficiary, when available"
31519
31523
  }),
31520
31524
  identity: CreditRetirementReceiptIdentitySchema.optional()
31521
31525
  }).meta({
31522
31526
  title: "Beneficiary",
31523
- description: "Beneficiary receiving the retirement benefit"
31527
+ description: "Beneficiary receiving the retirement benefit, identified by a hashed identifier with optional wallet address and identity"
31524
31528
  });
31525
31529
  var CreditRetirementReceiptCreditHolderSchema = zod.z.strictObject({
31526
- wallet_address: EthereumAddressSchema.meta({
31530
+ id_hash: Sha256HashSchema.meta({
31531
+ title: "Credit Holder ID Hash",
31532
+ description: "Anonymized pseudonymous identifier linking this credit holder to off-chain records via a keyed hash"
31533
+ }),
31534
+ wallet_address: EthereumAddressSchema.optional().meta({
31527
31535
  title: "Credit Holder Wallet Address",
31528
31536
  description: "Ethereum address of the wallet that held and surrendered the credits"
31529
31537
  }),
31530
31538
  identity: CreditRetirementReceiptIdentitySchema.optional()
31531
31539
  }).meta({
31532
31540
  title: "Credit Holder",
31533
- description: "Credit holder wallet and optional identity information"
31541
+ description: "Credit holder information including hashed identifier, optional wallet address, and optional identity"
31534
31542
  });
31535
31543
  var CreditRetirementReceiptCollectionSchema = createReceiptCollectionSchema({
31536
31544
  meta: {
@@ -31570,9 +31578,9 @@ var CreditRetirementReceiptCertificateSchema = CertificateReferenceBaseSchema.sa
31570
31578
  CertificateCollectionItemRetirementSchema,
31571
31579
  (item) => item.slug,
31572
31580
  "Collection slugs within certificate collections must be unique"
31573
- ).min(1).meta({
31581
+ ).meta({
31574
31582
  title: "Certificate Collections",
31575
- description: "Collections associated with this certificate, each with retired amounts"
31583
+ description: "Collections associated with this certificate, each with retired amounts. May be empty when this certificate is not assigned to any collection."
31576
31584
  }),
31577
31585
  credits_retired: uniqueBy(
31578
31586
  CreditRetirementReceiptCertificateCreditSchema,
@@ -31594,9 +31602,9 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31594
31602
  CreditRetirementReceiptCollectionSchema,
31595
31603
  (collection) => collection.slug,
31596
31604
  "Collection slugs must be unique"
31597
- ).min(1).meta({
31605
+ ).meta({
31598
31606
  title: "Collections",
31599
- description: "Impact collections referenced by this retirement, each identified by a unique slug"
31607
+ description: "Impact collections referenced by this retirement, each identified by a unique slug. May be empty when no certificate is assigned to a collection."
31600
31608
  }),
31601
31609
  credits: uniqueBy(
31602
31610
  CreditRetirementReceiptCreditSchema,
@@ -31650,6 +31658,17 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31650
31658
  path: ["certificates", index]
31651
31659
  });
31652
31660
  }
31661
+ const creditsRetiredTotal = certificate.credits_retired.reduce(
31662
+ (sum, credit) => sum + Number(credit.amount),
31663
+ 0
31664
+ );
31665
+ if (creditsRetiredTotal > certificate.total_amount) {
31666
+ ctx.addIssue({
31667
+ code: "custom",
31668
+ message: "Sum of certificate.credits_retired[].amount cannot exceed certificate.total_amount",
31669
+ path: ["certificates", index, "credits_retired"]
31670
+ });
31671
+ }
31653
31672
  validateCertificateCollectionSlugs({
31654
31673
  ctx,
31655
31674
  certificateCollections: certificate.collections,
@@ -31662,11 +31681,7 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31662
31681
  (collectionRetiredTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.retired_amount)
31663
31682
  );
31664
31683
  });
31665
- const creditsRetiredTotal = certificate.credits_retired.reduce(
31666
- (sum, credit) => sum + Number(credit.amount),
31667
- 0
31668
- );
31669
- if (!nearlyEqual(creditsRetiredTotal, certificateCollectionRetiredTotal)) {
31684
+ if (certificate.collections.length > 0 && !nearlyEqual(creditsRetiredTotal, certificateCollectionRetiredTotal)) {
31670
31685
  ctx.addIssue({
31671
31686
  code: "custom",
31672
31687
  message: "certificates.credits_retired amounts must sum to certificate.collections[].retired_amount",
@@ -31717,27 +31732,29 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31717
31732
  (creditTotalsBySymbol.get(credit.credit_symbol) ?? 0) + credit.amount
31718
31733
  );
31719
31734
  });
31720
- totalRetiredFromCertificates += certificateCollectionRetiredTotal;
31735
+ totalRetiredFromCertificates += creditsRetiredTotal;
31721
31736
  });
31722
31737
  validateTotalMatches({
31723
31738
  ctx,
31724
31739
  actualTotal: totalRetiredFromCertificates,
31725
31740
  expectedTotal: data.summary.total_credits_retired,
31726
31741
  path: ["summary", "total_credits_retired"],
31727
- message: "summary.total_credits_retired must equal sum of certificate.collections[].retired_amount"
31728
- });
31729
- validateCollectionsHaveRetiredAmounts({
31730
- ctx,
31731
- collections: data.collections,
31732
- retiredTotalsBySlug: collectionRetiredTotalsBySlug
31742
+ message: "summary.total_credits_retired must equal sum of certificates[].credits_retired[].amount"
31733
31743
  });
31744
+ if (data.collections.length > 0) {
31745
+ validateCollectionsHaveRetiredAmounts({
31746
+ ctx,
31747
+ collections: data.collections,
31748
+ retiredTotalsBySlug: collectionRetiredTotalsBySlug
31749
+ });
31750
+ }
31734
31751
  }).meta({
31735
31752
  title: "Credit Retirement Receipt Data",
31736
31753
  description: "Complete data structure for a credit retirement receipt"
31737
31754
  });
31738
31755
  var CreditRetirementReceiptIpfsSchemaMeta = {
31739
31756
  title: "CreditRetirementReceipt NFT IPFS Record",
31740
- description: "Complete CreditRetirementReceipt NFT IPFS record including retirement summary, beneficiary and credit holder details (identity optional), credit breakdowns, certificate allocations, and NFT display attributes",
31757
+ 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.",
31741
31758
  $id: buildSchemaUrl(
31742
31759
  "credit-retirement-receipt/credit-retirement-receipt.schema.json"
31743
31760
  ),