@carrot-foundation/schemas 1.0.0 → 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
@@ -29388,9 +29388,9 @@ var CreditPurchaseReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29388
29388
  title: "Total Amount (USDC)",
29389
29389
  description: "Total amount paid in USDC stablecoin for the credit purchase"
29390
29390
  }),
29391
- total_credits: CreditAmountSchema.meta({
29391
+ total_credits: CreditAmountSchema.gt(0).meta({
29392
29392
  title: "Total Credits",
29393
- 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."
29394
29394
  }),
29395
29395
  purchased_at: IsoDateTimeSchema.meta({
29396
29396
  title: "Purchased At",
@@ -29401,9 +29401,9 @@ var CreditPurchaseReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29401
29401
  description: "Summary totals for the credit purchase including payment amount, credit quantity, certificate count, and timestamp"
29402
29402
  });
29403
29403
  var CreditRetirementReceiptSummarySchema = SummaryBaseSchema.safeExtend({
29404
- total_credits_retired: CreditAmountSchema.meta({
29404
+ total_credits_retired: CreditAmountSchema.gt(0).meta({
29405
29405
  title: "Total Credits Retired",
29406
- 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."
29407
29407
  }),
29408
29408
  retired_at: IsoDateTimeSchema.meta({
29409
29409
  title: "Retired At",
@@ -29490,7 +29490,7 @@ function buildSchemaUrl(schemaPath) {
29490
29490
  return `${getSchemaBaseUrl()}/${cleanPath}`;
29491
29491
  }
29492
29492
  function getSchemaVersionOrDefault() {
29493
- return "1.0.0";
29493
+ return "2.0.0";
29494
29494
  }
29495
29495
 
29496
29496
  // src/shared/schema-validation.ts
@@ -31112,13 +31112,17 @@ var CreditPurchaseReceiptCertificateSchema = CertificateReferenceBaseSchema.safe
31112
31112
  credit_slug: CreditTokenSlugSchema.meta({
31113
31113
  description: "Slug of the credit type for this certificate"
31114
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
+ }),
31115
31119
  collections: uniqueBy(
31116
31120
  CertificateCollectionItemPurchaseSchema,
31117
31121
  (item) => item.slug,
31118
31122
  "Collection slugs within certificate collections must be unique"
31119
- ).min(1).meta({
31123
+ ).meta({
31120
31124
  title: "Certificate Collections",
31121
- 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."
31122
31126
  })
31123
31127
  }).meta({
31124
31128
  title: "Certificate",
@@ -31132,9 +31136,9 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31132
31136
  CreditPurchaseReceiptCollectionSchema,
31133
31137
  (collection) => collection.slug,
31134
31138
  "Collection slugs must be unique"
31135
- ).min(1).meta({
31139
+ ).meta({
31136
31140
  title: "Collections",
31137
- 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."
31138
31142
  }),
31139
31143
  credits: uniqueBy(
31140
31144
  CreditPurchaseReceiptCreditSchema,
@@ -31164,12 +31168,10 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31164
31168
  const collectionSlugs = new Set(
31165
31169
  data.collections.map((collection) => String(collection.slug))
31166
31170
  );
31171
+ const referencedCollectionSlugs = /* @__PURE__ */ new Set();
31167
31172
  const creditSlugs = new Set(
31168
31173
  data.credits.map((credit) => String(credit.slug))
31169
31174
  );
31170
- const creditPurchaseTotalsBySlug = /* @__PURE__ */ new Map();
31171
- const creditRetiredTotalsBySlug = /* @__PURE__ */ new Map();
31172
- const collectionPurchasedTotalsBySlug = /* @__PURE__ */ new Map();
31173
31175
  const collectionRetiredTotalsBySlug = /* @__PURE__ */ new Map();
31174
31176
  let totalCreditsFromCertificates = 0;
31175
31177
  data.certificates.forEach((certificate, index) => {
@@ -31180,7 +31182,6 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31180
31182
  path: ["certificates", index, "credit_slug"]
31181
31183
  });
31182
31184
  let certificatePurchasedTotal = 0;
31183
- let certificateRetiredTotal = 0;
31184
31185
  validateCertificateCollectionSlugs({
31185
31186
  ctx,
31186
31187
  certificateCollections: certificate.collections,
@@ -31188,6 +31189,7 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31188
31189
  certificateIndex: index
31189
31190
  });
31190
31191
  certificate.collections.forEach((collectionItem, collectionIndex) => {
31192
+ referencedCollectionSlugs.add(String(collectionItem.slug));
31191
31193
  if (collectionItem.retired_amount > collectionItem.purchased_amount) {
31192
31194
  ctx.addIssue({
31193
31195
  code: "custom",
@@ -31202,42 +31204,45 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31202
31204
  });
31203
31205
  }
31204
31206
  certificatePurchasedTotal += Number(collectionItem.purchased_amount);
31205
- certificateRetiredTotal += Number(collectionItem.retired_amount);
31206
- collectionPurchasedTotalsBySlug.set(
31207
- collectionItem.slug,
31208
- (collectionPurchasedTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.purchased_amount)
31209
- );
31210
31207
  collectionRetiredTotalsBySlug.set(
31211
31208
  collectionItem.slug,
31212
31209
  (collectionRetiredTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.retired_amount)
31213
31210
  );
31214
31211
  });
31215
- if (certificatePurchasedTotal > certificate.total_amount) {
31212
+ if (certificate.purchased_amount > certificate.total_amount) {
31216
31213
  ctx.addIssue({
31217
31214
  code: "custom",
31218
- message: "Sum of certificate.collections[].purchased_amount cannot exceed certificate.total_amount",
31219
- path: ["certificates", index]
31215
+ message: "certificate.purchased_amount cannot exceed certificate.total_amount",
31216
+ path: ["certificates", index, "purchased_amount"]
31220
31217
  });
31221
31218
  }
31222
- totalCreditsFromCertificates += certificatePurchasedTotal;
31223
- creditPurchaseTotalsBySlug.set(
31224
- String(certificate.credit_slug),
31225
- (creditPurchaseTotalsBySlug.get(certificate.credit_slug) ?? 0) + certificatePurchasedTotal
31226
- );
31227
- creditRetiredTotalsBySlug.set(
31228
- String(certificate.credit_slug),
31229
- (creditRetiredTotalsBySlug.get(certificate.credit_slug) ?? 0) + certificateRetiredTotal
31230
- );
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);
31231
31227
  });
31232
31228
  const certificateCollectionRetiredTotal = Array.from(
31233
31229
  collectionRetiredTotalsBySlug.values()
31234
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
+ });
31235
31240
  validateTotalMatches({
31236
31241
  ctx,
31237
31242
  actualTotal: totalCreditsFromCertificates,
31238
31243
  expectedTotal: data.summary.total_credits,
31239
31244
  path: ["summary", "total_credits"],
31240
- message: "summary.total_credits must equal sum of certificate.collections[].purchased_amount"
31245
+ message: "summary.total_credits must equal sum of certificates[].purchased_amount"
31241
31246
  });
31242
31247
  validateRetirementReceiptRequirement({
31243
31248
  ctx,
@@ -31250,7 +31255,7 @@ var CreditPurchaseReceiptDataSchema = zod.z.strictObject({
31250
31255
  });
31251
31256
  var CreditPurchaseReceiptIpfsSchemaMeta = {
31252
31257
  title: "CreditPurchaseReceipt NFT IPFS Record",
31253
- 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.",
31254
31259
  $id: buildSchemaUrl(
31255
31260
  "credit-purchase-receipt/credit-purchase-receipt.schema.json"
31256
31261
  ),
@@ -31388,14 +31393,10 @@ var CreditPurchaseReceiptIpfsSchema = NftIpfsSchema.safeExtend({
31388
31393
  });
31389
31394
  return;
31390
31395
  }
31391
- const certificatePurchasedTotal = certificate.collections.reduce(
31392
- (sum, collection) => sum + Number(collection.purchased_amount),
31393
- 0
31394
- );
31395
31396
  const currentTotal = creditTotalsBySymbol.get(credit.symbol) ?? 0;
31396
31397
  creditTotalsBySymbol.set(
31397
31398
  credit.symbol,
31398
- currentTotal + certificatePurchasedTotal
31399
+ currentTotal + Number(certificate.purchased_amount)
31399
31400
  );
31400
31401
  });
31401
31402
  data.credits.forEach((credit) => {
@@ -31410,7 +31411,7 @@ var CreditPurchaseReceiptIpfsSchema = NftIpfsSchema.safeExtend({
31410
31411
  } else if (Number(attribute.value) !== expectedTotal) {
31411
31412
  ctx.addIssue({
31412
31413
  code: "custom",
31413
- 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`,
31414
31415
  path: ["attributes"]
31415
31416
  });
31416
31417
  }
@@ -31577,9 +31578,9 @@ var CreditRetirementReceiptCertificateSchema = CertificateReferenceBaseSchema.sa
31577
31578
  CertificateCollectionItemRetirementSchema,
31578
31579
  (item) => item.slug,
31579
31580
  "Collection slugs within certificate collections must be unique"
31580
- ).min(1).meta({
31581
+ ).meta({
31581
31582
  title: "Certificate Collections",
31582
- 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."
31583
31584
  }),
31584
31585
  credits_retired: uniqueBy(
31585
31586
  CreditRetirementReceiptCertificateCreditSchema,
@@ -31601,9 +31602,9 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31601
31602
  CreditRetirementReceiptCollectionSchema,
31602
31603
  (collection) => collection.slug,
31603
31604
  "Collection slugs must be unique"
31604
- ).min(1).meta({
31605
+ ).meta({
31605
31606
  title: "Collections",
31606
- 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."
31607
31608
  }),
31608
31609
  credits: uniqueBy(
31609
31610
  CreditRetirementReceiptCreditSchema,
@@ -31657,6 +31658,17 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31657
31658
  path: ["certificates", index]
31658
31659
  });
31659
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
+ }
31660
31672
  validateCertificateCollectionSlugs({
31661
31673
  ctx,
31662
31674
  certificateCollections: certificate.collections,
@@ -31669,11 +31681,7 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31669
31681
  (collectionRetiredTotalsBySlug.get(collectionItem.slug) ?? 0) + Number(collectionItem.retired_amount)
31670
31682
  );
31671
31683
  });
31672
- const creditsRetiredTotal = certificate.credits_retired.reduce(
31673
- (sum, credit) => sum + Number(credit.amount),
31674
- 0
31675
- );
31676
- if (!nearlyEqual(creditsRetiredTotal, certificateCollectionRetiredTotal)) {
31684
+ if (certificate.collections.length > 0 && !nearlyEqual(creditsRetiredTotal, certificateCollectionRetiredTotal)) {
31677
31685
  ctx.addIssue({
31678
31686
  code: "custom",
31679
31687
  message: "certificates.credits_retired amounts must sum to certificate.collections[].retired_amount",
@@ -31724,27 +31732,29 @@ var CreditRetirementReceiptDataSchema = zod.z.strictObject({
31724
31732
  (creditTotalsBySymbol.get(credit.credit_symbol) ?? 0) + credit.amount
31725
31733
  );
31726
31734
  });
31727
- totalRetiredFromCertificates += certificateCollectionRetiredTotal;
31735
+ totalRetiredFromCertificates += creditsRetiredTotal;
31728
31736
  });
31729
31737
  validateTotalMatches({
31730
31738
  ctx,
31731
31739
  actualTotal: totalRetiredFromCertificates,
31732
31740
  expectedTotal: data.summary.total_credits_retired,
31733
31741
  path: ["summary", "total_credits_retired"],
31734
- message: "summary.total_credits_retired must equal sum of certificate.collections[].retired_amount"
31735
- });
31736
- validateCollectionsHaveRetiredAmounts({
31737
- ctx,
31738
- collections: data.collections,
31739
- retiredTotalsBySlug: collectionRetiredTotalsBySlug
31742
+ message: "summary.total_credits_retired must equal sum of certificates[].credits_retired[].amount"
31740
31743
  });
31744
+ if (data.collections.length > 0) {
31745
+ validateCollectionsHaveRetiredAmounts({
31746
+ ctx,
31747
+ collections: data.collections,
31748
+ retiredTotalsBySlug: collectionRetiredTotalsBySlug
31749
+ });
31750
+ }
31741
31751
  }).meta({
31742
31752
  title: "Credit Retirement Receipt Data",
31743
31753
  description: "Complete data structure for a credit retirement receipt"
31744
31754
  });
31745
31755
  var CreditRetirementReceiptIpfsSchemaMeta = {
31746
31756
  title: "CreditRetirementReceipt NFT IPFS Record",
31747
- 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.",
31748
31758
  $id: buildSchemaUrl(
31749
31759
  "credit-retirement-receipt/credit-retirement-receipt.schema.json"
31750
31760
  ),