@ftptech/x402-canton-core 0.1.0 → 0.1.1
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/amount.d.ts +89 -0
- package/dist/amount.d.ts.map +1 -0
- package/dist/amount.js +155 -0
- package/dist/amount.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/resource-url.d.ts +11 -0
- package/dist/resource-url.d.ts.map +1 -0
- package/dist/resource-url.js +36 -0
- package/dist/resource-url.js.map +1 -0
- package/dist/types.d.ts +248 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +112 -12
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/amount.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canton Coin amount-unit conversion (x402-ENVELOPE upstream convention).
|
|
3
|
+
*
|
|
4
|
+
* The x402 v2 wire field `PaymentRequirements.maxAmountRequired` (this scheme's
|
|
5
|
+
* `amount`) travels in ATOMIC UNITS as an integer string, while the on-ledger
|
|
6
|
+
* Daml `Decimal` the Token Standard / TransferCommand uses is a fixed-scale
|
|
7
|
+
* decimal string. The conversion boundary is fixed across CIP-56 by the Daml
|
|
8
|
+
* `Decimal` scale: **1 CC = 10^10 atomic units** (10 decimal places).
|
|
9
|
+
*
|
|
10
|
+
* BACK-COMPAT (read this): the DEPLOYED v1 (external-party-amulet-rules, live on
|
|
11
|
+
* MainNet) and cip56 paths put a Daml **Decimal** string on the x402 wire today
|
|
12
|
+
* (e.g. `"0.0100000000"`), NOT atomic integer units — every deployed client
|
|
13
|
+
* signs the Decimal verbatim into the ledger and the facilitator compares it
|
|
14
|
+
* byte-for-byte. These helpers therefore exist so a deploy that OPTS IN to the
|
|
15
|
+
* atomic-on-wire convention can convert EXACTLY at the boundary; they do NOT
|
|
16
|
+
* change the default wire unit (see specs/scheme_exact_canton.upstream.md
|
|
17
|
+
* § Amount units). The maths is pure BigInt/string so the round-trip never
|
|
18
|
+
* drifts by 10^10 (the off-by-scale footgun) and never goes through float.
|
|
19
|
+
*
|
|
20
|
+
* Mirrors `packages/pay-proxy/src/spend-budget-store.ts`'s `toAtomic` (the same
|
|
21
|
+
* "compare as BigInt atomic, never Number()" guardrail) but adds the inverse and
|
|
22
|
+
* a strict integer-atomic parser, and lives in core so client + facilitator
|
|
23
|
+
* share one implementation.
|
|
24
|
+
*/
|
|
25
|
+
/** Daml Decimal scale for Canton Coin / Amulet — 10 fractional digits. */
|
|
26
|
+
export declare const CC_ATOMIC_SCALE = 10;
|
|
27
|
+
/**
|
|
28
|
+
* Convert a Daml Decimal CC string (e.g. `"0.1"`, `"1.0000000000"`) to integer
|
|
29
|
+
* atomic units as a decimal string (e.g. `"1000000000"`, `"10000000000"`).
|
|
30
|
+
*
|
|
31
|
+
* Fail-CLOSED: throws on a non-numeric value, a negative value, or a fractional
|
|
32
|
+
* part with MORE than `CC_ATOMIC_SCALE` significant digits (which would lose
|
|
33
|
+
* precision) — a malformed amount must never silently produce a wrong integer.
|
|
34
|
+
* Trailing zeros within the scale are fine (`"0.1"` and `"0.1000000000"` both
|
|
35
|
+
* → `"1000000000"`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function decimalToAtomicCC(dec: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Convert integer atomic units (decimal string, e.g. `"1"`, `"10000000000"`) to
|
|
40
|
+
* a fixed-scale Daml Decimal CC string with exactly `CC_ATOMIC_SCALE` fractional
|
|
41
|
+
* digits (e.g. `"0.0000000001"`, `"1.0000000000"`).
|
|
42
|
+
*
|
|
43
|
+
* Fail-CLOSED: throws on a non-integer / non-numeric / negative input. The
|
|
44
|
+
* output is the canonical fixed-scale form the Token Standard ledger uses, so
|
|
45
|
+
* `decimalToAtomicCC(atomicToDecimalCC(x)) === x` and
|
|
46
|
+
* `atomicToDecimalCC(decimalToAtomicCC(d))` is `d` normalized to 10 dp.
|
|
47
|
+
*/
|
|
48
|
+
export declare function atomicToDecimalCC(atomic: string): string;
|
|
49
|
+
/** True iff this x402 scheme string carries the amount in ATOMIC integer units
|
|
50
|
+
* on the wire ("exact" — the x402-ENVELOPE convention). The legacy
|
|
51
|
+
* "exact-canton" (and anything else) carries a Daml Decimal. Mirrors
|
|
52
|
+
* `schemeMatches`'s "exact" is canonical / "exact-canton" is legacy split. */
|
|
53
|
+
export declare function schemeIsAtomic(scheme: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Convert an x402 WIRE amount to the on-ledger Daml **Decimal** it denotes,
|
|
56
|
+
* keyed by the wire's scheme. Atomic scheme ("exact") => `atomicToDecimalCC`;
|
|
57
|
+
* legacy/Decimal scheme ("exact-canton", anything else) => passthrough (the wire
|
|
58
|
+
* value already IS the Decimal). Fail-CLOSED via the underlying converter (a
|
|
59
|
+
* non-integer atomic value, or a malformed Decimal at a later comparison,
|
|
60
|
+
* throws rather than silently mis-comparing). Use this at EVERY site that
|
|
61
|
+
* compares a wire amount against an on-ledger Decimal.
|
|
62
|
+
*/
|
|
63
|
+
export declare function wireAmountToLedgerDecimal(scheme: string, wireAmount: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* Inverse of `wireAmountToLedgerDecimal`: given an on-ledger Daml Decimal,
|
|
66
|
+
* produce the WIRE amount for the given scheme. Atomic scheme ("exact") =>
|
|
67
|
+
* `decimalToAtomicCC`; legacy/Decimal scheme => passthrough. Used by the client
|
|
68
|
+
* builder / signer seam when emitting under the atomic scheme.
|
|
69
|
+
*/
|
|
70
|
+
export declare function ledgerDecimalToWireAmount(scheme: string, decimal: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Canonical VALUE-equality of two on-ledger Daml CC Decimal strings: compares
|
|
73
|
+
* by BigInt atomic units so "0.1" ≡ "0.1000000000" and a 10x/0.1x difference
|
|
74
|
+
* provably never folds. Fail-CLOSED: a malformed Decimal on either side throws
|
|
75
|
+
* (via `decimalToAtomicCC`), so a comparison can never silently pass on junk.
|
|
76
|
+
* This is the exactness guarantee the spec asks for at every amount-compare site
|
|
77
|
+
* (replaces a raw string `!==`).
|
|
78
|
+
*/
|
|
79
|
+
export declare function ledgerDecimalEquals(a: string, b: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Non-throwing, FAIL-CLOSED variant of `ledgerDecimalEquals` for the
|
|
82
|
+
* facilitator amount-validation arms: returns `true` only when both inputs are
|
|
83
|
+
* well-formed Daml CC Decimals of equal value; a malformed/junk value on either
|
|
84
|
+
* side returns `false` (→ amount_mismatch) instead of throwing. Use this at the
|
|
85
|
+
* pure validator sites (validateTransferCommand / validateAllocation /
|
|
86
|
+
* validateCip56*) that must map to a discriminated reject, never a 5xx.
|
|
87
|
+
*/
|
|
88
|
+
export declare function ledgerDecimalsMatch(a: string, b: string): boolean;
|
|
89
|
+
//# sourceMappingURL=amount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.d.ts","sourceRoot":"","sources":["../src/amount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,eAAe,KAAK,CAAC;AAIlC;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAsBrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASxD;AAwBD;;;+EAG+E;AAC/E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,MAAM,CAER;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAMjE"}
|
package/dist/amount.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canton Coin amount-unit conversion (x402-ENVELOPE upstream convention).
|
|
3
|
+
*
|
|
4
|
+
* The x402 v2 wire field `PaymentRequirements.maxAmountRequired` (this scheme's
|
|
5
|
+
* `amount`) travels in ATOMIC UNITS as an integer string, while the on-ledger
|
|
6
|
+
* Daml `Decimal` the Token Standard / TransferCommand uses is a fixed-scale
|
|
7
|
+
* decimal string. The conversion boundary is fixed across CIP-56 by the Daml
|
|
8
|
+
* `Decimal` scale: **1 CC = 10^10 atomic units** (10 decimal places).
|
|
9
|
+
*
|
|
10
|
+
* BACK-COMPAT (read this): the DEPLOYED v1 (external-party-amulet-rules, live on
|
|
11
|
+
* MainNet) and cip56 paths put a Daml **Decimal** string on the x402 wire today
|
|
12
|
+
* (e.g. `"0.0100000000"`), NOT atomic integer units — every deployed client
|
|
13
|
+
* signs the Decimal verbatim into the ledger and the facilitator compares it
|
|
14
|
+
* byte-for-byte. These helpers therefore exist so a deploy that OPTS IN to the
|
|
15
|
+
* atomic-on-wire convention can convert EXACTLY at the boundary; they do NOT
|
|
16
|
+
* change the default wire unit (see specs/scheme_exact_canton.upstream.md
|
|
17
|
+
* § Amount units). The maths is pure BigInt/string so the round-trip never
|
|
18
|
+
* drifts by 10^10 (the off-by-scale footgun) and never goes through float.
|
|
19
|
+
*
|
|
20
|
+
* Mirrors `packages/pay-proxy/src/spend-budget-store.ts`'s `toAtomic` (the same
|
|
21
|
+
* "compare as BigInt atomic, never Number()" guardrail) but adds the inverse and
|
|
22
|
+
* a strict integer-atomic parser, and lives in core so client + facilitator
|
|
23
|
+
* share one implementation.
|
|
24
|
+
*/
|
|
25
|
+
/** Daml Decimal scale for Canton Coin / Amulet — 10 fractional digits. */
|
|
26
|
+
export const CC_ATOMIC_SCALE = 10;
|
|
27
|
+
const SCALE_FACTOR = 10n ** BigInt(CC_ATOMIC_SCALE);
|
|
28
|
+
/**
|
|
29
|
+
* Convert a Daml Decimal CC string (e.g. `"0.1"`, `"1.0000000000"`) to integer
|
|
30
|
+
* atomic units as a decimal string (e.g. `"1000000000"`, `"10000000000"`).
|
|
31
|
+
*
|
|
32
|
+
* Fail-CLOSED: throws on a non-numeric value, a negative value, or a fractional
|
|
33
|
+
* part with MORE than `CC_ATOMIC_SCALE` significant digits (which would lose
|
|
34
|
+
* precision) — a malformed amount must never silently produce a wrong integer.
|
|
35
|
+
* Trailing zeros within the scale are fine (`"0.1"` and `"0.1000000000"` both
|
|
36
|
+
* → `"1000000000"`).
|
|
37
|
+
*/
|
|
38
|
+
export function decimalToAtomicCC(dec) {
|
|
39
|
+
if (typeof dec !== "string") {
|
|
40
|
+
throw new Error(`invalid CC decimal amount: ${JSON.stringify(dec)}`);
|
|
41
|
+
}
|
|
42
|
+
const m = /^(\d+)(?:\.(\d+))?$/.exec(dec.trim());
|
|
43
|
+
if (!m) {
|
|
44
|
+
throw new Error(`invalid CC decimal amount: ${JSON.stringify(dec)}`);
|
|
45
|
+
}
|
|
46
|
+
const whole = m[1] ?? "0";
|
|
47
|
+
const fracRaw = m[2] ?? "";
|
|
48
|
+
// Reject any significant digit past the scale (lossy). Trailing zeros that
|
|
49
|
+
// exceed the scale are NOT significant, so trim them before the length check.
|
|
50
|
+
const fracTrimmed = fracRaw.replace(/0+$/, "");
|
|
51
|
+
if (fracTrimmed.length > CC_ATOMIC_SCALE) {
|
|
52
|
+
throw new Error(`CC decimal amount ${JSON.stringify(dec)} has more than ${CC_ATOMIC_SCALE} ` +
|
|
53
|
+
`fractional digits — converting to atomic units would lose precision`);
|
|
54
|
+
}
|
|
55
|
+
const frac = fracRaw.slice(0, CC_ATOMIC_SCALE).padEnd(CC_ATOMIC_SCALE, "0");
|
|
56
|
+
const atomic = BigInt(whole) * SCALE_FACTOR + BigInt(frac || "0");
|
|
57
|
+
return atomic.toString();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Convert integer atomic units (decimal string, e.g. `"1"`, `"10000000000"`) to
|
|
61
|
+
* a fixed-scale Daml Decimal CC string with exactly `CC_ATOMIC_SCALE` fractional
|
|
62
|
+
* digits (e.g. `"0.0000000001"`, `"1.0000000000"`).
|
|
63
|
+
*
|
|
64
|
+
* Fail-CLOSED: throws on a non-integer / non-numeric / negative input. The
|
|
65
|
+
* output is the canonical fixed-scale form the Token Standard ledger uses, so
|
|
66
|
+
* `decimalToAtomicCC(atomicToDecimalCC(x)) === x` and
|
|
67
|
+
* `atomicToDecimalCC(decimalToAtomicCC(d))` is `d` normalized to 10 dp.
|
|
68
|
+
*/
|
|
69
|
+
export function atomicToDecimalCC(atomic) {
|
|
70
|
+
if (typeof atomic !== "string" || !/^\d+$/.test(atomic.trim())) {
|
|
71
|
+
throw new Error(`invalid CC atomic amount: ${JSON.stringify(atomic)}`);
|
|
72
|
+
}
|
|
73
|
+
const v = BigInt(atomic.trim());
|
|
74
|
+
const whole = v / SCALE_FACTOR;
|
|
75
|
+
const frac = v % SCALE_FACTOR;
|
|
76
|
+
const fracStr = frac.toString().padStart(CC_ATOMIC_SCALE, "0");
|
|
77
|
+
return `${whole.toString()}.${fracStr}`;
|
|
78
|
+
}
|
|
79
|
+
/* ------------------------------------------------------------------------- *
|
|
80
|
+
* Unit-by-scheme boundary (x402-ENVELOPE atomic-by-scheme, BACK-COMPAT).
|
|
81
|
+
*
|
|
82
|
+
* The amount unit on the x402 WIRE is keyed by the x402 scheme string on the
|
|
83
|
+
* SAME object the amount lives on (PaymentRequirements.scheme /
|
|
84
|
+
* PaymentPayload.scheme):
|
|
85
|
+
*
|
|
86
|
+
* - scheme "exact-canton" (LEGACY, the live emitted wire today) => the wire
|
|
87
|
+
* `amount` is a Daml **Decimal** string (e.g. "0.0100000000"), UNCHANGED
|
|
88
|
+
* from deployed behavior. Every deployed v1/cip56/allocation client signs
|
|
89
|
+
* this Decimal verbatim into the ledger and the facilitator compares it.
|
|
90
|
+
* - scheme "exact" (x402-ENVELOPE, post-deploy emit flip) => the wire
|
|
91
|
+
* `amount` is ATOMIC integer units (1 CC = 10^10), and the on-ledger Daml
|
|
92
|
+
* Decimal is derived EXACTLY via the BigInt converters above.
|
|
93
|
+
*
|
|
94
|
+
* The on-ledger Daml `transferLeg.amount` / `TransferCommand.amount` /
|
|
95
|
+
* `Holding.amount` is ALWAYS a fixed-scale Decimal. These two functions are the
|
|
96
|
+
* SINGLE place the scheme->unit decision is made, so every comparison site
|
|
97
|
+
* (facilitator verify arms, selectServerRequirements, client builder,
|
|
98
|
+
* verify-before-sign) converts identically — the off-by-10^10 firewall.
|
|
99
|
+
* ------------------------------------------------------------------------- */
|
|
100
|
+
/** True iff this x402 scheme string carries the amount in ATOMIC integer units
|
|
101
|
+
* on the wire ("exact" — the x402-ENVELOPE convention). The legacy
|
|
102
|
+
* "exact-canton" (and anything else) carries a Daml Decimal. Mirrors
|
|
103
|
+
* `schemeMatches`'s "exact" is canonical / "exact-canton" is legacy split. */
|
|
104
|
+
export function schemeIsAtomic(scheme) {
|
|
105
|
+
return scheme === "exact";
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Convert an x402 WIRE amount to the on-ledger Daml **Decimal** it denotes,
|
|
109
|
+
* keyed by the wire's scheme. Atomic scheme ("exact") => `atomicToDecimalCC`;
|
|
110
|
+
* legacy/Decimal scheme ("exact-canton", anything else) => passthrough (the wire
|
|
111
|
+
* value already IS the Decimal). Fail-CLOSED via the underlying converter (a
|
|
112
|
+
* non-integer atomic value, or a malformed Decimal at a later comparison,
|
|
113
|
+
* throws rather than silently mis-comparing). Use this at EVERY site that
|
|
114
|
+
* compares a wire amount against an on-ledger Decimal.
|
|
115
|
+
*/
|
|
116
|
+
export function wireAmountToLedgerDecimal(scheme, wireAmount) {
|
|
117
|
+
return schemeIsAtomic(scheme) ? atomicToDecimalCC(wireAmount) : wireAmount;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Inverse of `wireAmountToLedgerDecimal`: given an on-ledger Daml Decimal,
|
|
121
|
+
* produce the WIRE amount for the given scheme. Atomic scheme ("exact") =>
|
|
122
|
+
* `decimalToAtomicCC`; legacy/Decimal scheme => passthrough. Used by the client
|
|
123
|
+
* builder / signer seam when emitting under the atomic scheme.
|
|
124
|
+
*/
|
|
125
|
+
export function ledgerDecimalToWireAmount(scheme, decimal) {
|
|
126
|
+
return schemeIsAtomic(scheme) ? decimalToAtomicCC(decimal) : decimal;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Canonical VALUE-equality of two on-ledger Daml CC Decimal strings: compares
|
|
130
|
+
* by BigInt atomic units so "0.1" ≡ "0.1000000000" and a 10x/0.1x difference
|
|
131
|
+
* provably never folds. Fail-CLOSED: a malformed Decimal on either side throws
|
|
132
|
+
* (via `decimalToAtomicCC`), so a comparison can never silently pass on junk.
|
|
133
|
+
* This is the exactness guarantee the spec asks for at every amount-compare site
|
|
134
|
+
* (replaces a raw string `!==`).
|
|
135
|
+
*/
|
|
136
|
+
export function ledgerDecimalEquals(a, b) {
|
|
137
|
+
return decimalToAtomicCC(a) === decimalToAtomicCC(b);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Non-throwing, FAIL-CLOSED variant of `ledgerDecimalEquals` for the
|
|
141
|
+
* facilitator amount-validation arms: returns `true` only when both inputs are
|
|
142
|
+
* well-formed Daml CC Decimals of equal value; a malformed/junk value on either
|
|
143
|
+
* side returns `false` (→ amount_mismatch) instead of throwing. Use this at the
|
|
144
|
+
* pure validator sites (validateTransferCommand / validateAllocation /
|
|
145
|
+
* validateCip56*) that must map to a discriminated reject, never a 5xx.
|
|
146
|
+
*/
|
|
147
|
+
export function ledgerDecimalsMatch(a, b) {
|
|
148
|
+
try {
|
|
149
|
+
return ledgerDecimalEquals(a, b);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=amount.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.js","sourceRoot":"","sources":["../src/amount.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Lowercase-hex unsalted SHA-256 of the exact UTF-8 URL string. */
|
|
2
|
+
export declare function hashResourceUrl(url: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* True when an on-ledger `x402.resourceUrl` stamp binds to the expected URL —
|
|
5
|
+
* accepting BOTH the new hashed form `H(url)` and the legacy plaintext `url`
|
|
6
|
+
* (back-compat with deployed plaintext-stamping clients). Callers only invoke
|
|
7
|
+
* this when a stamp is PRESENT; an absent stamp is tolerated by the caller
|
|
8
|
+
* (the documented residual), exactly as before.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resourceUrlMatchesStamp(stamp: string, expectedUrl: string): boolean;
|
|
11
|
+
//# sourceMappingURL=resource-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-url.d.ts","sourceRoot":"","sources":["../src/resource-url.ts"],"names":[],"mappings":"AAsBA,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAEnF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource-URL on-ledger privacy stamp (x402-ENVELOPE upstream convention,
|
|
3
|
+
* PR #2634 review point 7).
|
|
4
|
+
*
|
|
5
|
+
* The client binds the payment to the gated resource by stamping the resource
|
|
6
|
+
* URL into the on-ledger Transfer/allocation `meta["x402.resourceUrl"]`, which
|
|
7
|
+
* the facilitator's /verify reproduces and compares (the binding guarantee that
|
|
8
|
+
* stops a transfer for resource A unlocking resource B). Committing the
|
|
9
|
+
* PLAINTEXT URL on-ledger is a privacy concern — it identifies which API the
|
|
10
|
+
* agent is paying for, visible to every stakeholder + in Scan. The convention
|
|
11
|
+
* is therefore to stamp an UNSALTED SHA-256 hash (lowercase hex of the digest of
|
|
12
|
+
* the exact UTF-8 URL string) instead of the plaintext, preserving the binding
|
|
13
|
+
* while hiding the URL.
|
|
14
|
+
*
|
|
15
|
+
* BACK-COMPAT: the facilitator MUST accept EITHER the stamped plaintext URL OR
|
|
16
|
+
* its hash (compare the on-ledger value against both `url` and
|
|
17
|
+
* `hashResourceUrl(url)`), so deployed clients that still stamp plaintext keep
|
|
18
|
+
* verifying while new clients stamp the hash. Absence stays tolerated (existing
|
|
19
|
+
* behavior). See `resourceUrlMatchesStamp`.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
/** Lowercase-hex unsalted SHA-256 of the exact UTF-8 URL string. */
|
|
23
|
+
export function hashResourceUrl(url) {
|
|
24
|
+
return createHash("sha256").update(url, "utf8").digest("hex");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* True when an on-ledger `x402.resourceUrl` stamp binds to the expected URL —
|
|
28
|
+
* accepting BOTH the new hashed form `H(url)` and the legacy plaintext `url`
|
|
29
|
+
* (back-compat with deployed plaintext-stamping clients). Callers only invoke
|
|
30
|
+
* this when a stamp is PRESENT; an absent stamp is tolerated by the caller
|
|
31
|
+
* (the documented residual), exactly as before.
|
|
32
|
+
*/
|
|
33
|
+
export function resourceUrlMatchesStamp(stamp, expectedUrl) {
|
|
34
|
+
return stamp === expectedUrl || stamp === hashResourceUrl(expectedUrl);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=resource-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource-url.js","sourceRoot":"","sources":["../src/resource-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,WAAmB;IACxE,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,eAAe,CAAC,WAAW,CAAC,CAAC;AACzE,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -8,23 +8,99 @@
|
|
|
8
8
|
*/
|
|
9
9
|
/** CAIP-2-style Canton network identifier. */
|
|
10
10
|
export type CantonNetwork = `canton:devnet` | `canton:mainnet` | `canton:${string}`;
|
|
11
|
+
/** x402 scheme discriminator. The x402-ENVELOPE upstream convention (PR #2634
|
|
12
|
+
* review) is that the scheme NAME is `"exact"` and Canton is a NETWORK of the
|
|
13
|
+
* exact scheme (CAIP-2 `canton:*`). The codebase historically used
|
|
14
|
+
* `"exact-canton"`; BOTH are accepted on input for back-compat (deployed
|
|
15
|
+
* facilitator + resource-server middleware string-check `"exact-canton"`).
|
|
16
|
+
* New emitters MAY use `"exact"` once the facilitator accept-both is deployed;
|
|
17
|
+
* see `schemeMatches` for the equivalence used in requirement matching. */
|
|
18
|
+
export type ExactScheme = "exact" | "exact-canton";
|
|
19
|
+
/** True when two scheme strings refer to the same exact scheme — `"exact"` and
|
|
20
|
+
* the legacy `"exact-canton"` are equivalent. Used so a new client emitting
|
|
21
|
+
* `"exact"` still matches a merchant `accepts[]` configured with the legacy
|
|
22
|
+
* `"exact-canton"` (and vice-versa). */
|
|
23
|
+
export declare function schemeMatches(a: string, b: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Extra-block reader helpers (x402-ENVELOPE accept-both). Prefer the new
|
|
26
|
+
* upstream key, fall back to the legacy one. Used everywhere the facilitator /
|
|
27
|
+
* client reads `extra.feePayer`/`facilitatorParty` or
|
|
28
|
+
* `extra.assetTransferMethod`/`transferMethod`, so a request carrying EITHER
|
|
29
|
+
* shape resolves identically.
|
|
30
|
+
*/
|
|
31
|
+
export declare function extraFeePayer(extra: {
|
|
32
|
+
feePayer?: string;
|
|
33
|
+
facilitatorParty?: string;
|
|
34
|
+
}): string | undefined;
|
|
35
|
+
export declare function extraMethod(extra: {
|
|
36
|
+
assetTransferMethod?: string;
|
|
37
|
+
transferMethod?: string;
|
|
38
|
+
}): string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* PaymentPayload payer reader (phdargen / upstream-maintainer naming, accept-both).
|
|
41
|
+
* The x402 payment payload's payer-party field is `payer` — aligning with the
|
|
42
|
+
* `payer` field on `SettleResponse` / `VerifyResponse`. The codebase historically
|
|
43
|
+
* used `payerParty`, and the DEPLOYED v1 MainNet facilitator still reads
|
|
44
|
+
* `payerParty` off the wire, so BOTH keys are accepted on input: prefer the new
|
|
45
|
+
* `payer`, fall back to the legacy `payerParty`. Mirrors `extraFeePayer`. New
|
|
46
|
+
* emitters set BOTH (see `client/src/scheme.ts`); every reader of the wire payer
|
|
47
|
+
* resolves via this helper so a payload carrying EITHER shape resolves identically.
|
|
48
|
+
*/
|
|
49
|
+
export declare function payloadPayer(p: {
|
|
50
|
+
payer?: string;
|
|
51
|
+
payerParty?: string;
|
|
52
|
+
}): string | undefined;
|
|
53
|
+
/** True when two `asset` symbols denote the same instrument. The x402-ENVELOPE
|
|
54
|
+
* convention is the symbol `"CC"`; `"canton-coin"` (legacy) is the same thing.
|
|
55
|
+
* The structured `"<admin>::Amulet"` form is also treated as Canton Coin. Any
|
|
56
|
+
* other value matches only by exact string equality (multi-asset tokens). */
|
|
57
|
+
export declare function assetMatches(a: string, b: string): boolean;
|
|
11
58
|
/** Which on-ledger primitive carries the actual CC movement.
|
|
12
59
|
* external-party-amulet-rules (v1): the facilitator submits
|
|
13
60
|
* TransferCommand_Send and pays the Global Synchronizer traffic fee.
|
|
14
61
|
* cip56-transfer-factory: the payer submits TransferFactory_Transfer and
|
|
15
|
-
* pays; the facilitator only verifies.
|
|
16
|
-
*
|
|
17
|
-
|
|
18
|
-
|
|
62
|
+
* pays; the facilitator only verifies.
|
|
63
|
+
* allocation-api (CIP-56 DVP, PRIMARY end-state): the payer LOCKS funds via
|
|
64
|
+
* AllocationFactory_Allocate (naming the facilitator as settlement.executor),
|
|
65
|
+
* then the facilitator submits Allocation_ExecuteTransfer and pays the GS
|
|
66
|
+
* traffic fee — facilitator-sponsored gas AND no nonce AND no per-merchant
|
|
67
|
+
* preapproval. A deploy advertises one via CANTON_X402_PRIMARY_METHOD; all
|
|
68
|
+
* are handled at verify/settle. The three branches are independent + additive
|
|
69
|
+
* (see specs/scheme_exact_canton.upstream.md § Method Coexistence). */
|
|
70
|
+
export type CantonTransferMethod = "external-party-amulet-rules" | "cip56-transfer-factory" | "allocation-api" | "allocation-direct";
|
|
71
|
+
/** Canton-specific `extra` block in 402 PaymentRequirements.
|
|
72
|
+
*
|
|
73
|
+
* x402-ENVELOPE upstream convention renames (PR #2634 review):
|
|
74
|
+
* - `facilitatorParty` → `feePayer` (the party that pays GS traffic)
|
|
75
|
+
* - `transferMethod` → `assetTransferMethod` (allocation value
|
|
76
|
+
* "allocation-api")
|
|
77
|
+
* - `synchronizerId` may be SOURCED FROM /supported (AmuletRules.domain_id)
|
|
78
|
+
* instead of stamped in `extra`.
|
|
79
|
+
*
|
|
80
|
+
* BACK-COMPAT: the OLD keys (`facilitatorParty`, `transferMethod`,
|
|
81
|
+
* `synchronizerId`) stay first-class so every deployed v1/cip56 reader compiles
|
|
82
|
+
* and runs unchanged; the NEW keys (`feePayer`, `assetTransferMethod`) are
|
|
83
|
+
* ADDED as optional siblings. New emitters set BOTH the canonical old field
|
|
84
|
+
* (kept for the deployed facilitator that only reads old) AND the new alias.
|
|
85
|
+
* Read via `extraFeePayer` / `extraMethod` (prefer new, fall back to old). The
|
|
86
|
+
* discriminator stays the `transferMethod` literal so the union narrows. */
|
|
19
87
|
export type CantonPaymentRequirementsExtra = {
|
|
20
88
|
transferMethod: "external-party-amulet-rules";
|
|
21
89
|
facilitatorParty: string;
|
|
90
|
+
/** x402-ENVELOPE name for the GS-traffic fee payer (alias of
|
|
91
|
+
* facilitatorParty). Optional so deployed 402s without it still type. */
|
|
92
|
+
feePayer?: string;
|
|
93
|
+
/** x402-ENVELOPE name for the method discriminator (alias of
|
|
94
|
+
* transferMethod). */
|
|
95
|
+
assetTransferMethod?: "external-party-amulet-rules";
|
|
22
96
|
synchronizerId: string;
|
|
23
97
|
merchantContractCid?: string;
|
|
24
98
|
memo?: string;
|
|
25
99
|
} | {
|
|
26
100
|
transferMethod: "cip56-transfer-factory";
|
|
27
101
|
facilitatorParty: string;
|
|
102
|
+
feePayer?: string;
|
|
103
|
+
assetTransferMethod?: "cip56-transfer-factory";
|
|
28
104
|
synchronizerId: string;
|
|
29
105
|
transferFactoryCid: string;
|
|
30
106
|
/** Hash-prefixed templateId of the factory contract, same form
|
|
@@ -36,12 +112,100 @@ export type CantonPaymentRequirementsExtra = {
|
|
|
36
112
|
id: string;
|
|
37
113
|
};
|
|
38
114
|
memo?: string;
|
|
115
|
+
} | {
|
|
116
|
+
/** CIP-56 DVP Allocation method (PRIMARY end-state). The payer LOCKS
|
|
117
|
+
* funds via AllocationFactory_Allocate naming the facilitator as
|
|
118
|
+
* settlement.executor; the facilitator then submits
|
|
119
|
+
* Allocation_ExecuteTransfer (sponsored gas). See
|
|
120
|
+
* specs/scheme_exact_canton.upstream.md. */
|
|
121
|
+
transferMethod: "allocation-api";
|
|
122
|
+
/** The facilitator's own party id. Also bound as settlement.executor. */
|
|
123
|
+
facilitatorParty: string;
|
|
124
|
+
/** x402-ENVELOPE fee payer (alias of facilitatorParty). */
|
|
125
|
+
feePayer?: string;
|
|
126
|
+
/** x402-ENVELOPE method discriminator name (= "allocation-api"). */
|
|
127
|
+
assetTransferMethod?: "allocation-api";
|
|
128
|
+
synchronizerId: string;
|
|
129
|
+
/** The Allocation factory cid the client exercises
|
|
130
|
+
* AllocationFactory_Allocate against (resolved from the registry's
|
|
131
|
+
* POST /registry/allocation-instruction/v1/allocation-factory). */
|
|
132
|
+
allocationFactoryCid: string;
|
|
133
|
+
/** Hash-prefixed templateId of the AllocationFactory (informational;
|
|
134
|
+
* the resolved factoryId comes back in the registry response). */
|
|
135
|
+
allocationFactoryTemplateId: string;
|
|
136
|
+
instrumentId: {
|
|
137
|
+
admin: string;
|
|
138
|
+
id: string;
|
|
139
|
+
};
|
|
140
|
+
/** The party that will submit Allocation_ExecuteTransfer. For the
|
|
141
|
+
* sponsored flow this EQUALS facilitatorParty and is bound during
|
|
142
|
+
* verification (settlement.executor == facilitatorParty). */
|
|
143
|
+
executor: string;
|
|
144
|
+
/** Relative deadline (seconds from now) the client uses to compute
|
|
145
|
+
* the SettlementInfo.allocateBefore absolute time. */
|
|
146
|
+
allocateBeforeSeconds: number;
|
|
147
|
+
/** Relative deadline (seconds from now) for SettlementInfo.settleBefore.
|
|
148
|
+
* MUST be strictly after allocateBefore. */
|
|
149
|
+
settleBeforeSeconds: number;
|
|
150
|
+
memo?: string;
|
|
151
|
+
} | {
|
|
152
|
+
/** CIP-56 DVP "2-tx DIRECT" Allocation method (Design B). The payer LOCKS
|
|
153
|
+
* funds via AllocationFactory_Allocate naming the MERCHANT (payTo) as
|
|
154
|
+
* transferLeg.receiver AND the facilitator as settlement.executor; the
|
|
155
|
+
* facilitator then settles in ONE tx via DirectSettlementConsent_Execute,
|
|
156
|
+
* which runs Allocation_ExecuteTransfer paying the merchant DIRECTLY. The
|
|
157
|
+
* `extra` shape is identical to "allocation-api" (same factory + executor +
|
|
158
|
+
* deadlines + instrument); only the discriminator differs, so the client
|
|
159
|
+
* builds the SAME AllocationFactory_Allocate exercise (receiver=merchant)
|
|
160
|
+
* and the facilitator routes to the direct settle path. See daml/x402-direct.
|
|
161
|
+
*/
|
|
162
|
+
transferMethod: "allocation-direct";
|
|
163
|
+
/** The facilitator's own party. Also bound as settlement.executor. */
|
|
164
|
+
facilitatorParty: string;
|
|
165
|
+
/** x402-ENVELOPE fee payer (alias of facilitatorParty). */
|
|
166
|
+
feePayer?: string;
|
|
167
|
+
/** x402-ENVELOPE method discriminator name (= "allocation-direct"). */
|
|
168
|
+
assetTransferMethod?: "allocation-direct";
|
|
169
|
+
synchronizerId: string;
|
|
170
|
+
/** The Allocation factory cid the client exercises
|
|
171
|
+
* AllocationFactory_Allocate against (resolved from the registry's
|
|
172
|
+
* POST /registry/allocation-instruction/v1/allocation-factory). */
|
|
173
|
+
allocationFactoryCid: string;
|
|
174
|
+
/** Hash-prefixed templateId of the AllocationFactory (informational;
|
|
175
|
+
* the resolved factoryId comes back in the registry response). */
|
|
176
|
+
allocationFactoryTemplateId: string;
|
|
177
|
+
instrumentId: {
|
|
178
|
+
admin: string;
|
|
179
|
+
id: string;
|
|
180
|
+
};
|
|
181
|
+
/** The party that will submit DirectSettlementConsent_Execute. For the
|
|
182
|
+
* sponsored flow this EQUALS facilitatorParty and is bound during
|
|
183
|
+
* verification (settlement.executor == facilitatorParty). */
|
|
184
|
+
executor: string;
|
|
185
|
+
/** Relative deadline (seconds from now) the client uses to compute
|
|
186
|
+
* the SettlementInfo.allocateBefore absolute time. */
|
|
187
|
+
allocateBeforeSeconds: number;
|
|
188
|
+
/** Relative deadline (seconds from now) for SettlementInfo.settleBefore.
|
|
189
|
+
* MUST be strictly after allocateBefore. */
|
|
190
|
+
settleBeforeSeconds: number;
|
|
191
|
+
memo?: string;
|
|
39
192
|
};
|
|
40
|
-
/** Canton-specific `payload` block in PaymentPayload.
|
|
193
|
+
/** Canton-specific `payload` block in PaymentPayload.
|
|
194
|
+
*
|
|
195
|
+
* PAYER FIELD (phdargen / upstream-maintainer naming, accept-both): the
|
|
196
|
+
* payer-party field is `payer` — aligning with `SettleResponse`/`VerifyResponse`
|
|
197
|
+
* `payer`. The legacy `payerParty` stays accepted on input because the DEPLOYED
|
|
198
|
+
* v1 MainNet facilitator reads `payerParty` off the wire. BOTH are optional here;
|
|
199
|
+
* a well-formed payload carries AT LEAST ONE (validate-body.ts enforces it), and
|
|
200
|
+
* new emitters set BOTH (`payer` primary + `payerParty` legacy). Read via
|
|
201
|
+
* `payloadPayer` (prefer `payer`, fall back to `payerParty`). */
|
|
41
202
|
export type CantonPaymentPayload = {
|
|
42
203
|
transferMethod: "external-party-amulet-rules";
|
|
43
204
|
transferCommandCid: string;
|
|
44
|
-
|
|
205
|
+
/** Payer party id (phdargen naming, primary). */
|
|
206
|
+
payer?: string;
|
|
207
|
+
/** Legacy payer party id (deployed-facilitator compat). Read via payloadPayer. */
|
|
208
|
+
payerParty?: string;
|
|
45
209
|
nonce: number;
|
|
46
210
|
signatureProof?: string;
|
|
47
211
|
/** updateId of the TransferCommand_Create transaction (relay path only).
|
|
@@ -50,7 +214,10 @@ export type CantonPaymentPayload = {
|
|
|
50
214
|
createUpdateId?: string;
|
|
51
215
|
} | {
|
|
52
216
|
transferMethod: "cip56-transfer-factory";
|
|
53
|
-
|
|
217
|
+
/** Payer party id (phdargen naming, primary). */
|
|
218
|
+
payer?: string;
|
|
219
|
+
/** Legacy payer party id (deployed-facilitator compat). Read via payloadPayer. */
|
|
220
|
+
payerParty?: string;
|
|
54
221
|
/** Canton ledger updateId of the TransferFactory_Transfer exercise.
|
|
55
222
|
* Required when the registry returned
|
|
56
223
|
* TransferInstructionResult_Completed (settlement already on-ledger;
|
|
@@ -60,6 +227,62 @@ export type CantonPaymentPayload = {
|
|
|
60
227
|
* TransferInstructionResult_Pending; /settle waits for it. */
|
|
61
228
|
transferInstructionCid?: string;
|
|
62
229
|
signatureProof?: string;
|
|
230
|
+
} | {
|
|
231
|
+
transferMethod: "allocation-api";
|
|
232
|
+
/** Payer party id (phdargen naming, primary). */
|
|
233
|
+
payer?: string;
|
|
234
|
+
/** Legacy payer party id (deployed-facilitator compat). Read via payloadPayer. */
|
|
235
|
+
payerParty?: string;
|
|
236
|
+
/** Contract id of the live Allocation (the _Completed case — Amulet
|
|
237
|
+
* creates it synchronously). This is the proof the facilitator verifies
|
|
238
|
+
* (participant ACS read) and then executes. */
|
|
239
|
+
allocationCid?: string;
|
|
240
|
+
/** Contract id of a still-pending AllocationInstruction (the _Pending
|
|
241
|
+
* case). Track A is sync-only: the facilitator rejects a pending-only
|
|
242
|
+
* payload with invalid_exact_canton_allocation_pending. */
|
|
243
|
+
allocationInstructionCid?: string;
|
|
244
|
+
/** Design A — the X402Escrow cid the facilitator exercises
|
|
245
|
+
* X402Escrow_Settle on; present for the escrow flow. When set, the
|
|
246
|
+
* standard /settle settles via the EscrowService's ATOMIC settleAtomic
|
|
247
|
+
* (v0.3.0: ONE X402Escrow_Settle tx that runs Allocation_ExecuteTransfer
|
|
248
|
+
* AND forwards facilitator->merchant in the same transaction) instead of
|
|
249
|
+
* the direct Allocation_ExecuteTransfer (which fails the 3-party auth).
|
|
250
|
+
* Absent → the plain sponsored allocation execute path. */
|
|
251
|
+
escrowCid?: string;
|
|
252
|
+
/** Optional Ed25519 proof over the prepared-tx hash of the
|
|
253
|
+
* AllocationFactory_Allocate exercise. */
|
|
254
|
+
signatureProof?: string;
|
|
255
|
+
} | {
|
|
256
|
+
/** CIP-56 DVP "2-tx DIRECT" (Design B). The payer LOCKED funds via
|
|
257
|
+
* AllocationFactory_Allocate with transferLeg.receiver = the MERCHANT
|
|
258
|
+
* (payTo) and settlement.executor = the facilitator; the facilitator then
|
|
259
|
+
* settles in ONE tx via DirectSettlementConsent_Execute (no escrow, no
|
|
260
|
+
* forward, no facilitator custody). See daml/x402-direct. */
|
|
261
|
+
transferMethod: "allocation-direct";
|
|
262
|
+
/** Payer party id (phdargen naming, primary). */
|
|
263
|
+
payer?: string;
|
|
264
|
+
/** Legacy payer party id (deployed-facilitator compat). Read via payloadPayer. */
|
|
265
|
+
payerParty?: string;
|
|
266
|
+
/** Contract id of the live Allocation (the _Completed case — Amulet creates
|
|
267
|
+
* it synchronously). receiver == merchant. This is the proof the facilitator
|
|
268
|
+
* verifies (participant ACS read) and then executes via the consent. */
|
|
269
|
+
allocationCid?: string;
|
|
270
|
+
/** Contract id of a still-pending AllocationInstruction (the _Pending case).
|
|
271
|
+
* Track A is sync-only: the facilitator rejects a pending-only payload with
|
|
272
|
+
* invalid_exact_canton_allocation_pending. */
|
|
273
|
+
allocationInstructionCid?: string;
|
|
274
|
+
/** Optional: the standing DirectSettlementConsent {sender, merchant} cid the
|
|
275
|
+
* facilitator exercises DirectSettlementConsent_Execute on. When ABSENT the
|
|
276
|
+
* facilitator RESOLVES it from its own ACS (the {sender, merchant} pair) and,
|
|
277
|
+
* if none exists, MINTS it from the standing SenderConsent + MerchantConsent
|
|
278
|
+
* (facilitator-alone, once-total onboarding). Supplying it is an O(1)
|
|
279
|
+
* fast-path; the cid moves no funds on its own — the consent's on-ledger
|
|
280
|
+
* choice pins receiver==merchant / sender==consent.sender / executor==
|
|
281
|
+
* facilitator and re-validates every term. */
|
|
282
|
+
directConsentCid?: string;
|
|
283
|
+
/** Optional Ed25519 proof over the prepared-tx hash of the
|
|
284
|
+
* AllocationFactory_Allocate exercise. */
|
|
285
|
+
signatureProof?: string;
|
|
63
286
|
};
|
|
64
287
|
/** Resource being paid for. Echoed from the server's 402 PAYMENT-REQUIRED
|
|
65
288
|
* header into every PaymentPayload per x402 v2. */
|
|
@@ -73,7 +296,7 @@ export type FacilitatorRequest = {
|
|
|
73
296
|
x402Version: 2;
|
|
74
297
|
paymentPayload: {
|
|
75
298
|
x402Version: 2;
|
|
76
|
-
scheme:
|
|
299
|
+
scheme: ExactScheme;
|
|
77
300
|
network: CantonNetwork;
|
|
78
301
|
resource: X402ResourceInfo;
|
|
79
302
|
accepted: PaymentRequirements;
|
|
@@ -108,10 +331,14 @@ export type SettleResponse = {
|
|
|
108
331
|
export type SupportedResponse = {
|
|
109
332
|
kinds: Array<{
|
|
110
333
|
x402Version: 1 | 2;
|
|
111
|
-
scheme:
|
|
334
|
+
scheme: ExactScheme;
|
|
112
335
|
network: CantonNetwork;
|
|
113
336
|
extra?: {
|
|
114
337
|
transferMethods: CantonTransferMethod[];
|
|
338
|
+
/** x402-ENVELOPE: the Global Synchronizer id (AmuletRules.domain_id) the
|
|
339
|
+
* facilitator settles on. Advertised here so a 402 `extra` MAY omit
|
|
340
|
+
* `synchronizerId` and the client sources it from /supported. */
|
|
341
|
+
synchronizerId?: string;
|
|
115
342
|
};
|
|
116
343
|
}>;
|
|
117
344
|
extensions: string[];
|
|
@@ -119,7 +346,7 @@ export type SupportedResponse = {
|
|
|
119
346
|
};
|
|
120
347
|
/** PaymentRequirements entry as advertised in an `accepts[]` array. */
|
|
121
348
|
export type PaymentRequirements = {
|
|
122
|
-
scheme:
|
|
349
|
+
scheme: ExactScheme;
|
|
123
350
|
network: CantonNetwork;
|
|
124
351
|
amount: string;
|
|
125
352
|
asset: string;
|
|
@@ -128,7 +355,7 @@ export type PaymentRequirements = {
|
|
|
128
355
|
extra: CantonPaymentRequirementsExtra;
|
|
129
356
|
};
|
|
130
357
|
/** Canton-specific x402 error codes. Prefix `invalid_exact_canton_*`. */
|
|
131
|
-
export type CantonErrorCode = "invalid_exact_canton_transfer_command_not_found" | "invalid_exact_canton_amount_mismatch" | "invalid_exact_canton_asset_mismatch" | "invalid_exact_canton_expired" | "invalid_exact_canton_nonce_reuse" | "invalid_exact_canton_payer_mismatch" | "invalid_exact_canton_merchant_mismatch" | "invalid_exact_canton_signature" | "
|
|
358
|
+
export type CantonErrorCode = "invalid_exact_canton_transfer_command_not_found" | "invalid_exact_canton_amount_mismatch" | "invalid_exact_canton_asset_mismatch" | "invalid_exact_canton_expired" | "invalid_exact_canton_nonce_reuse" | "invalid_exact_canton_payer_mismatch" | "invalid_exact_canton_merchant_mismatch" | "invalid_exact_canton_signature" | "invalid_exact_canton_merchant_not_registered" | "invalid_exact_canton_counter_not_ready" | "invalid_exact_canton_transfer_instruction_not_found" | "invalid_exact_canton_transfer_completed_not_visible" | "invalid_exact_canton_transfer_instruction_pending" | "invalid_exact_canton_instrument_id_mismatch" | "invalid_exact_canton_transfer_factory_not_found" | "invalid_exact_canton_missing_proof" | "invalid_exact_canton_holding_locked" | "invalid_exact_canton_payment_already_settled" | "invalid_exact_canton_executor_mismatch" | "invalid_exact_canton_allocation_pending" | "invalid_exact_canton_allocation_not_found" | "invalid_exact_canton_allocation_factory_not_found" | "invalid_exact_canton_execute_failed" | "invalid_exact_canton_insufficient_balance" | "invalid_exact_canton_self_payment" | "invalid_exact_canton_direct_disabled" | "unexpected_canton_ledger_error";
|
|
132
359
|
/**
|
|
133
360
|
* Pick the server's OWN PaymentRequirements entry that a client claims to
|
|
134
361
|
* be paying against — defeating client tampering of price / recipient.
|
|
@@ -149,9 +376,13 @@ export type CantonErrorCode = "invalid_exact_canton_transfer_command_not_found"
|
|
|
149
376
|
* the facilitator with the client's numbers.
|
|
150
377
|
*
|
|
151
378
|
* Matching is on the money-critical fields only: scheme, network, amount,
|
|
152
|
-
* asset, payTo, and extra.{
|
|
153
|
-
*
|
|
154
|
-
*
|
|
379
|
+
* asset, payTo, and extra.{method, feePayer, synchronizerId, instrumentId}.
|
|
380
|
+
* `maxTimeoutSeconds` / `memo` / discovery cids are not part of the price
|
|
381
|
+
* contract. x402-ENVELOPE accept-both (PR #2634): scheme `"exact"` ≡
|
|
382
|
+
* `"exact-canton"`, asset `"CC"` ≡ `"canton-coin"` ≡ `"<admin>::Amulet"`, the
|
|
383
|
+
* method/feePayer fields are read via the new (assetTransferMethod/feePayer) or
|
|
384
|
+
* legacy (transferMethod/facilitatorParty) key, and synchronizerId is only
|
|
385
|
+
* enforced when BOTH sides carry it (it may be sourced from /supported).
|
|
155
386
|
*/
|
|
156
387
|
export declare function selectServerRequirements(accepts: PaymentRequirements[], clientAccepted: unknown): PaymentRequirements | null;
|
|
157
388
|
/**
|
|
@@ -160,8 +391,9 @@ export declare function selectServerRequirements(accepts: PaymentRequirements[],
|
|
|
160
391
|
* configures an `asset` of the structured `<admin>::<id>` form that disagrees
|
|
161
392
|
* with `extra.instrumentId`, the mismatch is silent and the instrumentId wins.
|
|
162
393
|
* Catch it at middleware setup instead. `asset` may also be a symbolic value
|
|
163
|
-
* such as "canton-coin" (see PaymentRequirements.asset) — only the `::`
|
|
164
|
-
* cross-checked. Throws on a mismatch; no-op when consistent or not
|
|
394
|
+
* such as "CC" / "canton-coin" (see PaymentRequirements.asset) — only the `::`
|
|
395
|
+
* form is cross-checked. Throws on a mismatch; no-op when consistent or not
|
|
396
|
+
* applicable.
|
|
165
397
|
*/
|
|
166
398
|
export declare function assertAssetInstrumentConsistency(req: PaymentRequirements): void;
|
|
167
399
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -6,6 +6,54 @@
|
|
|
6
6
|
* `extra` (server side) and `payload` (client side); the envelope is
|
|
7
7
|
* standard x402 v2.
|
|
8
8
|
*/
|
|
9
|
+
import { ledgerDecimalEquals, wireAmountToLedgerDecimal } from "./amount.js";
|
|
10
|
+
/** True when two scheme strings refer to the same exact scheme — `"exact"` and
|
|
11
|
+
* the legacy `"exact-canton"` are equivalent. Used so a new client emitting
|
|
12
|
+
* `"exact"` still matches a merchant `accepts[]` configured with the legacy
|
|
13
|
+
* `"exact-canton"` (and vice-versa). */
|
|
14
|
+
export function schemeMatches(a, b) {
|
|
15
|
+
if (a === b)
|
|
16
|
+
return true;
|
|
17
|
+
const norm = (s) => (s === "exact-canton" ? "exact" : s);
|
|
18
|
+
return norm(a) === norm(b);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extra-block reader helpers (x402-ENVELOPE accept-both). Prefer the new
|
|
22
|
+
* upstream key, fall back to the legacy one. Used everywhere the facilitator /
|
|
23
|
+
* client reads `extra.feePayer`/`facilitatorParty` or
|
|
24
|
+
* `extra.assetTransferMethod`/`transferMethod`, so a request carrying EITHER
|
|
25
|
+
* shape resolves identically.
|
|
26
|
+
*/
|
|
27
|
+
export function extraFeePayer(extra) {
|
|
28
|
+
return extra.feePayer ?? extra.facilitatorParty;
|
|
29
|
+
}
|
|
30
|
+
export function extraMethod(extra) {
|
|
31
|
+
return extra.assetTransferMethod ?? extra.transferMethod;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* PaymentPayload payer reader (phdargen / upstream-maintainer naming, accept-both).
|
|
35
|
+
* The x402 payment payload's payer-party field is `payer` — aligning with the
|
|
36
|
+
* `payer` field on `SettleResponse` / `VerifyResponse`. The codebase historically
|
|
37
|
+
* used `payerParty`, and the DEPLOYED v1 MainNet facilitator still reads
|
|
38
|
+
* `payerParty` off the wire, so BOTH keys are accepted on input: prefer the new
|
|
39
|
+
* `payer`, fall back to the legacy `payerParty`. Mirrors `extraFeePayer`. New
|
|
40
|
+
* emitters set BOTH (see `client/src/scheme.ts`); every reader of the wire payer
|
|
41
|
+
* resolves via this helper so a payload carrying EITHER shape resolves identically.
|
|
42
|
+
*/
|
|
43
|
+
export function payloadPayer(p) {
|
|
44
|
+
return p.payer ?? p.payerParty;
|
|
45
|
+
}
|
|
46
|
+
/** True when two `asset` symbols denote the same instrument. The x402-ENVELOPE
|
|
47
|
+
* convention is the symbol `"CC"`; `"canton-coin"` (legacy) is the same thing.
|
|
48
|
+
* The structured `"<admin>::Amulet"` form is also treated as Canton Coin. Any
|
|
49
|
+
* other value matches only by exact string equality (multi-asset tokens). */
|
|
50
|
+
export function assetMatches(a, b) {
|
|
51
|
+
if (a === b)
|
|
52
|
+
return true;
|
|
53
|
+
const CC = new Set(["CC", "canton-coin"]);
|
|
54
|
+
const isCC = (s) => CC.has(s) || /::Amulet$/.test(s);
|
|
55
|
+
return isCC(a) && isCC(b);
|
|
56
|
+
}
|
|
9
57
|
/**
|
|
10
58
|
* Pick the server's OWN PaymentRequirements entry that a client claims to
|
|
11
59
|
* be paying against — defeating client tampering of price / recipient.
|
|
@@ -26,33 +74,84 @@
|
|
|
26
74
|
* the facilitator with the client's numbers.
|
|
27
75
|
*
|
|
28
76
|
* Matching is on the money-critical fields only: scheme, network, amount,
|
|
29
|
-
* asset, payTo, and extra.{
|
|
30
|
-
*
|
|
31
|
-
*
|
|
77
|
+
* asset, payTo, and extra.{method, feePayer, synchronizerId, instrumentId}.
|
|
78
|
+
* `maxTimeoutSeconds` / `memo` / discovery cids are not part of the price
|
|
79
|
+
* contract. x402-ENVELOPE accept-both (PR #2634): scheme `"exact"` ≡
|
|
80
|
+
* `"exact-canton"`, asset `"CC"` ≡ `"canton-coin"` ≡ `"<admin>::Amulet"`, the
|
|
81
|
+
* method/feePayer fields are read via the new (assetTransferMethod/feePayer) or
|
|
82
|
+
* legacy (transferMethod/facilitatorParty) key, and synchronizerId is only
|
|
83
|
+
* enforced when BOTH sides carry it (it may be sourced from /supported).
|
|
32
84
|
*/
|
|
33
85
|
export function selectServerRequirements(accepts, clientAccepted) {
|
|
34
86
|
if (typeof clientAccepted !== "object" || clientAccepted === null) {
|
|
35
87
|
return null;
|
|
36
88
|
}
|
|
37
89
|
const c = clientAccepted;
|
|
90
|
+
// x402-ENVELOPE accept-both: read the extra via the alias helpers so a client
|
|
91
|
+
// sending EITHER the new (feePayer/assetTransferMethod) or old
|
|
92
|
+
// (facilitatorParty/transferMethod) shape matches a server `accepts[]`
|
|
93
|
+
// configured in EITHER shape.
|
|
38
94
|
const cExtra = (c.extra ?? {});
|
|
95
|
+
const cMethod = extraMethod(cExtra);
|
|
96
|
+
const cFeePayer = extraFeePayer(cExtra);
|
|
39
97
|
for (const r of accepts) {
|
|
40
98
|
const rExtra = r.extra;
|
|
41
|
-
|
|
99
|
+
// scheme: "exact" ≡ "exact-canton" (a new client vs an old merchant config).
|
|
100
|
+
if (typeof r.scheme !== "string" ||
|
|
101
|
+
typeof c.scheme !== "string" ||
|
|
102
|
+
!schemeMatches(r.scheme, c.scheme))
|
|
42
103
|
continue;
|
|
43
104
|
if (r.network !== c.network)
|
|
44
105
|
continue;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
106
|
+
// amount: unit-by-scheme + canonical-decimal compare. The server-configured
|
|
107
|
+
// wire amount `r.amount` and the client-claimed wire amount `c.amount` may
|
|
108
|
+
// legitimately be in DIFFERENT units when scheme `"exact"` (atomic) and the
|
|
109
|
+
// legacy `"exact-canton"` (Decimal) are accept-both equivalent: a raw
|
|
110
|
+
// `r.amount !== c.amount` would then wrongly reject an honest cross-scheme
|
|
111
|
+
// match, OR (worse) let a crafted atomic value string-coincide with a
|
|
112
|
+
// Decimal. Normalize BOTH sides to the on-ledger Daml Decimal keyed by their
|
|
113
|
+
// OWN scheme, then compare by BigInt atomic units (folds "0.1" ≡
|
|
114
|
+
// "0.1000000000", and a 10x/0.1x amount provably cannot match). Fail-closed:
|
|
115
|
+
// a malformed amount throws in the converter rather than passing. This is the
|
|
116
|
+
// matching authority and MUST agree byte-exactly with the facilitator's
|
|
117
|
+
// wireAmountToLedgerDecimal comparisons.
|
|
118
|
+
{
|
|
119
|
+
let rDec;
|
|
120
|
+
let cDec;
|
|
121
|
+
try {
|
|
122
|
+
rDec = wireAmountToLedgerDecimal(r.scheme, r.amount);
|
|
123
|
+
cDec = wireAmountToLedgerDecimal(c.scheme, c.amount);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
continue; // malformed amount on either side → no match (fail-closed).
|
|
127
|
+
}
|
|
128
|
+
let amountEq;
|
|
129
|
+
try {
|
|
130
|
+
amountEq = ledgerDecimalEquals(rDec, cDec);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!amountEq)
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// asset: "CC" ≡ "canton-coin" ≡ "<admin>::Amulet" (symbolic equivalence).
|
|
139
|
+
if (typeof r.asset !== "string" ||
|
|
140
|
+
typeof c.asset !== "string" ||
|
|
141
|
+
!assetMatches(r.asset, c.asset))
|
|
48
142
|
continue;
|
|
49
143
|
if (r.payTo !== c.payTo)
|
|
50
144
|
continue;
|
|
51
|
-
if (rExtra
|
|
145
|
+
if (extraMethod(rExtra) !== cMethod)
|
|
52
146
|
continue;
|
|
53
|
-
if (rExtra
|
|
147
|
+
if (extraFeePayer(rExtra) !== cFeePayer)
|
|
54
148
|
continue;
|
|
55
|
-
|
|
149
|
+
// synchronizerId: x402-ENVELOPE allows omitting it from `extra` (sourced from
|
|
150
|
+
// /supported). Mirror the instrumentId pattern — only enforce equality when
|
|
151
|
+
// BOTH sides carry it; if either omits it, do not reject on this field.
|
|
152
|
+
if (rExtra.synchronizerId !== undefined &&
|
|
153
|
+
cExtra.synchronizerId !== undefined &&
|
|
154
|
+
rExtra.synchronizerId !== cExtra.synchronizerId)
|
|
56
155
|
continue;
|
|
57
156
|
// instrumentId (CIP-56): if either side carries it, both must match.
|
|
58
157
|
const rInst = rExtra.instrumentId;
|
|
@@ -73,8 +172,9 @@ export function selectServerRequirements(accepts, clientAccepted) {
|
|
|
73
172
|
* configures an `asset` of the structured `<admin>::<id>` form that disagrees
|
|
74
173
|
* with `extra.instrumentId`, the mismatch is silent and the instrumentId wins.
|
|
75
174
|
* Catch it at middleware setup instead. `asset` may also be a symbolic value
|
|
76
|
-
* such as "canton-coin" (see PaymentRequirements.asset) — only the `::`
|
|
77
|
-
* cross-checked. Throws on a mismatch; no-op when consistent or not
|
|
175
|
+
* such as "CC" / "canton-coin" (see PaymentRequirements.asset) — only the `::`
|
|
176
|
+
* form is cross-checked. Throws on a mismatch; no-op when consistent or not
|
|
177
|
+
* applicable.
|
|
78
178
|
*/
|
|
79
179
|
export function assertAssetInstrumentConsistency(req) {
|
|
80
180
|
const inst = req.extra.instrumentId;
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAiB7E;;;yCAGyC;AACzC,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,CAAS;IAChD,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;AAC7B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAG7B;IACC,OAAO,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,gBAAgB,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAG3B;IACC,OAAO,KAAK,CAAC,mBAAmB,IAAI,KAAK,CAAC,cAAc,CAAC;AAC3D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,CAG5B;IACC,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,UAAU,CAAC;AACjC,CAAC;AAED;;;8EAG8E;AAC9E,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,CAAS;IAC/C,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,CAAC,CAAS,EAAW,EAAE,CAClC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;AAC5B,CAAC;AA4aD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAA8B,EAC9B,cAAuB;IAEvB,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,cAA8C,CAAC;IACzD,8EAA8E;IAC9E,+DAA+D;IAC/D,uEAAuE;IACvE,8BAA8B;IAC9B,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAO3B,CAAC;IACH,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,CAAC,CAAC,KAOhB,CAAC;QACF,6EAA6E;QAC7E,IACE,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;YAC5B,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;YAC5B,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;YAElC,SAAS;QACX,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;YAAE,SAAS;QACtC,4EAA4E;QAC5E,2EAA2E;QAC3E,4EAA4E;QAC5E,sEAAsE;QACtE,2EAA2E;QAC3E,sEAAsE;QACtE,6EAA6E;QAC7E,iEAAiE;QACjE,6EAA6E;QAC7E,8EAA8E;QAC9E,wEAAwE;QACxE,yCAAyC;QACzC,CAAC;YACC,IAAI,IAAY,CAAC;YACjB,IAAI,IAAY,CAAC;YACjB,IAAI,CAAC;gBACH,IAAI,GAAG,yBAAyB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;gBACrD,IAAI,GAAG,yBAAyB,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,MAAgB,CAAC,CAAC;YACjE,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,4DAA4D;YACxE,CAAC;YACD,IAAI,QAAiB,CAAC;YACtB,IAAI,CAAC;gBACH,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,CAAC,QAAQ;gBAAE,SAAS;QAC1B,CAAC;QACD,0EAA0E;QAC1E,IACE,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC3B,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC3B,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC;YAE/B,SAAS;QACX,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;YAAE,SAAS;QAClC,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,OAAO;YAAE,SAAS;QAC9C,IAAI,aAAa,CAAC,MAAM,CAAC,KAAK,SAAS;YAAE,SAAS;QAClD,8EAA8E;QAC9E,4EAA4E;QAC5E,wEAAwE;QACxE,IACE,MAAM,CAAC,cAAc,KAAK,SAAS;YACnC,MAAM,CAAC,cAAc,KAAK,SAAS;YACnC,MAAM,CAAC,cAAc,KAAK,MAAM,CAAC,cAAc;YAE/C,SAAS;QACX,qEAAqE;QACrE,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC;QAClC,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;YACnB,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK;gBAAE,SAAS;YAC/B,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE;gBAAE,SAAS;QACrE,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gCAAgC,CAC9C,GAAwB;IAExB,MAAM,IAAI,GACR,GAAG,CAAC,KACL,CAAC,YAAY,CAAC;IACf,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,sCAAsC;IAC7E,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;IAC7C,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,+BAA+B,GAAG,CAAC,KAAK,mBAAmB;YACzD,wBAAwB,QAAQ,sCAAsC;YACtE,qEAAqE;YACrE,0DAA0D,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC"}
|