@xyo-network/payment-plugin 3.0.18 → 3.0.20

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.
@@ -1,4 +1,4 @@
1
- import { Address } from '@xylabs/hex';
1
+ import { Address, Hash } from '@xylabs/hex';
2
2
  import { ArchivistInstance } from '@xyo-network/archivist-model';
3
3
  import { AbstractDiviner } from '@xyo-network/diviner-abstract';
4
4
  import { HashLeaseEstimate } from '@xyo-network/diviner-hash-lease';
@@ -14,6 +14,8 @@ export declare class PaymentDiscountDiviner<TParams extends PaymentDiscountDivin
14
14
  protected filterToSigned(coupons: Coupon[]): Promise<Coupon[]>;
15
15
  protected getDiscountsArchivist(): Promise<ArchivistInstance>;
16
16
  protected getDiscountsBoundWitnessDiviner(): Promise<DivinerInstance>;
17
+ protected getEscrowAppraisals(terms: EscrowTerms, hashMap: Record<Hash, Payload>): HashLeaseEstimate[];
18
+ protected getEscrowDiscounts(terms: EscrowTerms, hashMap: Record<Hash, Payload>): Promise<Coupon[]>;
17
19
  protected isCouponCurrent(coupon: Coupon): boolean;
18
20
  }
19
21
  //# sourceMappingURL=Diviner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../../src/Discount/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAQ,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,iBAAiB,EAAuB,MAAM,8BAA8B,CAAA;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAE/D,OAAO,EACL,iBAAiB,EAElB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACc,eAAe,EAAE,sBAAsB,EAC3D,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AACpD,OAAO,EACL,MAAM,EACN,QAAQ,EACR,WAAW,EAEqD,4BAA4B,EAC7F,MAAM,sCAAsC,CAAA;AAU7C,MAAM,MAAM,+BAA+B,GAAG,WAAW,GAAG,MAAM,GAAG,iBAAiB,GAAG,OAAO,CAAA;AAEhG,qBACa,sBAAsB,CACjC,OAAO,SAAS,4BAA4B,GAAG,4BAA4B,EAC3E,GAAG,SAAS,+BAA+B,GAAG,+BAA+B,EAC7E,IAAI,SAAS,QAAQ,GAAG,QAAQ,EAChC,UAAU,SAAS,sBAAsB,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAChH,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EACnC,GAAG,EACH,IAAI,CACL,CACD,SAAQ,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACvD,OAAgB,aAAa,WAAuC;IACpE,OAAgB,mBAAmB,SAAqC;IAExE,SAAS,KAAK,iBAAiB,IAAI,OAAO,EAAE,CAE3C;cAEe,aAAa,CAAC,QAAQ,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAiEpD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;cAqBpD,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC;cAMnD,+BAA+B,IAAI,OAAO,CAAC,eAAe,CAAC;IAM3E,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAInD"}
1
+ {"version":3,"file":"Diviner.d.ts","sourceRoot":"","sources":["../../../src/Discount/Diviner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,iBAAiB,EAAuB,MAAM,8BAA8B,CAAA;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAE/D,OAAO,EACL,iBAAiB,EAElB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACc,eAAe,EAAE,sBAAsB,EAC3D,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AACpD,OAAO,EACL,MAAM,EACN,QAAQ,EACR,WAAW,EAEqD,4BAA4B,EAC7F,MAAM,sCAAsC,CAAA;AAU7C,MAAM,MAAM,+BAA+B,GAAG,WAAW,GAAG,MAAM,GAAG,iBAAiB,GAAG,OAAO,CAAA;AAEhG,qBACa,sBAAsB,CACjC,OAAO,SAAS,4BAA4B,GAAG,4BAA4B,EAC3E,GAAG,SAAS,+BAA+B,GAAG,+BAA+B,EAC7E,IAAI,SAAS,QAAQ,GAAG,QAAQ,EAChC,UAAU,SAAS,sBAAsB,CAAC,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,sBAAsB,CAChH,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,EACnC,GAAG,EACH,IAAI,CACL,CACD,SAAQ,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC;IACvD,OAAgB,aAAa,WAAuC;IACpE,OAAgB,mBAAmB,SAAqC;IAExE,SAAS,KAAK,iBAAiB,IAAI,OAAO,EAAE,CAE3C;cAEe,aAAa,CAAC,QAAQ,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;cAyCpD,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;cAqBpD,qBAAqB,IAAI,OAAO,CAAC,iBAAiB,CAAC;cAMnD,+BAA+B,IAAI,OAAO,CAAC,eAAe,CAAC;IAY3E,SAAS,CAAC,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,iBAAiB,EAAE;cAYtF,kBAAkB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA2BzG,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;CAInD"}
@@ -1 +1 @@
1
- {"version":3,"file":"applyCoupons.d.ts","sourceRoot":"","sources":["../../../../src/Discount/lib/applyCoupons.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACxE,OAAO,KAAK,EAEV,MAAM,EAAE,QAAQ,EAEjB,MAAM,sCAAsC,CAAA;AAM7C,eAAO,MAAM,YAAY,eAAgB,iBAAiB,EAAE,WAAW,MAAM,EAAE,KAAG,QA4CjF,CAAA"}
1
+ {"version":3,"file":"applyCoupons.d.ts","sourceRoot":"","sources":["../../../../src/Discount/lib/applyCoupons.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AACxE,OAAO,KAAK,EAEV,MAAM,EAAE,QAAQ,EAGjB,MAAM,sCAAsC,CAAA;AAO7C,eAAO,MAAM,YAAY,eAAgB,iBAAiB,EAAE,WAAW,MAAM,EAAE,KAAG,QAiDjF,CAAA"}
@@ -0,0 +1,6 @@
1
+ import type { Hash } from '@xylabs/hex';
2
+ import type { Payload } from '@xyo-network/payload-model';
3
+ import type { Coupon } from '@xyo-network/payment-payload-plugins';
4
+ export declare const areConditionsFulfilled: (coupon: Coupon, payloads: Payload[]) => Promise<boolean>;
5
+ export declare const findUnfulfilledConditions: (coupon: Coupon, payloads: Payload[]) => Promise<Hash[]>;
6
+ //# sourceMappingURL=findUnfulfilledConditions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findUnfulfilledConditions.d.ts","sourceRoot":"","sources":["../../../../src/Discount/lib/findUnfulfilledConditions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,4BAA4B,CAAA;AACnE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sCAAsC,CAAA;AAUlE,eAAO,MAAM,sBAAsB,WAAkB,MAAM,YAAY,OAAO,EAAE,KAAG,OAAO,CAAC,OAAO,CAChC,CAAA;AASlE,eAAO,MAAM,yBAAyB,WAAkB,MAAM,YAAY,OAAO,EAAE,KAAG,OAAO,CAAC,IAAI,EAAE,CAwCnG,CAAA"}
@@ -1,2 +1,3 @@
1
1
  export * from './applyCoupons.ts';
2
+ export * from './findUnfulfilledConditions.ts';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Discount/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/Discount/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAA;AACjC,cAAc,gCAAgC,CAAA"}
@@ -24,7 +24,7 @@ import {
24
24
  asDivinerInstance
25
25
  } from "@xyo-network/diviner-model";
26
26
  import { creatableModule } from "@xyo-network/module-model";
27
- import { PayloadBuilder } from "@xyo-network/payload-builder";
27
+ import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/payload-builder";
28
28
  import {
29
29
  isCoupon,
30
30
  isCouponWithMeta,
@@ -40,6 +40,7 @@ import {
40
40
  DiscountSchema,
41
41
  isFixedAmountCoupon,
42
42
  isFixedPercentageCoupon,
43
+ isFixedPriceCoupon,
43
44
  isStackable
44
45
  } from "@xyo-network/payment-payload-plugins";
45
46
  var applyCoupons = (appraisals, coupons) => {
@@ -50,11 +51,14 @@ var applyCoupons = (appraisals, coupons) => {
50
51
  const total = appraisals.reduce((acc, appraisal) => acc + appraisal.price, 0);
51
52
  const singularFixedDiscount = Math.max(...coupons.filter((coupon) => isFixedAmountCoupon(coupon) && !isStackable(coupon)).map((coupon) => coupon.amount), 0);
52
53
  const singularPercentageDiscount = Math.max(...coupons.filter((coupon) => isFixedPercentageCoupon(coupon) && !isStackable(coupon)).map((coupon) => coupon.percentage), 0) * total;
54
+ const singularFixedPriceDiscount = calculateSingularFixedPriceDiscount(total, appraisals, coupons, false);
53
55
  const stackedFixedDiscount = coupons.filter((coupon) => isFixedAmountCoupon(coupon) && isStackable(coupon)).reduce((acc, coupon) => acc + coupon.amount, 0);
54
56
  const stackedPercentageDiscount = coupons.filter((coupon) => isFixedPercentageCoupon(coupon) && isStackable(coupon)).reduce((acc, coupon) => acc + coupon.percentage, 0) * (total - stackedFixedDiscount);
55
- const stackedDiscount = stackedFixedDiscount + stackedPercentageDiscount;
57
+ const stackedFixedPriceDiscount = calculateSingularFixedPriceDiscount(total, appraisals, coupons, true);
58
+ const stackedDiscount = stackedFixedDiscount + stackedPercentageDiscount + stackedFixedPriceDiscount;
56
59
  const maxDiscount = Math.max(
57
60
  singularFixedDiscount,
61
+ singularFixedPriceDiscount,
58
62
  singularPercentageDiscount,
59
63
  stackedDiscount,
60
64
  0
@@ -66,6 +70,55 @@ var applyCoupons = (appraisals, coupons) => {
66
70
  currency: "USD"
67
71
  };
68
72
  };
73
+ var calculateSingularFixedPriceDiscount = (total, appraisals, coupons, stackable = false) => {
74
+ const singularFixedPriceDiscounts = coupons.filter(isFixedPriceCoupon).filter((coupon) => stackable ? isStackable(coupon) : !isStackable(coupon)).map((coupon) => coupon.amount).filter((amount) => amount > 0);
75
+ if (singularFixedPriceDiscounts.length === 0) return 0;
76
+ const lowestFixedPrice = Math.min(...singularFixedPriceDiscounts);
77
+ const reducedPrices = appraisals.map((appraisal) => (
78
+ // If the appraisal price is less than the fixed price
79
+ appraisal.price < lowestFixedPrice ? appraisal.price : lowestFixedPrice
80
+ ));
81
+ const reducedTotal = reducedPrices.reduce((acc, price) => acc + price, 0);
82
+ const discount = total - reducedTotal;
83
+ return discount > 0 ? discount : 0;
84
+ };
85
+
86
+ // src/Discount/lib/findUnfulfilledConditions.ts
87
+ import { PayloadBuilder } from "@xyo-network/payload-builder";
88
+ import { isSchemaPayloadWithMeta } from "@xyo-network/schema-payload-plugin";
89
+ import { Ajv } from "ajv";
90
+ var ajv = new Ajv({ strict: false });
91
+ var schemaCache = /* @__PURE__ */ new Map();
92
+ var areConditionsFulfilled = async (coupon, payloads) => (await findUnfulfilledConditions(coupon, payloads)).length === 0;
93
+ var findUnfulfilledConditions = async (coupon, payloads) => {
94
+ const unfulfilledConditions = [];
95
+ if (!coupon.conditions || coupon.conditions.length === 0) return unfulfilledConditions;
96
+ const hashMap = await PayloadBuilder.toAllHashMap(payloads);
97
+ const conditions = coupon.conditions.map((hash) => hashMap[hash]).filter(isSchemaPayloadWithMeta);
98
+ if (conditions.length !== coupon.conditions.length) {
99
+ const missing = coupon.conditions.filter((hash) => !hashMap[hash]);
100
+ unfulfilledConditions.push(...missing);
101
+ return unfulfilledConditions;
102
+ }
103
+ for (const hash of coupon.conditions) {
104
+ let validator;
105
+ if (schemaCache.has(hash)) {
106
+ validator = schemaCache.get(hash);
107
+ } else {
108
+ const payload = hashMap[hash];
109
+ const definition = isSchemaPayloadWithMeta(payload) ? payload.definition : void 0;
110
+ if (definition) {
111
+ validator = ajv.compile(definition);
112
+ schemaCache.set(hash, validator);
113
+ } else {
114
+ unfulfilledConditions.push(hash);
115
+ continue;
116
+ }
117
+ }
118
+ if (!validator(payloads)) unfulfilledConditions.push(hash);
119
+ }
120
+ return unfulfilledConditions;
121
+ };
69
122
 
70
123
  // src/Discount/Diviner.ts
71
124
  var DEFAULT_BOUND_WITNESS_DIVINER_QUERY_PROPS = {
@@ -81,34 +134,16 @@ var PaymentDiscountDiviner = class extends AbstractDiviner {
81
134
  const sources = [];
82
135
  const terms = payloads.find(isEscrowTerms);
83
136
  if (!terms) return [{ ...NO_DISCOUNT, sources }];
84
- sources.push(await PayloadBuilder.hash(terms));
85
- const discountHashes = terms.discounts ?? [];
86
- if (discountHashes.length === 0) return [{ ...NO_DISCOUNT, sources }];
137
+ sources.push(await PayloadBuilder2.hash(terms));
87
138
  const termsAppraisals = terms?.appraisals;
88
139
  if (!termsAppraisals || termsAppraisals.length === 0) return [{ ...NO_DISCOUNT, sources }];
89
- const hashMap = await PayloadBuilder.toAllHashMap(payloads);
90
- const foundAppraisals = termsAppraisals.filter((hash) => hashMap[hash]);
91
- sources.push(...foundAppraisals);
92
- if (foundAppraisals.length !== termsAppraisals.length) {
93
- return [{ ...NO_DISCOUNT, sources }];
94
- }
95
- const appraisals = foundAppraisals.map((hash) => hashMap[hash]).filter(exists2).filter(isHashLeaseEstimate);
96
- const discounts = discountHashes.map((hash) => hashMap[hash]).filter(exists2).filter(isCoupon);
97
- if (discounts.length !== discountHashes.length) {
98
- const discountsArchivist = await this.getDiscountsArchivist();
99
- const foundDiscounts = await discountsArchivist.get(discountHashes);
100
- discounts.push(...foundDiscounts.filter(isCouponWithMeta));
101
- }
102
- const discountsMap = await PayloadBuilder.toAllHashMap(discounts);
103
- if (Object.keys(discountsMap).length === 0) return [{ ...NO_DISCOUNT, sources }];
104
- const foundDiscountsHashes = Object.keys(discountsMap);
105
- sources.push(...foundDiscountsHashes);
106
- for (const hash of discountHashes) {
107
- if (!foundDiscountsHashes.includes(hash)) {
108
- console.warn(`Discount ${hash} not found for terms ${await PayloadBuilder.hash(terms)}`);
109
- }
110
- }
111
- const coupons = Object.values(discountsMap);
140
+ const hashMap = await PayloadBuilder2.toAllHashMap(payloads);
141
+ const appraisals = this.getEscrowAppraisals(terms, hashMap);
142
+ sources.push(...termsAppraisals);
143
+ if (appraisals.length !== termsAppraisals.length) return [{ ...NO_DISCOUNT, sources }];
144
+ const coupons = await this.getEscrowDiscounts(terms, hashMap);
145
+ const couponHashes = await PayloadBuilder2.hashes(coupons);
146
+ sources.push(...couponHashes);
112
147
  const validCoupons = await this.filterToSigned(coupons.filter(this.isCouponCurrent));
113
148
  if (validCoupons.length === 0) return [{ ...NO_DISCOUNT, sources }];
114
149
  const discount = applyCoupons(appraisals, validCoupons);
@@ -122,7 +157,7 @@ var PaymentDiscountDiviner = class extends AbstractDiviner {
122
157
  */
123
158
  async filterToSigned(coupons) {
124
159
  const signed = [];
125
- const dataHashMap = await PayloadBuilder.toDataHashMap(coupons);
160
+ const dataHashMap = await PayloadBuilder2.toDataHashMap(coupons);
126
161
  const boundWitnessDiviner = await this.getDiscountsBoundWitnessDiviner();
127
162
  const hashes = Object.keys(dataHashMap);
128
163
  const addresses = this.couponAuthorities;
@@ -150,6 +185,42 @@ var PaymentDiscountDiviner = class extends AbstractDiviner {
150
185
  const mod = assertEx2(await this.resolve(name), () => `Error resolving boundWitnessDiviner: ${name}`);
151
186
  return assertEx2(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`);
152
187
  }
188
+ /**
189
+ * Finds the appraisals specified by the escrow terms from the supplied payloads
190
+ * @param terms The escrow terms
191
+ * @param payloads The payloads to search for the appraisals
192
+ * @returns The appraisals found in the payloads
193
+ */
194
+ getEscrowAppraisals(terms, hashMap) {
195
+ const hashes = terms?.appraisals ?? [];
196
+ if (hashes.length === 0) return [];
197
+ return hashes.map((hash) => hashMap[hash]).filter(exists2).filter(isHashLeaseEstimate);
198
+ }
199
+ /**
200
+ * Finds the discounts specified by the escrow terms from the supplied payloads
201
+ * @param terms The escrow terms
202
+ * @param hashMap The payloads to search for the discounts
203
+ * @returns The discounts found in the payloads
204
+ */
205
+ async getEscrowDiscounts(terms, hashMap) {
206
+ const hashes = terms.discounts ?? [];
207
+ if (hashes.length === 0) return [];
208
+ const discounts = hashes.map((hash) => hashMap[hash]).filter(exists2).filter(isCoupon);
209
+ const missing = hashes.filter((hash) => !hashMap[hash]);
210
+ if (missing.length > 0) {
211
+ const discountsArchivist = await this.getDiscountsArchivist();
212
+ const payloads = await discountsArchivist.get(missing);
213
+ discounts.push(...payloads.filter(isCouponWithMeta));
214
+ }
215
+ if (discounts.length !== hashes.length) {
216
+ const termsHash = await PayloadBuilder2.hash(terms);
217
+ const foundHashes = await PayloadBuilder2.hashes(discounts);
218
+ for (const hash of hashes) {
219
+ if (!foundHashes.includes(hash)) console.warn(`Discount ${hash} not found for terms ${termsHash}`);
220
+ }
221
+ }
222
+ return discounts;
223
+ }
153
224
  isCouponCurrent(coupon) {
154
225
  const now = Date.now();
155
226
  return coupon.exp > now && coupon.nbf < now;
@@ -162,7 +233,7 @@ PaymentDiscountDiviner = __decorateClass([
162
233
  ], PaymentDiscountDiviner);
163
234
 
164
235
  // src/Invoice/getInvoiceForEscrow.ts
165
- import { PayloadBuilder as PayloadBuilder2 } from "@xyo-network/payload-builder";
236
+ import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/payload-builder";
166
237
  import {
167
238
  isDiscount,
168
239
  isSubtotal,
@@ -189,14 +260,14 @@ var getInvoiceForEscrow = async (terms, dataHashMap, paymentTotalDiviner) => {
189
260
  };
190
261
  var getSources = async (terms, subtotal, total, discount) => {
191
262
  const sources = discount ? [terms, subtotal, total, discount] : [terms, subtotal, total];
192
- return await Promise.all(sources.map((p) => PayloadBuilder2.dataHash(p)));
263
+ return await Promise.all(sources.map((p) => PayloadBuilder3.dataHash(p)));
193
264
  };
194
265
 
195
266
  // src/Subtotal/Diviner.ts
196
267
  import { AbstractDiviner as AbstractDiviner2 } from "@xyo-network/diviner-abstract";
197
268
  import { isHashLeaseEstimate as isHashLeaseEstimate2 } from "@xyo-network/diviner-hash-lease";
198
269
  import { creatableModule as creatableModule2 } from "@xyo-network/module-model";
199
- import { PayloadBuilder as PayloadBuilder3 } from "@xyo-network/payload-builder";
270
+ import { PayloadBuilder as PayloadBuilder4 } from "@xyo-network/payload-builder";
200
271
  import {
201
272
  isEscrowTerms as isEscrowTerms2,
202
273
  PaymentSubtotalDivinerConfigSchema,
@@ -261,12 +332,12 @@ var PaymentSubtotalDiviner = class extends AbstractDiviner2 {
261
332
  if (!terms) return [];
262
333
  if (!termsValidators.every((validator) => validator(terms))) return [];
263
334
  const validTerms = terms;
264
- const hashMap = await PayloadBuilder3.toAllHashMap(payloads);
335
+ const hashMap = await PayloadBuilder4.toAllHashMap(payloads);
265
336
  const appraisals = validTerms.appraisals.map((appraisal) => hashMap[appraisal]).filter(isHashLeaseEstimate2);
266
337
  if (appraisals.length !== validTerms.appraisals.length) return [];
267
338
  if (!appraisalValidators.every((validator) => validator(appraisals))) return [];
268
339
  const amount = calculateSubtotal(appraisals);
269
- const sources = [await PayloadBuilder3.dataHash(validTerms), ...validTerms.appraisals];
340
+ const sources = [await PayloadBuilder4.dataHash(validTerms), ...validTerms.appraisals];
270
341
  return [{
271
342
  amount,
272
343
  currency,
@@ -342,6 +413,8 @@ export {
342
413
  PaymentSubtotalDiviner,
343
414
  PaymentTotalDiviner,
344
415
  applyCoupons,
416
+ areConditionsFulfilled,
417
+ findUnfulfilledConditions,
345
418
  getInvoiceForEscrow
346
419
  };
347
420
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Discount/Diviner.ts","../../src/Discount/lib/applyCoupons.ts","../../src/Invoice/getInvoiceForEscrow.ts","../../src/Subtotal/Diviner.ts","../../src/Subtotal/lib/appraisalValidators.ts","../../src/Subtotal/lib/durationValidators.ts","../../src/Subtotal/lib/termsValidators.ts","../../src/Total/Diviner.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport { exists } from '@xylabs/exists'\nimport { Address, Hash } from '@xylabs/hex'\nimport { ArchivistInstance, asArchivistInstance } from '@xyo-network/archivist-model'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { BoundWitnessDivinerQueryPayload, BoundWitnessDivinerQuerySchema } from '@xyo-network/diviner-boundwitness-model'\nimport {\n HashLeaseEstimate,\n isHashLeaseEstimate,\n} from '@xyo-network/diviner-hash-lease'\nimport {\n asDivinerInstance, DivinerInstance, DivinerModuleEventData,\n} from '@xyo-network/diviner-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\nimport {\n Coupon,\n Discount,\n EscrowTerms, isCoupon,\n isCouponWithMeta,\n isEscrowTerms, NO_DISCOUNT, PaymentDiscountDivinerConfigSchema, PaymentDiscountDivinerParams,\n} from '@xyo-network/payment-payload-plugins'\n\nimport { applyCoupons } from './lib/index.ts'\n\nconst DEFAULT_BOUND_WITNESS_DIVINER_QUERY_PROPS: Readonly<BoundWitnessDivinerQueryPayload> = {\n limit: 1,\n order: 'desc',\n schema: BoundWitnessDivinerQuerySchema,\n}\n\nexport type PaymentDiscountDivinerInputType = EscrowTerms | Coupon | HashLeaseEstimate | Payload\n\n@creatableModule()\nexport class PaymentDiscountDiviner<\n TParams extends PaymentDiscountDivinerParams = PaymentDiscountDivinerParams,\n TIn extends PaymentDiscountDivinerInputType = PaymentDiscountDivinerInputType,\n TOut extends Discount = Discount,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends AbstractDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [PaymentDiscountDivinerConfigSchema]\n static override defaultConfigSchema = PaymentDiscountDivinerConfigSchema\n\n protected get couponAuthorities(): Address[] {\n return [...(this.config.couponAuthorities ?? []), ...(this.params.couponAuthorities ?? [])]\n }\n\n protected async divineHandler(payloads: TIn[] = []): Promise<TOut[]> {\n const sources: Hash[] = []\n\n // Parse terms\n const terms = payloads.find(isEscrowTerms) as EscrowTerms | undefined\n if (!terms) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n sources.push(await PayloadBuilder.hash(terms))\n\n // Parse discounts\n const discountHashes = terms.discounts ?? []\n if (discountHashes.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n\n // TODO: Call paymentSubtotalDiviner to get the subtotal to centralize the logic\n // Parse appraisals\n const termsAppraisals = terms?.appraisals\n if (!termsAppraisals || termsAppraisals.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n const hashMap = await PayloadBuilder.toAllHashMap(payloads)\n const foundAppraisals = termsAppraisals.filter(hash => hashMap[hash])\n // Add the appraisals that were found to the sources\n sources.push(...foundAppraisals)\n // If not all appraisals are found, return no discount\n if (foundAppraisals.length !== termsAppraisals.length) {\n return [{ ...NO_DISCOUNT, sources }] as TOut[]\n }\n // TODO: Cast should not be required\n const appraisals = foundAppraisals.map(hash => hashMap[hash]).filter(exists).filter(isHashLeaseEstimate) as unknown as HashLeaseEstimate[]\n\n // Use the supplied payloads to find the discounts\n const discounts = discountHashes.map(hash => hashMap[hash]).filter(exists).filter(isCoupon) as Coupon[]\n // Find any remaining coupons from the archivist\n if (discounts.length !== discountHashes.length) {\n // Find remaining from discounts archivist\n const discountsArchivist = await this.getDiscountsArchivist()\n const foundDiscounts = await discountsArchivist.get(discountHashes)\n discounts.push(...foundDiscounts.filter(isCouponWithMeta))\n }\n const discountsMap = await PayloadBuilder.toAllHashMap(discounts)\n if (Object.keys(discountsMap).length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n\n // Add the found discounts to the sources\n const foundDiscountsHashes = Object.keys(discountsMap) as Hash[]\n sources.push(...foundDiscountsHashes)\n\n // Log individual discounts that were not found\n for (const hash of discountHashes) {\n if (!foundDiscountsHashes.includes(hash)) {\n console.warn(`Discount ${hash} not found for terms ${await PayloadBuilder.hash(terms)}`)\n }\n }\n\n // Parse coupons\n const coupons = Object.values(discountsMap)\n const validCoupons = await this.filterToSigned(coupons.filter(this.isCouponCurrent))\n if (validCoupons.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n\n const discount = applyCoupons(appraisals, validCoupons)\n return [{ ...discount, sources }] as TOut[]\n }\n\n /**\n * Filters the supplied list of coupons to only those that are signed by\n * addresses specified in the couponAuthorities\n * @param coupons The list of coupons to filter\n * @returns The filtered list of coupons that are signed by the couponAuthorities\n */\n protected async filterToSigned(coupons: Coupon[]): Promise<Coupon[]> {\n const signed: Coupon[] = []\n const dataHashMap = await PayloadBuilder.toDataHashMap(coupons)\n const boundWitnessDiviner = await this.getDiscountsBoundWitnessDiviner()\n const hashes = Object.keys(dataHashMap)\n const addresses = this.couponAuthorities\n // TODO: Keep an in memory cache of the hashes queried and their results\n // to avoid querying the same hash multiple times\n await Promise.all(hashes.map((h) => {\n const hash = h as Hash\n return Promise.all(addresses.map(async (address) => {\n const query: BoundWitnessDivinerQueryPayload = {\n ...DEFAULT_BOUND_WITNESS_DIVINER_QUERY_PROPS, addresses: [address], payload_hashes: [hash],\n }\n const result = await boundWitnessDiviner.divine([query])\n if (result.length > 0) signed.push(dataHashMap[hash])\n }))\n }))\n return signed\n }\n\n protected async getDiscountsArchivist(): Promise<ArchivistInstance> {\n const name = assertEx(this.config.archivist, () => 'Missing archivist in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving archivist: ${name}`)\n return assertEx(asArchivistInstance(mod), () => `Resolved module ${mod.address} not a valid Archivist`)\n }\n\n protected async getDiscountsBoundWitnessDiviner(): Promise<DivinerInstance> {\n const name = assertEx(this.config.boundWitnessDiviner, () => 'Missing boundWitnessDiviner in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving boundWitnessDiviner: ${name}`)\n return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`)\n }\n\n protected isCouponCurrent(coupon: Coupon): boolean {\n const now = Date.now()\n return coupon.exp > now && coupon.nbf < now\n }\n}\n","import { assertEx } from '@xylabs/assert'\nimport { exists } from '@xylabs/exists'\nimport type { HashLeaseEstimate } from '@xyo-network/diviner-hash-lease'\nimport type {\n AmountFields,\n Coupon, Discount, FixedAmountCoupon,\n FixedPercentageCoupon,\n} from '@xyo-network/payment-payload-plugins'\nimport {\n DiscountSchema, isFixedAmountCoupon, isFixedPercentageCoupon,\n isStackable,\n} from '@xyo-network/payment-payload-plugins'\n\nexport const applyCoupons = (appraisals: HashLeaseEstimate[], coupons: Coupon[]): Discount => {\n // Ensure all appraisals and coupons are in USD\n const allAppraisalsAreUSD = appraisals.every(appraisal => appraisal.currency === 'USD')\n assertEx(allAppraisalsAreUSD, 'All appraisals must be in USD')\n const allCouponsAreUSD = coupons.map(coupon => (coupon as Partial<AmountFields>)?.currency).filter(exists).every(currency => currency === 'USD')\n assertEx(allCouponsAreUSD, 'All coupons must be in USD')\n const total = appraisals.reduce((acc, appraisal) => acc + appraisal.price, 0)\n\n // Calculated non-stackable discount coupons\n const singularFixedDiscount = Math.max(...coupons\n .filter(coupon => isFixedAmountCoupon(coupon) && !isStackable(coupon))\n .map(coupon => (coupon as FixedAmountCoupon).amount), 0)\n const singularPercentageDiscount = (Math.max(...coupons\n .filter(coupon => isFixedPercentageCoupon(coupon) && !isStackable(coupon))\n .map(coupon => (coupon as FixedPercentageCoupon).percentage), 0)) * total\n\n // Calculate stackable discount coupons\n // First calculate the total discount from fixed amount coupons\n const stackedFixedDiscount = coupons\n .filter(coupon => isFixedAmountCoupon(coupon) && isStackable(coupon))\n .reduce((acc, coupon) => acc + (coupon as FixedAmountCoupon).amount, 0)\n // Then calculate the total discount from percentage coupons and apply\n // the percentage discount to the remaining total after fixed discounts\n const stackedPercentageDiscount = coupons\n .filter(coupon => isFixedPercentageCoupon(coupon) && isStackable(coupon))\n .reduce((acc, coupon) => acc + (coupon as FixedPercentageCoupon).percentage, 0) * (total - stackedFixedDiscount)\n // Sum all stackable discounts\n const stackedDiscount = stackedFixedDiscount + stackedPercentageDiscount\n\n // Find the best coupon(s) to apply\n const maxDiscount = Math.max(\n singularFixedDiscount,\n singularPercentageDiscount,\n stackedDiscount,\n 0,\n )\n\n // Ensure discount is not more than the total\n const amount = Math.min(maxDiscount, total)\n\n // Return single discount payload\n return {\n amount, schema: DiscountSchema, currency: 'USD',\n }\n}\n","import type { Hash } from '@xylabs/hex'\nimport type { DivinerInstance } from '@xyo-network/diviner-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport type { Payload } from '@xyo-network/payload-model'\nimport type {\n Discount, EscrowTerms, Invoice, Payment, Subtotal, Total,\n} from '@xyo-network/payment-payload-plugins'\nimport {\n isDiscount, isSubtotal, isTotal, PaymentSchema,\n} from '@xyo-network/payment-payload-plugins'\n\n/**\n * Validates the escrow terms to ensure they are valid for a purchase\n * @returns A payment if the terms are valid for a purchase, undefined otherwise\n */\nexport const getInvoiceForEscrow = async (\n terms: EscrowTerms,\n dataHashMap: Record<Hash, Payload>,\n paymentTotalDiviner: DivinerInstance,\n): Promise<Invoice | undefined> => {\n const payloads = Object.values(dataHashMap)\n const results = await paymentTotalDiviner.divine([terms, ...payloads])\n const subtotal = results.find(isSubtotal) as Subtotal | undefined\n const discount = results.find(isDiscount) as Discount | undefined\n const total = results.find(isTotal) as Total | undefined\n if (!subtotal || !total) return undefined\n const { amount, currency } = total\n if (currency !== 'USD') return undefined\n const sources = await getSources(terms, subtotal, total, discount)\n const payment: Payment = {\n amount, currency, schema: PaymentSchema, sources,\n }\n return discount ? [subtotal, total, payment, discount] : [subtotal, total, payment]\n}\n\nconst getSources = async (terms: EscrowTerms, subtotal: Subtotal, total: Total, discount?: Discount): Promise<Hash[]> => {\n const sources = discount ? [terms, subtotal, total, discount] : [terms, subtotal, total]\n return await Promise.all(sources.map(p => PayloadBuilder.dataHash(p)))\n}\n","import { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { HashLeaseEstimate, isHashLeaseEstimate } from '@xyo-network/diviner-hash-lease'\nimport { DivinerInstance, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\nimport {\n EscrowTerms, isEscrowTerms, PaymentSubtotalDivinerConfigSchema, PaymentSubtotalDivinerParams, Subtotal, SubtotalSchema,\n} from '@xyo-network/payment-payload-plugins'\n\nimport {\n appraisalValidators, termsValidators, ValidEscrowTerms,\n} from './lib/index.ts'\n\nconst currency = 'USD'\n\n/**\n * Escrow terms that contain all the valid fields for calculating a subtotal\n */\nexport type PaymentSubtotalDivinerInputType = EscrowTerms | HashLeaseEstimate | Payload\n\n@creatableModule()\nexport class PaymentSubtotalDiviner<\n TParams extends PaymentSubtotalDivinerParams = PaymentSubtotalDivinerParams,\n TIn extends PaymentSubtotalDivinerInputType = PaymentSubtotalDivinerInputType,\n TOut extends Subtotal = Subtotal,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends AbstractDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [PaymentSubtotalDivinerConfigSchema]\n static override defaultConfigSchema = PaymentSubtotalDivinerConfigSchema\n\n protected async divineHandler(payloads: TIn[] = []): Promise<TOut[]> {\n // Find the escrow terms\n const terms = payloads.find(isEscrowTerms) as EscrowTerms | undefined\n if (!terms) return []\n\n // Run all terms validations\n if (!termsValidators.every(validator => validator(terms))) return []\n const validTerms = terms as ValidEscrowTerms\n\n // Retrieve all appraisals from terms\n const hashMap = await PayloadBuilder.toAllHashMap(payloads)\n const appraisals = validTerms.appraisals.map(appraisal => hashMap[appraisal]).filter(isHashLeaseEstimate) as unknown as HashLeaseEstimate[]\n\n // Ensure all appraisals are present\n if (appraisals.length !== validTerms.appraisals.length) return []\n\n // Run all appraisal validations\n if (!appraisalValidators.every(validator => validator(appraisals))) return []\n const amount = calculateSubtotal(appraisals)\n const sources = [await PayloadBuilder.dataHash(validTerms), ...validTerms.appraisals]\n return [{\n amount, currency, schema: SubtotalSchema, sources,\n }] as TOut[]\n }\n}\n\n// TODO: Add support for other currencies\nconst calculateSubtotal = (appraisals: HashLeaseEstimate[]): number => {\n return appraisals.reduce((sum, appraisal) => sum + appraisal.price, 0)\n}\n","import type { HashLeaseEstimate } from '@xyo-network/diviner-hash-lease'\nimport { isIso4217CurrencyCode } from '@xyo-network/payment-payload-plugins'\n\nimport { validateDuration } from './durationValidators.ts'\n\nconst validateAppraisalAmount = (appraisals: HashLeaseEstimate[]): boolean => {\n // Ensure all appraisals are numeric\n if (appraisals.some(appraisal => typeof appraisal.price !== 'number')) return false\n // Ensure all appraisals are positive numbers\n if (appraisals.some(appraisal => appraisal.price < 0)) return false\n return true\n}\n\nconst validateAppraisalCurrency = (appraisals: HashLeaseEstimate[]): boolean => {\n // NOTE: Only supporting USD for now, the remaining checks are for future-proofing.\n if (!appraisals.every(appraisal => appraisal.currency == 'USD')) return false\n\n // Check every object in the array to ensure they all are in a supported currency.\n if (!appraisals.every(appraisal => isIso4217CurrencyCode(appraisal.currency))) return false\n\n return true\n}\n\nconst validateAppraisalConsistentCurrency = (appraisals: HashLeaseEstimate[]): boolean => {\n // Check if the array is empty or contains only one element, no need to compare.\n if (appraisals.length <= 1) return true\n\n // Get the currency of the first element to compare with others.\n const { currency } = appraisals[0]\n if (!currency) return false\n\n // Check every object in the array to ensure they all have the same currency.\n if (!appraisals.every(item => item.currency === currency)) return false\n\n return true\n}\n\nconst validateAppraisalWindow = (appraisals: HashLeaseEstimate[]): boolean => appraisals.every(validateDuration)\n\nexport const appraisalValidators = [\n validateAppraisalAmount,\n validateAppraisalCurrency,\n validateAppraisalConsistentCurrency,\n validateAppraisalWindow,\n]\n","import type { DurationFields } from '@xyo-network/xns-record-payload-plugins'\n\nconst FIVE_MINUTES = 1000 * 60 * 5\n\n/**\n * Validates that the current time is within the duration window, within a configurable a buffer\n * @param value The duration value\n * @param windowMs The window in milliseconds to allow for a buffer\n * @returns True if the duration is valid, false otherwise\n */\nexport const validateDuration = (value: Partial<DurationFields>, windowMs = FIVE_MINUTES): boolean => {\n const now = Date.now()\n if (!value.nbf || value.nbf > now) return false\n // If already expired (include for a 5 minute buffer to allow for a reasonable\n // minimum amount of time for the transaction to be processed)\n if (!value.exp || value.exp - now < windowMs) return false\n return true\n}\n","import type { Hash } from '@xylabs/hex'\nimport type { EscrowTerms } from '@xyo-network/payment-payload-plugins'\n\nimport { validateDuration } from './durationValidators.ts'\n\nexport type ValidEscrowTerms = Required<EscrowTerms>\n\nconst validateTermsAppraisals = (terms: EscrowTerms): terms is Required<EscrowTerms & { appraisals: Hash[] }> => {\n if (!terms.appraisals) return false\n if (terms.appraisals.length === 0) return false\n return true\n}\nconst validateTermsWindow = (terms: EscrowTerms): boolean => validateDuration(terms)\n\nexport const termsValidators = [\n validateTermsAppraisals,\n validateTermsWindow,\n]\n","import { assertEx } from '@xylabs/assert'\nimport { Hash } from '@xylabs/hex'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport {\n asDivinerInstance, DivinerInstance, DivinerModuleEventData,\n} from '@xyo-network/diviner-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport {\n Discount,\n isDiscountWithMeta,\n isSubtotalWithMeta,\n PaymentTotalDivinerConfigSchema, PaymentTotalDivinerParams, Subtotal, Total, TotalSchema,\n} from '@xyo-network/payment-payload-plugins'\n\nimport { PaymentDiscountDiviner, PaymentDiscountDivinerInputType } from '../Discount/index.ts'\nimport { PaymentSubtotalDiviner, PaymentSubtotalDivinerInputType } from '../Subtotal/index.ts'\n\ntype InputType = PaymentDiscountDivinerInputType | PaymentSubtotalDivinerInputType\ntype OutputType = Subtotal | Discount | Total\n\n@creatableModule()\nexport class PaymentTotalDiviner<\n TParams extends PaymentTotalDivinerParams = PaymentTotalDivinerParams,\n TIn extends InputType = InputType,\n TOut extends OutputType = OutputType,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends AbstractDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [PaymentTotalDivinerConfigSchema]\n static override defaultConfigSchema: PaymentTotalDivinerConfigSchema = PaymentTotalDivinerConfigSchema\n\n protected async divineHandler(payloads: TIn[] = []): Promise<TOut[]> {\n const subtotalDiviner = await this.getPaymentSubtotalDiviner()\n const subtotalResult = await subtotalDiviner.divine(payloads)\n const subtotal = subtotalResult.find(isSubtotalWithMeta)\n if (!subtotal) return []\n const discountDiviner = await this.getPaymentDiscountsDiviner()\n const discountResult = await discountDiviner.divine(payloads)\n const discount = discountResult.find(isDiscountWithMeta)\n if (!discount) return []\n const { currency: subtotalCurrency } = subtotal\n const { currency: discountCurrency } = discount\n assertEx(subtotalCurrency === discountCurrency, () => `Subtotal currency ${subtotalCurrency} does not match discount currency ${discountCurrency}`)\n const amount = Math.max(0, subtotal.amount - discount.amount)\n const currency = subtotalCurrency\n const sources = [subtotal.$hash, discount.$hash] as Hash[]\n const total: Total = {\n amount, currency, sources, schema: TotalSchema,\n }\n return [subtotal, discount, total] as TOut[]\n }\n\n protected async getPaymentDiscountsDiviner(): Promise<PaymentDiscountDiviner> {\n const name = assertEx(this.config.paymentDiscountDiviner, () => 'Missing paymentDiscountDiviner in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving paymentDiscountDiviner: ${name}`)\n return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`) as PaymentDiscountDiviner\n }\n\n protected async getPaymentSubtotalDiviner(): Promise<PaymentSubtotalDiviner> {\n const name = assertEx(this.config.paymentSubtotalDiviner, () => 'Missing paymentSubtotalDiviner in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving paymentSubtotalDiviner: ${name}`)\n return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`) as PaymentSubtotalDiviner\n }\n}\n"],"mappings":"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","names":["assertEx","exists","currency","exists","assertEx","PayloadBuilder","currency","AbstractDiviner","isHashLeaseEstimate","creatableModule","PayloadBuilder","isEscrowTerms","currency","AbstractDiviner","isEscrowTerms","PayloadBuilder","isHashLeaseEstimate","creatableModule","assertEx","AbstractDiviner","asDivinerInstance","creatableModule","AbstractDiviner","assertEx","currency","asDivinerInstance","creatableModule"]}
1
+ {"version":3,"sources":["../../src/Discount/Diviner.ts","../../src/Discount/lib/applyCoupons.ts","../../src/Discount/lib/findUnfulfilledConditions.ts","../../src/Invoice/getInvoiceForEscrow.ts","../../src/Subtotal/Diviner.ts","../../src/Subtotal/lib/appraisalValidators.ts","../../src/Subtotal/lib/durationValidators.ts","../../src/Subtotal/lib/termsValidators.ts","../../src/Total/Diviner.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport { exists } from '@xylabs/exists'\nimport { Address, Hash } from '@xylabs/hex'\nimport { ArchivistInstance, asArchivistInstance } from '@xyo-network/archivist-model'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { BoundWitnessDivinerQueryPayload, BoundWitnessDivinerQuerySchema } from '@xyo-network/diviner-boundwitness-model'\nimport {\n HashLeaseEstimate,\n isHashLeaseEstimate,\n} from '@xyo-network/diviner-hash-lease'\nimport {\n asDivinerInstance, DivinerInstance, DivinerModuleEventData,\n} from '@xyo-network/diviner-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\nimport {\n Coupon,\n Discount,\n EscrowTerms, isCoupon,\n isCouponWithMeta,\n isEscrowTerms, NO_DISCOUNT, PaymentDiscountDivinerConfigSchema, PaymentDiscountDivinerParams,\n} from '@xyo-network/payment-payload-plugins'\n\nimport { applyCoupons } from './lib/index.ts'\n\nconst DEFAULT_BOUND_WITNESS_DIVINER_QUERY_PROPS: Readonly<BoundWitnessDivinerQueryPayload> = {\n limit: 1,\n order: 'desc',\n schema: BoundWitnessDivinerQuerySchema,\n}\n\nexport type PaymentDiscountDivinerInputType = EscrowTerms | Coupon | HashLeaseEstimate | Payload\n\n@creatableModule()\nexport class PaymentDiscountDiviner<\n TParams extends PaymentDiscountDivinerParams = PaymentDiscountDivinerParams,\n TIn extends PaymentDiscountDivinerInputType = PaymentDiscountDivinerInputType,\n TOut extends Discount = Discount,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends AbstractDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [PaymentDiscountDivinerConfigSchema]\n static override defaultConfigSchema = PaymentDiscountDivinerConfigSchema\n\n protected get couponAuthorities(): Address[] {\n return [...(this.config.couponAuthorities ?? []), ...(this.params.couponAuthorities ?? [])]\n }\n\n protected async divineHandler(payloads: TIn[] = []): Promise<TOut[]> {\n const sources: Hash[] = []\n\n // Parse terms\n const terms = payloads.find(isEscrowTerms) as EscrowTerms | undefined\n if (!terms) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n sources.push(await PayloadBuilder.hash(terms))\n\n // Parse appraisals\n const termsAppraisals = terms?.appraisals\n // If the escrow terms do not have appraisals, return no discount\n if (!termsAppraisals || termsAppraisals.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n const hashMap = await PayloadBuilder.toAllHashMap(payloads) as Record<Hash, Payload>\n const appraisals = this.getEscrowAppraisals(terms, hashMap)\n // Add the appraisals that were found to the sources\n sources.push(...termsAppraisals)\n // If not all appraisals are found, return no discount\n if (appraisals.length !== termsAppraisals.length) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n\n // Parse coupons\n const coupons = await this.getEscrowDiscounts(terms, hashMap)\n // Add the coupons that were found to the sources\n // NOTE: Should we throw if not all coupons are found?\n const couponHashes = await PayloadBuilder.hashes(coupons)\n sources.push(...couponHashes)\n\n const validCoupons = await this.filterToSigned(coupons.filter(this.isCouponCurrent))\n // NOTE: Should we throw if not all coupons are valid?\n if (validCoupons.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]\n\n // TODO: Call paymentSubtotalDiviner to get the subtotal to centralize the logic\n const discount = applyCoupons(appraisals, validCoupons)\n return [{ ...discount, sources }] as TOut[]\n }\n\n /**\n * Filters the supplied list of coupons to only those that are signed by\n * addresses specified in the couponAuthorities\n * @param coupons The list of coupons to filter\n * @returns The filtered list of coupons that are signed by the couponAuthorities\n */\n protected async filterToSigned(coupons: Coupon[]): Promise<Coupon[]> {\n const signed: Coupon[] = []\n const dataHashMap = await PayloadBuilder.toDataHashMap(coupons)\n const boundWitnessDiviner = await this.getDiscountsBoundWitnessDiviner()\n const hashes = Object.keys(dataHashMap)\n const addresses = this.couponAuthorities\n // TODO: Keep an in memory cache of the hashes queried and their results\n // to avoid querying the same hash multiple times\n await Promise.all(hashes.map((h) => {\n const hash = h as Hash\n return Promise.all(addresses.map(async (address) => {\n const query: BoundWitnessDivinerQueryPayload = {\n ...DEFAULT_BOUND_WITNESS_DIVINER_QUERY_PROPS, addresses: [address], payload_hashes: [hash],\n }\n const result = await boundWitnessDiviner.divine([query])\n if (result.length > 0) signed.push(dataHashMap[hash])\n }))\n }))\n return signed\n }\n\n protected async getDiscountsArchivist(): Promise<ArchivistInstance> {\n const name = assertEx(this.config.archivist, () => 'Missing archivist in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving archivist: ${name}`)\n return assertEx(asArchivistInstance(mod), () => `Resolved module ${mod.address} not a valid Archivist`)\n }\n\n protected async getDiscountsBoundWitnessDiviner(): Promise<DivinerInstance> {\n const name = assertEx(this.config.boundWitnessDiviner, () => 'Missing boundWitnessDiviner in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving boundWitnessDiviner: ${name}`)\n return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`)\n }\n\n /**\n * Finds the appraisals specified by the escrow terms from the supplied payloads\n * @param terms The escrow terms\n * @param payloads The payloads to search for the appraisals\n * @returns The appraisals found in the payloads\n */\n protected getEscrowAppraisals(terms: EscrowTerms, hashMap: Record<Hash, Payload>): HashLeaseEstimate[] {\n const hashes = terms?.appraisals ?? []\n if (hashes.length === 0) return []\n return hashes.map(hash => hashMap[hash]).filter(exists).filter(isHashLeaseEstimate)\n }\n\n /**\n * Finds the discounts specified by the escrow terms from the supplied payloads\n * @param terms The escrow terms\n * @param hashMap The payloads to search for the discounts\n * @returns The discounts found in the payloads\n */\n protected async getEscrowDiscounts(terms: EscrowTerms, hashMap: Record<Hash, Payload>): Promise<Coupon[]> {\n // Parse discounts\n const hashes = terms.discounts ?? []\n if (hashes.length === 0) return []\n\n // Use the supplied payloads to find the discounts\n const discounts: Coupon[] = hashes.map(hash => hashMap[hash]).filter(exists).filter(isCoupon)\n const missing = hashes.filter(hash => !hashMap[hash])\n // If not all discounts are found\n if (missing.length > 0) {\n // Find any remaining from discounts archivist\n const discountsArchivist = await this.getDiscountsArchivist()\n const payloads = await discountsArchivist.get(missing)\n discounts.push(...payloads.filter(isCouponWithMeta))\n }\n // If not all discounts are found\n if (discounts.length !== hashes.length) {\n const termsHash = await PayloadBuilder.hash(terms)\n const foundHashes = await PayloadBuilder.hashes(discounts)\n // Log individual discounts that were not found\n for (const hash of hashes) {\n if (!foundHashes.includes(hash)) console.warn(`Discount ${hash} not found for terms ${termsHash}`)\n }\n }\n return discounts\n }\n\n protected isCouponCurrent(coupon: Coupon): boolean {\n const now = Date.now()\n return coupon.exp > now && coupon.nbf < now\n }\n}\n","import { assertEx } from '@xylabs/assert'\nimport { exists } from '@xylabs/exists'\nimport type { HashLeaseEstimate } from '@xyo-network/diviner-hash-lease'\nimport type {\n AmountFields,\n Coupon, Discount, FixedAmountCoupon,\n FixedPercentageCoupon,\n FixedPriceCoupon,\n} from '@xyo-network/payment-payload-plugins'\nimport {\n DiscountSchema, isFixedAmountCoupon, isFixedPercentageCoupon,\n isFixedPriceCoupon,\n isStackable,\n} from '@xyo-network/payment-payload-plugins'\n\nexport const applyCoupons = (appraisals: HashLeaseEstimate[], coupons: Coupon[]): Discount => {\n // Ensure all appraisals and coupons are in USD\n const allAppraisalsAreUSD = appraisals.every(appraisal => appraisal.currency === 'USD')\n assertEx(allAppraisalsAreUSD, 'All appraisals must be in USD')\n const allCouponsAreUSD = coupons.map(coupon => (coupon as Partial<AmountFields>)?.currency).filter(exists).every(currency => currency === 'USD')\n assertEx(allCouponsAreUSD, 'All coupons must be in USD')\n const total = appraisals.reduce((acc, appraisal) => acc + appraisal.price, 0)\n\n // Calculated non-stackable discount coupons\n const singularFixedDiscount = Math.max(...coupons\n .filter(coupon => isFixedAmountCoupon(coupon) && !isStackable(coupon))\n .map(coupon => (coupon as FixedAmountCoupon).amount), 0)\n const singularPercentageDiscount = (Math.max(...coupons\n .filter(coupon => isFixedPercentageCoupon(coupon) && !isStackable(coupon))\n .map(coupon => (coupon as FixedPercentageCoupon).percentage), 0)) * total\n const singularFixedPriceDiscount = calculateSingularFixedPriceDiscount(total, appraisals, coupons, false)\n\n // Calculate stackable discount coupons\n // First calculate the total discount from fixed amount coupons\n const stackedFixedDiscount = coupons\n .filter(coupon => isFixedAmountCoupon(coupon) && isStackable(coupon))\n .reduce((acc, coupon) => acc + (coupon as FixedAmountCoupon).amount, 0)\n // Then calculate the total discount from percentage coupons and apply\n // the percentage discount to the remaining total after fixed discounts\n const stackedPercentageDiscount = coupons\n .filter(coupon => isFixedPercentageCoupon(coupon) && isStackable(coupon))\n .reduce((acc, coupon) => acc + (coupon as FixedPercentageCoupon).percentage, 0) * (total - stackedFixedDiscount)\n // Then calculate the total discount from fixed price coupons\n const stackedFixedPriceDiscount = calculateSingularFixedPriceDiscount(total, appraisals, coupons, true)\n\n // Sum all stackable discounts\n const stackedDiscount = stackedFixedDiscount + stackedPercentageDiscount + stackedFixedPriceDiscount\n\n // Find the best coupon(s) to apply\n const maxDiscount = Math.max(\n singularFixedDiscount,\n singularFixedPriceDiscount,\n singularPercentageDiscount,\n stackedDiscount,\n 0,\n )\n\n // Ensure discount is not more than the total\n const amount = Math.min(maxDiscount, total)\n\n // Return single discount payload\n return {\n amount, schema: DiscountSchema, currency: 'USD',\n }\n}\n\nconst calculateSingularFixedPriceDiscount = (total: number, appraisals: HashLeaseEstimate[], coupons: Coupon[], stackable = false): number => {\n // Find all singular fixed price discounts\n const singularFixedPriceDiscounts = coupons\n .filter(isFixedPriceCoupon)\n .filter(coupon => stackable ? isStackable(coupon) : !isStackable(coupon))\n .map(coupon => (coupon as FixedPriceCoupon).amount)\n // Ensure all fixed price discounts are positive\n .filter(amount => amount > 0)\n\n // If there are no singular fixed price discounts, return no discount\n if (singularFixedPriceDiscounts.length === 0) return 0\n\n // Find the maximum discount (the lowest fixed price)\n const lowestFixedPrice = Math.min(...singularFixedPriceDiscounts)\n\n // Apply the fixed price to all appraisals to get the reduced prices\n const reducedPrices = appraisals.map(appraisal =>\n // If the appraisal price is less than the fixed price\n appraisal.price < lowestFixedPrice\n // Use the appraisal price as the coupon will result in a higher price\n ? appraisal.price\n // Otherwise, apply the fixed price discount\n : lowestFixedPrice)\n\n // Calculate the reduced total using the reduced prices\n const reducedTotal = reducedPrices.reduce((acc, price) => acc + price, 0)\n\n // Calculate the discount\n const discount = total - reducedTotal\n\n // Return the discount or 0 if the discount would have resulted in a negative value\n return discount > 0 ? discount : 0\n}\n","import type { Hash } from '@xylabs/hex'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport type { Payload, WithMeta } from '@xyo-network/payload-model'\nimport type { Coupon } from '@xyo-network/payment-payload-plugins'\nimport type { SchemaPayload } from '@xyo-network/schema-payload-plugin'\nimport { isSchemaPayloadWithMeta } from '@xyo-network/schema-payload-plugin'\nimport type { ValidateFunction } from 'ajv'\nimport { Ajv } from 'ajv'\n\n// TODO: Use our schema cache\nconst ajv = new Ajv({ strict: false }) // Create the Ajv instance once\nconst schemaCache = new Map() // Cache to store compiled validators\n\nexport const areConditionsFulfilled = async (coupon: Coupon, payloads: Payload[]): Promise<boolean> =>\n (await findUnfulfilledConditions(coupon, payloads)).length === 0\n\n// TODO: Should we separate conditions and payloads to prevent conflating data and \"operands\" (schemas to validate against data)?\n/**\n * Validates the conditions of a coupon against the provided payloads\n * @param coupon The coupon to check\n * @param payloads The associated payloads (containing the conditions and data to validate the conditions against)\n * @returns The unfulfilled condition hashes\n */\nexport const findUnfulfilledConditions = async (coupon: Coupon, payloads: Payload[]): Promise<Hash[]> => {\n const unfulfilledConditions: Hash[] = []\n // If there are no conditions, then they are fulfilled\n if (!coupon.conditions || coupon.conditions.length === 0) return unfulfilledConditions\n const hashMap = await PayloadBuilder.toAllHashMap(payloads)\n // Find all the conditions\n const conditions = coupon.conditions.map(hash => hashMap[hash]).filter(isSchemaPayloadWithMeta) as WithMeta<SchemaPayload>[]\n // Not all conditions were found\n if (conditions.length !== coupon.conditions.length) {\n const missing = coupon.conditions.filter(hash => !hashMap[hash])\n unfulfilledConditions.push(...missing)\n return unfulfilledConditions\n }\n\n // Test each condition\n for (const hash of coupon.conditions) {\n let validator: ValidateFunction\n\n // Check if the schema is already cached\n if (schemaCache.has(hash)) {\n validator = schemaCache.get(hash)\n } else {\n const payload = hashMap[hash]\n const definition = isSchemaPayloadWithMeta(payload) ? payload.definition : undefined\n if (definition) {\n // Compile and cache the validator\n validator = ajv.compile(definition)\n schemaCache.set(hash, validator)\n\n // Validate the payload\n } else {\n unfulfilledConditions.push(hash)\n continue\n }\n }\n if (!validator(payloads)) unfulfilledConditions.push(hash)\n }\n\n // All conditions passed\n return unfulfilledConditions\n}\n","import type { Hash } from '@xylabs/hex'\nimport type { DivinerInstance } from '@xyo-network/diviner-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport type { Payload } from '@xyo-network/payload-model'\nimport type {\n Discount, EscrowTerms, Invoice, Payment, Subtotal, Total,\n} from '@xyo-network/payment-payload-plugins'\nimport {\n isDiscount, isSubtotal, isTotal, PaymentSchema,\n} from '@xyo-network/payment-payload-plugins'\n\n/**\n * Validates the escrow terms to ensure they are valid for a purchase\n * @returns A payment if the terms are valid for a purchase, undefined otherwise\n */\nexport const getInvoiceForEscrow = async (\n terms: EscrowTerms,\n dataHashMap: Record<Hash, Payload>,\n paymentTotalDiviner: DivinerInstance,\n): Promise<Invoice | undefined> => {\n const payloads = Object.values(dataHashMap)\n const results = await paymentTotalDiviner.divine([terms, ...payloads])\n const subtotal = results.find(isSubtotal) as Subtotal | undefined\n const discount = results.find(isDiscount) as Discount | undefined\n const total = results.find(isTotal) as Total | undefined\n if (!subtotal || !total) return undefined\n const { amount, currency } = total\n if (currency !== 'USD') return undefined\n const sources = await getSources(terms, subtotal, total, discount)\n const payment: Payment = {\n amount, currency, schema: PaymentSchema, sources,\n }\n return discount ? [subtotal, total, payment, discount] : [subtotal, total, payment]\n}\n\nconst getSources = async (terms: EscrowTerms, subtotal: Subtotal, total: Total, discount?: Discount): Promise<Hash[]> => {\n const sources = discount ? [terms, subtotal, total, discount] : [terms, subtotal, total]\n return await Promise.all(sources.map(p => PayloadBuilder.dataHash(p)))\n}\n","import { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport { HashLeaseEstimate, isHashLeaseEstimate } from '@xyo-network/diviner-hash-lease'\nimport { DivinerInstance, DivinerModuleEventData } from '@xyo-network/diviner-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport { PayloadBuilder } from '@xyo-network/payload-builder'\nimport { Payload } from '@xyo-network/payload-model'\nimport {\n EscrowTerms, isEscrowTerms, PaymentSubtotalDivinerConfigSchema, PaymentSubtotalDivinerParams, Subtotal, SubtotalSchema,\n} from '@xyo-network/payment-payload-plugins'\n\nimport {\n appraisalValidators, termsValidators, ValidEscrowTerms,\n} from './lib/index.ts'\n\nconst currency = 'USD'\n\n/**\n * Escrow terms that contain all the valid fields for calculating a subtotal\n */\nexport type PaymentSubtotalDivinerInputType = EscrowTerms | HashLeaseEstimate | Payload\n\n@creatableModule()\nexport class PaymentSubtotalDiviner<\n TParams extends PaymentSubtotalDivinerParams = PaymentSubtotalDivinerParams,\n TIn extends PaymentSubtotalDivinerInputType = PaymentSubtotalDivinerInputType,\n TOut extends Subtotal = Subtotal,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends AbstractDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [PaymentSubtotalDivinerConfigSchema]\n static override defaultConfigSchema = PaymentSubtotalDivinerConfigSchema\n\n protected async divineHandler(payloads: TIn[] = []): Promise<TOut[]> {\n // Find the escrow terms\n const terms = payloads.find(isEscrowTerms) as EscrowTerms | undefined\n if (!terms) return []\n\n // Run all terms validations\n if (!termsValidators.every(validator => validator(terms))) return []\n const validTerms = terms as ValidEscrowTerms\n\n // Retrieve all appraisals from terms\n const hashMap = await PayloadBuilder.toAllHashMap(payloads)\n const appraisals = validTerms.appraisals.map(appraisal => hashMap[appraisal]).filter(isHashLeaseEstimate) as unknown as HashLeaseEstimate[]\n\n // Ensure all appraisals are present\n if (appraisals.length !== validTerms.appraisals.length) return []\n\n // Run all appraisal validations\n if (!appraisalValidators.every(validator => validator(appraisals))) return []\n const amount = calculateSubtotal(appraisals)\n const sources = [await PayloadBuilder.dataHash(validTerms), ...validTerms.appraisals]\n return [{\n amount, currency, schema: SubtotalSchema, sources,\n }] as TOut[]\n }\n}\n\n// TODO: Add support for other currencies\nconst calculateSubtotal = (appraisals: HashLeaseEstimate[]): number => {\n return appraisals.reduce((sum, appraisal) => sum + appraisal.price, 0)\n}\n","import type { HashLeaseEstimate } from '@xyo-network/diviner-hash-lease'\nimport { isIso4217CurrencyCode } from '@xyo-network/payment-payload-plugins'\n\nimport { validateDuration } from './durationValidators.ts'\n\nconst validateAppraisalAmount = (appraisals: HashLeaseEstimate[]): boolean => {\n // Ensure all appraisals are numeric\n if (appraisals.some(appraisal => typeof appraisal.price !== 'number')) return false\n // Ensure all appraisals are positive numbers\n if (appraisals.some(appraisal => appraisal.price < 0)) return false\n return true\n}\n\nconst validateAppraisalCurrency = (appraisals: HashLeaseEstimate[]): boolean => {\n // NOTE: Only supporting USD for now, the remaining checks are for future-proofing.\n if (!appraisals.every(appraisal => appraisal.currency == 'USD')) return false\n\n // Check every object in the array to ensure they all are in a supported currency.\n if (!appraisals.every(appraisal => isIso4217CurrencyCode(appraisal.currency))) return false\n\n return true\n}\n\nconst validateAppraisalConsistentCurrency = (appraisals: HashLeaseEstimate[]): boolean => {\n // Check if the array is empty or contains only one element, no need to compare.\n if (appraisals.length <= 1) return true\n\n // Get the currency of the first element to compare with others.\n const { currency } = appraisals[0]\n if (!currency) return false\n\n // Check every object in the array to ensure they all have the same currency.\n if (!appraisals.every(item => item.currency === currency)) return false\n\n return true\n}\n\nconst validateAppraisalWindow = (appraisals: HashLeaseEstimate[]): boolean => appraisals.every(validateDuration)\n\nexport const appraisalValidators = [\n validateAppraisalAmount,\n validateAppraisalCurrency,\n validateAppraisalConsistentCurrency,\n validateAppraisalWindow,\n]\n","import type { DurationFields } from '@xyo-network/xns-record-payload-plugins'\n\nconst FIVE_MINUTES = 1000 * 60 * 5\n\n/**\n * Validates that the current time is within the duration window, within a configurable a buffer\n * @param value The duration value\n * @param windowMs The window in milliseconds to allow for a buffer\n * @returns True if the duration is valid, false otherwise\n */\nexport const validateDuration = (value: Partial<DurationFields>, windowMs = FIVE_MINUTES): boolean => {\n const now = Date.now()\n if (!value.nbf || value.nbf > now) return false\n // If already expired (include for a 5 minute buffer to allow for a reasonable\n // minimum amount of time for the transaction to be processed)\n if (!value.exp || value.exp - now < windowMs) return false\n return true\n}\n","import type { Hash } from '@xylabs/hex'\nimport type { EscrowTerms } from '@xyo-network/payment-payload-plugins'\n\nimport { validateDuration } from './durationValidators.ts'\n\nexport type ValidEscrowTerms = Required<EscrowTerms>\n\nconst validateTermsAppraisals = (terms: EscrowTerms): terms is Required<EscrowTerms & { appraisals: Hash[] }> => {\n if (!terms.appraisals) return false\n if (terms.appraisals.length === 0) return false\n return true\n}\nconst validateTermsWindow = (terms: EscrowTerms): boolean => validateDuration(terms)\n\nexport const termsValidators = [\n validateTermsAppraisals,\n validateTermsWindow,\n]\n","import { assertEx } from '@xylabs/assert'\nimport { Hash } from '@xylabs/hex'\nimport { AbstractDiviner } from '@xyo-network/diviner-abstract'\nimport {\n asDivinerInstance, DivinerInstance, DivinerModuleEventData,\n} from '@xyo-network/diviner-model'\nimport { creatableModule } from '@xyo-network/module-model'\nimport {\n Discount,\n isDiscountWithMeta,\n isSubtotalWithMeta,\n PaymentTotalDivinerConfigSchema, PaymentTotalDivinerParams, Subtotal, Total, TotalSchema,\n} from '@xyo-network/payment-payload-plugins'\n\nimport { PaymentDiscountDiviner, PaymentDiscountDivinerInputType } from '../Discount/index.ts'\nimport { PaymentSubtotalDiviner, PaymentSubtotalDivinerInputType } from '../Subtotal/index.ts'\n\ntype InputType = PaymentDiscountDivinerInputType | PaymentSubtotalDivinerInputType\ntype OutputType = Subtotal | Discount | Total\n\n@creatableModule()\nexport class PaymentTotalDiviner<\n TParams extends PaymentTotalDivinerParams = PaymentTotalDivinerParams,\n TIn extends InputType = InputType,\n TOut extends OutputType = OutputType,\n TEventData extends DivinerModuleEventData<DivinerInstance<TParams, TIn, TOut>, TIn, TOut> = DivinerModuleEventData<\n DivinerInstance<TParams, TIn, TOut>,\n TIn,\n TOut\n >,\n> extends AbstractDiviner<TParams, TIn, TOut, TEventData> {\n static override configSchemas = [PaymentTotalDivinerConfigSchema]\n static override defaultConfigSchema: PaymentTotalDivinerConfigSchema = PaymentTotalDivinerConfigSchema\n\n protected async divineHandler(payloads: TIn[] = []): Promise<TOut[]> {\n const subtotalDiviner = await this.getPaymentSubtotalDiviner()\n const subtotalResult = await subtotalDiviner.divine(payloads)\n const subtotal = subtotalResult.find(isSubtotalWithMeta)\n if (!subtotal) return []\n const discountDiviner = await this.getPaymentDiscountsDiviner()\n const discountResult = await discountDiviner.divine(payloads)\n const discount = discountResult.find(isDiscountWithMeta)\n if (!discount) return []\n const { currency: subtotalCurrency } = subtotal\n const { currency: discountCurrency } = discount\n assertEx(subtotalCurrency === discountCurrency, () => `Subtotal currency ${subtotalCurrency} does not match discount currency ${discountCurrency}`)\n const amount = Math.max(0, subtotal.amount - discount.amount)\n const currency = subtotalCurrency\n const sources = [subtotal.$hash, discount.$hash] as Hash[]\n const total: Total = {\n amount, currency, sources, schema: TotalSchema,\n }\n return [subtotal, discount, total] as TOut[]\n }\n\n protected async getPaymentDiscountsDiviner(): Promise<PaymentDiscountDiviner> {\n const name = assertEx(this.config.paymentDiscountDiviner, () => 'Missing paymentDiscountDiviner in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving paymentDiscountDiviner: ${name}`)\n return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`) as PaymentDiscountDiviner\n }\n\n protected async getPaymentSubtotalDiviner(): Promise<PaymentSubtotalDiviner> {\n const name = assertEx(this.config.paymentSubtotalDiviner, () => 'Missing paymentSubtotalDiviner in config')\n const mod = assertEx(await this.resolve(name), () => `Error resolving paymentSubtotalDiviner: ${name}`)\n return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`) as PaymentSubtotalDiviner\n }\n}\n"],"mappings":"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","names":["assertEx","exists","PayloadBuilder","currency","PayloadBuilder","assertEx","exists","PayloadBuilder","currency","AbstractDiviner","isHashLeaseEstimate","creatableModule","PayloadBuilder","isEscrowTerms","currency","AbstractDiviner","isEscrowTerms","PayloadBuilder","isHashLeaseEstimate","creatableModule","assertEx","AbstractDiviner","asDivinerInstance","creatableModule","AbstractDiviner","assertEx","currency","asDivinerInstance","creatableModule"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/payment-plugin",
3
- "version": "3.0.18",
3
+ "version": "3.0.20",
4
4
  "description": "Typescript/Javascript Plugins for XYO Platform",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -29,28 +29,31 @@
29
29
  "module": "dist/neutral/index.mjs",
30
30
  "types": "dist/neutral/index.d.ts",
31
31
  "dependencies": {
32
- "@xylabs/assert": "^4.0.9",
32
+ "@xylabs/assert": "^4.0.10",
33
33
  "@xylabs/exists": "^4.0.10",
34
34
  "@xylabs/hex": "^4.0.10",
35
- "@xyo-network/archivist-model": "^3.1.9",
36
- "@xyo-network/diviner-abstract": "^3.1.9",
37
- "@xyo-network/diviner-boundwitness-model": "^3.1.9",
38
- "@xyo-network/diviner-hash-lease": "^3.1.9",
39
- "@xyo-network/diviner-model": "^3.1.9",
40
- "@xyo-network/module-model": "^3.1.9",
41
- "@xyo-network/payload-builder": "^3.1.9",
42
- "@xyo-network/payload-model": "^3.1.9",
43
- "@xyo-network/payment-payload-plugins": "^3.0.18",
44
- "@xyo-network/xns-record-payload-plugins": "^3.0.18"
35
+ "@xyo-network/archivist-model": "^3.1.10",
36
+ "@xyo-network/diviner-abstract": "^3.1.10",
37
+ "@xyo-network/diviner-boundwitness-model": "^3.1.10",
38
+ "@xyo-network/diviner-hash-lease": "^3.1.10",
39
+ "@xyo-network/diviner-model": "^3.1.10",
40
+ "@xyo-network/module-model": "^3.1.10",
41
+ "@xyo-network/payload-builder": "^3.1.10",
42
+ "@xyo-network/payload-model": "^3.1.10",
43
+ "@xyo-network/payment-payload-plugins": "^3.0.20",
44
+ "@xyo-network/schema-payload-plugin": "^3.1.10",
45
+ "@xyo-network/xns-record-payload-plugins": "^3.0.20",
46
+ "ajv": "^8.17.1"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@xylabs/ts-scripts-yarn3": "^4.0.7",
48
50
  "@xylabs/tsconfig": "^4.0.7",
49
- "@xyo-network/account": "^3.1.9",
50
- "@xyo-network/archivist-memory": "^3.1.9",
51
- "@xyo-network/boundwitness-builder": "^3.1.9",
52
- "@xyo-network/diviner-boundwitness-memory": "^3.1.9",
53
- "@xyo-network/node-memory": "^3.1.9",
51
+ "@xyo-network/account": "^3.1.10",
52
+ "@xyo-network/archivist-memory": "^3.1.10",
53
+ "@xyo-network/boundwitness-builder": "^3.1.10",
54
+ "@xyo-network/diviner-boundwitness-memory": "^3.1.10",
55
+ "@xyo-network/id-payload-plugin": "^3.1.10",
56
+ "@xyo-network/node-memory": "^3.1.10",
54
57
  "jest": "^29.7.0",
55
58
  "typescript": "^5.5.4",
56
59
  "vitest": "^2.0.5"
@@ -58,53 +58,29 @@ export class PaymentDiscountDiviner<
58
58
  if (!terms) return [{ ...NO_DISCOUNT, sources }] as TOut[]
59
59
  sources.push(await PayloadBuilder.hash(terms))
60
60
 
61
- // Parse discounts
62
- const discountHashes = terms.discounts ?? []
63
- if (discountHashes.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]
64
-
65
- // TODO: Call paymentSubtotalDiviner to get the subtotal to centralize the logic
66
61
  // Parse appraisals
67
62
  const termsAppraisals = terms?.appraisals
63
+ // If the escrow terms do not have appraisals, return no discount
68
64
  if (!termsAppraisals || termsAppraisals.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]
69
- const hashMap = await PayloadBuilder.toAllHashMap(payloads)
70
- const foundAppraisals = termsAppraisals.filter(hash => hashMap[hash])
65
+ const hashMap = await PayloadBuilder.toAllHashMap(payloads) as Record<Hash, Payload>
66
+ const appraisals = this.getEscrowAppraisals(terms, hashMap)
71
67
  // Add the appraisals that were found to the sources
72
- sources.push(...foundAppraisals)
68
+ sources.push(...termsAppraisals)
73
69
  // If not all appraisals are found, return no discount
74
- if (foundAppraisals.length !== termsAppraisals.length) {
75
- return [{ ...NO_DISCOUNT, sources }] as TOut[]
76
- }
77
- // TODO: Cast should not be required
78
- const appraisals = foundAppraisals.map(hash => hashMap[hash]).filter(exists).filter(isHashLeaseEstimate) as unknown as HashLeaseEstimate[]
79
-
80
- // Use the supplied payloads to find the discounts
81
- const discounts = discountHashes.map(hash => hashMap[hash]).filter(exists).filter(isCoupon) as Coupon[]
82
- // Find any remaining coupons from the archivist
83
- if (discounts.length !== discountHashes.length) {
84
- // Find remaining from discounts archivist
85
- const discountsArchivist = await this.getDiscountsArchivist()
86
- const foundDiscounts = await discountsArchivist.get(discountHashes)
87
- discounts.push(...foundDiscounts.filter(isCouponWithMeta))
88
- }
89
- const discountsMap = await PayloadBuilder.toAllHashMap(discounts)
90
- if (Object.keys(discountsMap).length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]
91
-
92
- // Add the found discounts to the sources
93
- const foundDiscountsHashes = Object.keys(discountsMap) as Hash[]
94
- sources.push(...foundDiscountsHashes)
95
-
96
- // Log individual discounts that were not found
97
- for (const hash of discountHashes) {
98
- if (!foundDiscountsHashes.includes(hash)) {
99
- console.warn(`Discount ${hash} not found for terms ${await PayloadBuilder.hash(terms)}`)
100
- }
101
- }
70
+ if (appraisals.length !== termsAppraisals.length) return [{ ...NO_DISCOUNT, sources }] as TOut[]
102
71
 
103
72
  // Parse coupons
104
- const coupons = Object.values(discountsMap)
73
+ const coupons = await this.getEscrowDiscounts(terms, hashMap)
74
+ // Add the coupons that were found to the sources
75
+ // NOTE: Should we throw if not all coupons are found?
76
+ const couponHashes = await PayloadBuilder.hashes(coupons)
77
+ sources.push(...couponHashes)
78
+
105
79
  const validCoupons = await this.filterToSigned(coupons.filter(this.isCouponCurrent))
80
+ // NOTE: Should we throw if not all coupons are valid?
106
81
  if (validCoupons.length === 0) return [{ ...NO_DISCOUNT, sources }] as TOut[]
107
82
 
83
+ // TODO: Call paymentSubtotalDiviner to get the subtotal to centralize the logic
108
84
  const discount = applyCoupons(appraisals, validCoupons)
109
85
  return [{ ...discount, sources }] as TOut[]
110
86
  }
@@ -148,6 +124,51 @@ export class PaymentDiscountDiviner<
148
124
  return assertEx(asDivinerInstance(mod), () => `Resolved module ${mod.address} not a valid Diviner`)
149
125
  }
150
126
 
127
+ /**
128
+ * Finds the appraisals specified by the escrow terms from the supplied payloads
129
+ * @param terms The escrow terms
130
+ * @param payloads The payloads to search for the appraisals
131
+ * @returns The appraisals found in the payloads
132
+ */
133
+ protected getEscrowAppraisals(terms: EscrowTerms, hashMap: Record<Hash, Payload>): HashLeaseEstimate[] {
134
+ const hashes = terms?.appraisals ?? []
135
+ if (hashes.length === 0) return []
136
+ return hashes.map(hash => hashMap[hash]).filter(exists).filter(isHashLeaseEstimate)
137
+ }
138
+
139
+ /**
140
+ * Finds the discounts specified by the escrow terms from the supplied payloads
141
+ * @param terms The escrow terms
142
+ * @param hashMap The payloads to search for the discounts
143
+ * @returns The discounts found in the payloads
144
+ */
145
+ protected async getEscrowDiscounts(terms: EscrowTerms, hashMap: Record<Hash, Payload>): Promise<Coupon[]> {
146
+ // Parse discounts
147
+ const hashes = terms.discounts ?? []
148
+ if (hashes.length === 0) return []
149
+
150
+ // Use the supplied payloads to find the discounts
151
+ const discounts: Coupon[] = hashes.map(hash => hashMap[hash]).filter(exists).filter(isCoupon)
152
+ const missing = hashes.filter(hash => !hashMap[hash])
153
+ // If not all discounts are found
154
+ if (missing.length > 0) {
155
+ // Find any remaining from discounts archivist
156
+ const discountsArchivist = await this.getDiscountsArchivist()
157
+ const payloads = await discountsArchivist.get(missing)
158
+ discounts.push(...payloads.filter(isCouponWithMeta))
159
+ }
160
+ // If not all discounts are found
161
+ if (discounts.length !== hashes.length) {
162
+ const termsHash = await PayloadBuilder.hash(terms)
163
+ const foundHashes = await PayloadBuilder.hashes(discounts)
164
+ // Log individual discounts that were not found
165
+ for (const hash of hashes) {
166
+ if (!foundHashes.includes(hash)) console.warn(`Discount ${hash} not found for terms ${termsHash}`)
167
+ }
168
+ }
169
+ return discounts
170
+ }
171
+
151
172
  protected isCouponCurrent(coupon: Coupon): boolean {
152
173
  const now = Date.now()
153
174
  return coupon.exp > now && coupon.nbf < now
@@ -5,9 +5,11 @@ import type {
5
5
  AmountFields,
6
6
  Coupon, Discount, FixedAmountCoupon,
7
7
  FixedPercentageCoupon,
8
+ FixedPriceCoupon,
8
9
  } from '@xyo-network/payment-payload-plugins'
9
10
  import {
10
11
  DiscountSchema, isFixedAmountCoupon, isFixedPercentageCoupon,
12
+ isFixedPriceCoupon,
11
13
  isStackable,
12
14
  } from '@xyo-network/payment-payload-plugins'
13
15
 
@@ -26,6 +28,7 @@ export const applyCoupons = (appraisals: HashLeaseEstimate[], coupons: Coupon[])
26
28
  const singularPercentageDiscount = (Math.max(...coupons
27
29
  .filter(coupon => isFixedPercentageCoupon(coupon) && !isStackable(coupon))
28
30
  .map(coupon => (coupon as FixedPercentageCoupon).percentage), 0)) * total
31
+ const singularFixedPriceDiscount = calculateSingularFixedPriceDiscount(total, appraisals, coupons, false)
29
32
 
30
33
  // Calculate stackable discount coupons
31
34
  // First calculate the total discount from fixed amount coupons
@@ -37,12 +40,16 @@ export const applyCoupons = (appraisals: HashLeaseEstimate[], coupons: Coupon[])
37
40
  const stackedPercentageDiscount = coupons
38
41
  .filter(coupon => isFixedPercentageCoupon(coupon) && isStackable(coupon))
39
42
  .reduce((acc, coupon) => acc + (coupon as FixedPercentageCoupon).percentage, 0) * (total - stackedFixedDiscount)
43
+ // Then calculate the total discount from fixed price coupons
44
+ const stackedFixedPriceDiscount = calculateSingularFixedPriceDiscount(total, appraisals, coupons, true)
45
+
40
46
  // Sum all stackable discounts
41
- const stackedDiscount = stackedFixedDiscount + stackedPercentageDiscount
47
+ const stackedDiscount = stackedFixedDiscount + stackedPercentageDiscount + stackedFixedPriceDiscount
42
48
 
43
49
  // Find the best coupon(s) to apply
44
50
  const maxDiscount = Math.max(
45
51
  singularFixedDiscount,
52
+ singularFixedPriceDiscount,
46
53
  singularPercentageDiscount,
47
54
  stackedDiscount,
48
55
  0,
@@ -56,3 +63,37 @@ export const applyCoupons = (appraisals: HashLeaseEstimate[], coupons: Coupon[])
56
63
  amount, schema: DiscountSchema, currency: 'USD',
57
64
  }
58
65
  }
66
+
67
+ const calculateSingularFixedPriceDiscount = (total: number, appraisals: HashLeaseEstimate[], coupons: Coupon[], stackable = false): number => {
68
+ // Find all singular fixed price discounts
69
+ const singularFixedPriceDiscounts = coupons
70
+ .filter(isFixedPriceCoupon)
71
+ .filter(coupon => stackable ? isStackable(coupon) : !isStackable(coupon))
72
+ .map(coupon => (coupon as FixedPriceCoupon).amount)
73
+ // Ensure all fixed price discounts are positive
74
+ .filter(amount => amount > 0)
75
+
76
+ // If there are no singular fixed price discounts, return no discount
77
+ if (singularFixedPriceDiscounts.length === 0) return 0
78
+
79
+ // Find the maximum discount (the lowest fixed price)
80
+ const lowestFixedPrice = Math.min(...singularFixedPriceDiscounts)
81
+
82
+ // Apply the fixed price to all appraisals to get the reduced prices
83
+ const reducedPrices = appraisals.map(appraisal =>
84
+ // If the appraisal price is less than the fixed price
85
+ appraisal.price < lowestFixedPrice
86
+ // Use the appraisal price as the coupon will result in a higher price
87
+ ? appraisal.price
88
+ // Otherwise, apply the fixed price discount
89
+ : lowestFixedPrice)
90
+
91
+ // Calculate the reduced total using the reduced prices
92
+ const reducedTotal = reducedPrices.reduce((acc, price) => acc + price, 0)
93
+
94
+ // Calculate the discount
95
+ const discount = total - reducedTotal
96
+
97
+ // Return the discount or 0 if the discount would have resulted in a negative value
98
+ return discount > 0 ? discount : 0
99
+ }
@@ -0,0 +1,64 @@
1
+ import type { Hash } from '@xylabs/hex'
2
+ import { PayloadBuilder } from '@xyo-network/payload-builder'
3
+ import type { Payload, WithMeta } from '@xyo-network/payload-model'
4
+ import type { Coupon } from '@xyo-network/payment-payload-plugins'
5
+ import type { SchemaPayload } from '@xyo-network/schema-payload-plugin'
6
+ import { isSchemaPayloadWithMeta } from '@xyo-network/schema-payload-plugin'
7
+ import type { ValidateFunction } from 'ajv'
8
+ import { Ajv } from 'ajv'
9
+
10
+ // TODO: Use our schema cache
11
+ const ajv = new Ajv({ strict: false }) // Create the Ajv instance once
12
+ const schemaCache = new Map() // Cache to store compiled validators
13
+
14
+ export const areConditionsFulfilled = async (coupon: Coupon, payloads: Payload[]): Promise<boolean> =>
15
+ (await findUnfulfilledConditions(coupon, payloads)).length === 0
16
+
17
+ // TODO: Should we separate conditions and payloads to prevent conflating data and "operands" (schemas to validate against data)?
18
+ /**
19
+ * Validates the conditions of a coupon against the provided payloads
20
+ * @param coupon The coupon to check
21
+ * @param payloads The associated payloads (containing the conditions and data to validate the conditions against)
22
+ * @returns The unfulfilled condition hashes
23
+ */
24
+ export const findUnfulfilledConditions = async (coupon: Coupon, payloads: Payload[]): Promise<Hash[]> => {
25
+ const unfulfilledConditions: Hash[] = []
26
+ // If there are no conditions, then they are fulfilled
27
+ if (!coupon.conditions || coupon.conditions.length === 0) return unfulfilledConditions
28
+ const hashMap = await PayloadBuilder.toAllHashMap(payloads)
29
+ // Find all the conditions
30
+ const conditions = coupon.conditions.map(hash => hashMap[hash]).filter(isSchemaPayloadWithMeta) as WithMeta<SchemaPayload>[]
31
+ // Not all conditions were found
32
+ if (conditions.length !== coupon.conditions.length) {
33
+ const missing = coupon.conditions.filter(hash => !hashMap[hash])
34
+ unfulfilledConditions.push(...missing)
35
+ return unfulfilledConditions
36
+ }
37
+
38
+ // Test each condition
39
+ for (const hash of coupon.conditions) {
40
+ let validator: ValidateFunction
41
+
42
+ // Check if the schema is already cached
43
+ if (schemaCache.has(hash)) {
44
+ validator = schemaCache.get(hash)
45
+ } else {
46
+ const payload = hashMap[hash]
47
+ const definition = isSchemaPayloadWithMeta(payload) ? payload.definition : undefined
48
+ if (definition) {
49
+ // Compile and cache the validator
50
+ validator = ajv.compile(definition)
51
+ schemaCache.set(hash, validator)
52
+
53
+ // Validate the payload
54
+ } else {
55
+ unfulfilledConditions.push(hash)
56
+ continue
57
+ }
58
+ }
59
+ if (!validator(payloads)) unfulfilledConditions.push(hash)
60
+ }
61
+
62
+ // All conditions passed
63
+ return unfulfilledConditions
64
+ }
@@ -1 +1,2 @@
1
1
  export * from './applyCoupons.ts'
2
+ export * from './findUnfulfilledConditions.ts'