jalin-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +81 -0
- package/dist/anonymity.d.ts +99 -0
- package/dist/anonymity.d.ts.map +1 -0
- package/dist/anonymity.js +166 -0
- package/dist/anonymity.js.map +1 -0
- package/dist/crowd.d.ts +53 -0
- package/dist/crowd.d.ts.map +1 -0
- package/dist/crowd.js +50 -0
- package/dist/crowd.js.map +1 -0
- package/dist/disclosure.d.ts +22 -0
- package/dist/disclosure.d.ts.map +1 -0
- package/dist/disclosure.js +98 -0
- package/dist/disclosure.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/plan.d.ts +88 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +161 -0
- package/dist/plan.js.map +1 -0
- package/dist/receipt.d.ts +77 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +164 -0
- package/dist/receipt.js.map +1 -0
- package/dist/recipes.d.ts +60 -0
- package/dist/recipes.d.ts.map +1 -0
- package/dist/recipes.js +57 -0
- package/dist/recipes.js.map +1 -0
- package/dist/rpc-response.d.ts +22 -0
- package/dist/rpc-response.d.ts.map +1 -0
- package/dist/rpc-response.js +44 -0
- package/dist/rpc-response.js.map +1 -0
- package/dist/shadow.d.ts +72 -0
- package/dist/shadow.d.ts.map +1 -0
- package/dist/shadow.js +61 -0
- package/dist/shadow.js.map +1 -0
- package/dist/share.d.ts +46 -0
- package/dist/share.d.ts.map +1 -0
- package/dist/share.js +99 -0
- package/dist/share.js.map +1 -0
- package/dist/subaccounts.d.ts +63 -0
- package/dist/subaccounts.d.ts.map +1 -0
- package/dist/subaccounts.js +83 -0
- package/dist/subaccounts.js.map +1 -0
- package/dist/wallet.d.ts +103 -0
- package/dist/wallet.d.ts.map +1 -0
- package/dist/wallet.js +135 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +36 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./plan.js";
|
|
2
|
+
export * from "./subaccounts.js";
|
|
3
|
+
export * from "./shadow.js";
|
|
4
|
+
export * from "./recipes.js";
|
|
5
|
+
export * from "./disclosure.js";
|
|
6
|
+
export * from "./wallet.js";
|
|
7
|
+
export * from "./crowd.js";
|
|
8
|
+
export * from "./anonymity.js";
|
|
9
|
+
export * from "./rpc-response.js";
|
|
10
|
+
export * from "./share.js";
|
|
11
|
+
export * from "./receipt.js";
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAE3B,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
|
package/dist/plan.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan encoding for the Jalin router.
|
|
3
|
+
*
|
|
4
|
+
* A plan is what the router takes instead of fixed parameters, so encoding it
|
|
5
|
+
* correctly is the whole integration surface. The layout below is Cairo's
|
|
6
|
+
* positional Serde for:
|
|
7
|
+
*
|
|
8
|
+
* privacy_invoke(
|
|
9
|
+
* pool_address: ContractAddress,
|
|
10
|
+
* steps: Array<Step>,
|
|
11
|
+
* outputs: Array<Output>,
|
|
12
|
+
* ) -> Span<OpenNoteDeposit>
|
|
13
|
+
*
|
|
14
|
+
* An `Array<T>` serialises as its length followed by its elements, and a `u128`
|
|
15
|
+
* occupies a single felt. Nothing here is nested any deeper than that.
|
|
16
|
+
*/
|
|
17
|
+
/** A felt, or a wallet placeholder such as `${openNoteIds[0]}`. */
|
|
18
|
+
export type Felt = string | bigint;
|
|
19
|
+
export interface Approval {
|
|
20
|
+
/** ERC-20 the step is allowed to move. */
|
|
21
|
+
token: Felt;
|
|
22
|
+
/** Allowance granted before the call and reset to zero after it. */
|
|
23
|
+
amount: bigint;
|
|
24
|
+
}
|
|
25
|
+
export interface Step {
|
|
26
|
+
target: Felt;
|
|
27
|
+
/** `selector!("...")` - use `selectorOf()` rather than hand-computing it. */
|
|
28
|
+
selector: Felt;
|
|
29
|
+
approvals: Approval[];
|
|
30
|
+
calldata: Felt[];
|
|
31
|
+
}
|
|
32
|
+
export interface Output {
|
|
33
|
+
token: Felt;
|
|
34
|
+
/**
|
|
35
|
+
* The open note this token is credited into. With the Wallet API this is the
|
|
36
|
+
* placeholder `${openNoteIds[N]}`, which the wallet resolves at submit time.
|
|
37
|
+
*/
|
|
38
|
+
noteId: Felt;
|
|
39
|
+
/** Floor on what is actually received, checked after the protocol fee. */
|
|
40
|
+
minAmount: bigint;
|
|
41
|
+
}
|
|
42
|
+
export interface Plan {
|
|
43
|
+
steps: Step[];
|
|
44
|
+
outputs: Output[];
|
|
45
|
+
}
|
|
46
|
+
/** Placeholder for the Nth open note declared alongside the invoke action. */
|
|
47
|
+
export declare function openNote(index: number): string;
|
|
48
|
+
/** Placeholder the wallet replaces with the pool address. */
|
|
49
|
+
export declare const POOL_ADDRESS = "${poolAddress}";
|
|
50
|
+
/**
|
|
51
|
+
* The router bounds step count and calldata length on chain, and those bounds
|
|
52
|
+
* are governed rather than fixed - so anything that has read the governor
|
|
53
|
+
* should pass what it read. Checking here means a plan fails in the caller
|
|
54
|
+
* rather than after a proof has been generated.
|
|
55
|
+
*/
|
|
56
|
+
export interface PlanLimits {
|
|
57
|
+
maxSteps: number;
|
|
58
|
+
maxCalldata: number;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* What the governor held at deployment. A fallback for callers with no way to
|
|
62
|
+
* read it, and wrong the moment a vote moves it - which is the whole point of
|
|
63
|
+
* `limits` being an argument everywhere below.
|
|
64
|
+
*/
|
|
65
|
+
export declare const DEFAULT_LIMITS: PlanLimits;
|
|
66
|
+
export declare function validatePlan(plan: Plan, limits?: PlanLimits): void;
|
|
67
|
+
/** Tokens a step may move but no output claims. Each one must end at zero. */
|
|
68
|
+
export declare function unclaimedTokens(plan: Plan): string[];
|
|
69
|
+
/**
|
|
70
|
+
* Flattens a plan into router calldata, leaving placeholder strings intact so
|
|
71
|
+
* the wallet can resolve them.
|
|
72
|
+
*/
|
|
73
|
+
export declare function encodePlan(plan: Plan, poolAddress?: Felt, limits?: PlanLimits): Felt[];
|
|
74
|
+
/** A `u256` argument occupies two felts, low then high. */
|
|
75
|
+
export declare function u256(value: bigint): [bigint, bigint];
|
|
76
|
+
/** Fluent builder. Every method returns a new plan rather than mutating one. */
|
|
77
|
+
export declare class PlanBuilder {
|
|
78
|
+
readonly plan: Plan;
|
|
79
|
+
private constructor();
|
|
80
|
+
static create(): PlanBuilder;
|
|
81
|
+
/** An arbitrary call. This is the escape hatch that makes the router general. */
|
|
82
|
+
call(step: Step): PlanBuilder;
|
|
83
|
+
/** Credit a token back into a shielded note. */
|
|
84
|
+
creditTo(token: Felt, noteId: Felt, minAmount?: bigint): PlanBuilder;
|
|
85
|
+
build(): Plan;
|
|
86
|
+
encode(poolAddress?: Felt): Felt[];
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,mEAAmE;AACnE,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,CAAA;AAElC,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,KAAK,EAAE,IAAI,CAAA;IACX,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,IAAI;IACnB,MAAM,EAAE,IAAI,CAAA;IACZ,6EAA6E;IAC7E,QAAQ,EAAE,IAAI,CAAA;IACd,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,EAAE,IAAI,EAAE,CAAA;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,IAAI,CAAA;IACX;;;OAGG;IACH,MAAM,EAAE,IAAI,CAAA;IACZ,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,IAAI,EAAE,CAAA;IACb,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK9C;AAED,6DAA6D;AAC7D,eAAO,MAAM,YAAY,mBAAmB,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,UAA6C,CAAA;AA6B1E,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,GAAE,UAA2B,GAAG,IAAI,CA4ClF;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,CAMpD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,IAAI,EACV,WAAW,GAAE,IAAmB,EAChC,MAAM,GAAE,UAA2B,GAClC,IAAI,EAAE,CAmBR;AAED,2DAA2D;AAC3D,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAIpD;AAED,gFAAgF;AAChF,qBAAa,WAAW;IACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAA;IAEnB,OAAO;IAIP,MAAM,CAAC,MAAM,IAAI,WAAW;IAI5B,iFAAiF;IACjF,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW;IAI7B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,SAAK,GAAG,WAAW;IAOhE,KAAK,IAAI,IAAI;IAKb,MAAM,CAAC,WAAW,GAAE,IAAmB,GAAG,IAAI,EAAE;CAGjD"}
|
package/dist/plan.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan encoding for the Jalin router.
|
|
3
|
+
*
|
|
4
|
+
* A plan is what the router takes instead of fixed parameters, so encoding it
|
|
5
|
+
* correctly is the whole integration surface. The layout below is Cairo's
|
|
6
|
+
* positional Serde for:
|
|
7
|
+
*
|
|
8
|
+
* privacy_invoke(
|
|
9
|
+
* pool_address: ContractAddress,
|
|
10
|
+
* steps: Array<Step>,
|
|
11
|
+
* outputs: Array<Output>,
|
|
12
|
+
* ) -> Span<OpenNoteDeposit>
|
|
13
|
+
*
|
|
14
|
+
* An `Array<T>` serialises as its length followed by its elements, and a `u128`
|
|
15
|
+
* occupies a single felt. Nothing here is nested any deeper than that.
|
|
16
|
+
*/
|
|
17
|
+
/** Placeholder for the Nth open note declared alongside the invoke action. */
|
|
18
|
+
export function openNote(index) {
|
|
19
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
20
|
+
throw new RangeError(`open note index must be a non-negative integer, got ${index}`);
|
|
21
|
+
}
|
|
22
|
+
return `\${openNoteIds[${index}]}`;
|
|
23
|
+
}
|
|
24
|
+
/** Placeholder the wallet replaces with the pool address. */
|
|
25
|
+
export const POOL_ADDRESS = '${poolAddress}';
|
|
26
|
+
/**
|
|
27
|
+
* What the governor held at deployment. A fallback for callers with no way to
|
|
28
|
+
* read it, and wrong the moment a vote moves it - which is the whole point of
|
|
29
|
+
* `limits` being an argument everywhere below.
|
|
30
|
+
*/
|
|
31
|
+
export const DEFAULT_LIMITS = { maxSteps: 8, maxCalldata: 64 };
|
|
32
|
+
/** Placeholders the wallet resolves at submit time; everything else is a felt. */
|
|
33
|
+
const PLACEHOLDER = /^\$\{(?:openNoteIds\[[0-9]+\]|poolAddress)\}$/;
|
|
34
|
+
/** The Starknet field prime. A felt is a residue mod this, so 2^251+17*2^192+1. */
|
|
35
|
+
const FIELD_PRIME = 2n ** 251n + 17n * 2n ** 192n + 1n;
|
|
36
|
+
/**
|
|
37
|
+
* Every value in a plan is eventually a felt on the wire.
|
|
38
|
+
*
|
|
39
|
+
* Without this a target of `not-an-address` travelled the whole way through the
|
|
40
|
+
* composer - a plan, a preview, an enabled submit button - and only failed when
|
|
41
|
+
* the wallet tried to encode it, which is after the user has been told it is
|
|
42
|
+
* ready to sign. Failing here names the field.
|
|
43
|
+
*/
|
|
44
|
+
function checkFelt(value, where) {
|
|
45
|
+
if (typeof value === 'string' && PLACEHOLDER.test(value))
|
|
46
|
+
return;
|
|
47
|
+
let felt;
|
|
48
|
+
try {
|
|
49
|
+
felt = BigInt(value);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
throw new Error(`${where} is not a felt: ${JSON.stringify(String(value))}`);
|
|
53
|
+
}
|
|
54
|
+
if (felt < 0n)
|
|
55
|
+
throw new Error(`${where} is negative, and a felt cannot be`);
|
|
56
|
+
if (felt >= FIELD_PRIME)
|
|
57
|
+
throw new Error(`${where} is larger than the field prime`);
|
|
58
|
+
}
|
|
59
|
+
export function validatePlan(plan, limits = DEFAULT_LIMITS) {
|
|
60
|
+
// Outputs may be empty. The pool accepts an empty Span, which is what makes
|
|
61
|
+
// fire-and-forget plans expressible: bridging value off Starknet, funding an
|
|
62
|
+
// escrow, casting a ballot. The residue rule is what keeps that safe.
|
|
63
|
+
if (plan.steps.length === 0) {
|
|
64
|
+
throw new Error('a plan needs at least one step, or it does nothing at all');
|
|
65
|
+
}
|
|
66
|
+
if (plan.steps.length > limits.maxSteps) {
|
|
67
|
+
throw new Error(`plan has ${plan.steps.length} steps, the router allows ${limits.maxSteps}`);
|
|
68
|
+
}
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
for (const output of plan.outputs) {
|
|
71
|
+
const key = String(output.token);
|
|
72
|
+
if (seen.has(key)) {
|
|
73
|
+
throw new Error(`output token ${key} is declared twice; the router rejects duplicates`);
|
|
74
|
+
}
|
|
75
|
+
seen.add(key);
|
|
76
|
+
checkFelt(output.token, `output ${key} token`);
|
|
77
|
+
if (output.minAmount < 0n)
|
|
78
|
+
throw new Error('minAmount cannot be negative');
|
|
79
|
+
}
|
|
80
|
+
for (const [i, step] of plan.steps.entries()) {
|
|
81
|
+
checkFelt(step.target, `step ${i} target`);
|
|
82
|
+
checkFelt(step.selector, `step ${i} selector`);
|
|
83
|
+
step.calldata.forEach((felt, j) => checkFelt(felt, `step ${i} calldata[${j}]`));
|
|
84
|
+
step.approvals.forEach((approval, j) => {
|
|
85
|
+
checkFelt(approval.token, `step ${i} approval[${j}] token`);
|
|
86
|
+
});
|
|
87
|
+
if (step.calldata.length > limits.maxCalldata) {
|
|
88
|
+
throw new Error(`step ${i} has ${step.calldata.length} felts of calldata, the router allows ${limits.maxCalldata}`);
|
|
89
|
+
}
|
|
90
|
+
for (const approval of step.approvals) {
|
|
91
|
+
if (approval.amount < 0n)
|
|
92
|
+
throw new Error(`step ${i} approves a negative amount`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// The residue rule bites here more often than anywhere else: a token a step is
|
|
96
|
+
// allowed to move, that no output claims, must end the transaction at zero.
|
|
97
|
+
// That is legitimate when a step spends its whole allowance, so this is a
|
|
98
|
+
// warning surface rather than an error - see docs/threat-model.md.
|
|
99
|
+
}
|
|
100
|
+
/** Tokens a step may move but no output claims. Each one must end at zero. */
|
|
101
|
+
export function unclaimedTokens(plan) {
|
|
102
|
+
const claimed = new Set(plan.outputs.map((o) => String(o.token)));
|
|
103
|
+
const touched = new Set(plan.steps.flatMap((s) => s.approvals.map((a) => String(a.token))));
|
|
104
|
+
return [...touched].filter((t) => !claimed.has(t));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Flattens a plan into router calldata, leaving placeholder strings intact so
|
|
108
|
+
* the wallet can resolve them.
|
|
109
|
+
*/
|
|
110
|
+
export function encodePlan(plan, poolAddress = POOL_ADDRESS, limits = DEFAULT_LIMITS) {
|
|
111
|
+
validatePlan(plan, limits);
|
|
112
|
+
const calldata = [poolAddress, BigInt(plan.steps.length)];
|
|
113
|
+
for (const step of plan.steps) {
|
|
114
|
+
calldata.push(step.target, step.selector, BigInt(step.approvals.length));
|
|
115
|
+
for (const approval of step.approvals) {
|
|
116
|
+
calldata.push(approval.token, approval.amount);
|
|
117
|
+
}
|
|
118
|
+
calldata.push(BigInt(step.calldata.length), ...step.calldata);
|
|
119
|
+
}
|
|
120
|
+
calldata.push(BigInt(plan.outputs.length));
|
|
121
|
+
for (const output of plan.outputs) {
|
|
122
|
+
calldata.push(output.token, output.noteId, output.minAmount);
|
|
123
|
+
}
|
|
124
|
+
return calldata;
|
|
125
|
+
}
|
|
126
|
+
/** A `u256` argument occupies two felts, low then high. */
|
|
127
|
+
export function u256(value) {
|
|
128
|
+
if (value < 0n)
|
|
129
|
+
throw new RangeError('u256 cannot be negative');
|
|
130
|
+
const MASK = (1n << 128n) - 1n;
|
|
131
|
+
return [value & MASK, value >> 128n];
|
|
132
|
+
}
|
|
133
|
+
/** Fluent builder. Every method returns a new plan rather than mutating one. */
|
|
134
|
+
export class PlanBuilder {
|
|
135
|
+
plan;
|
|
136
|
+
constructor(plan) {
|
|
137
|
+
this.plan = plan;
|
|
138
|
+
}
|
|
139
|
+
static create() {
|
|
140
|
+
return new PlanBuilder({ steps: [], outputs: [] });
|
|
141
|
+
}
|
|
142
|
+
/** An arbitrary call. This is the escape hatch that makes the router general. */
|
|
143
|
+
call(step) {
|
|
144
|
+
return new PlanBuilder({ ...this.plan, steps: [...this.plan.steps, step] });
|
|
145
|
+
}
|
|
146
|
+
/** Credit a token back into a shielded note. */
|
|
147
|
+
creditTo(token, noteId, minAmount = 0n) {
|
|
148
|
+
return new PlanBuilder({
|
|
149
|
+
...this.plan,
|
|
150
|
+
outputs: [...this.plan.outputs, { token, noteId, minAmount }],
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
build() {
|
|
154
|
+
validatePlan(this.plan);
|
|
155
|
+
return this.plan;
|
|
156
|
+
}
|
|
157
|
+
encode(poolAddress = POOL_ADDRESS) {
|
|
158
|
+
return encodePlan(this.build(), poolAddress);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=plan.js.map
|
package/dist/plan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does a transaction count?
|
|
3
|
+
*
|
|
4
|
+
* The sprint rules ask four things of every listed transaction: it exists, it
|
|
5
|
+
* succeeded, it touched the STRK20 pool, and — if the project deployed contracts
|
|
6
|
+
* — it ran through one of them. All four are decidable from the receipt, so
|
|
7
|
+
* nobody has to wait for a panel to find out.
|
|
8
|
+
*
|
|
9
|
+
* The rules live here rather than in the script that checks the manifest,
|
|
10
|
+
* because the demo asks the same question of a transaction it just submitted,
|
|
11
|
+
* and two copies of a rule are two rules.
|
|
12
|
+
*/
|
|
13
|
+
/** The minimum shape of a Starknet receipt this needs. */
|
|
14
|
+
export interface ReceiptLike {
|
|
15
|
+
execution_status?: string;
|
|
16
|
+
finality_status?: string;
|
|
17
|
+
events?: {
|
|
18
|
+
from_address: string;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
export interface Verdict {
|
|
22
|
+
exists: boolean;
|
|
23
|
+
succeeded: boolean;
|
|
24
|
+
touchedPool: boolean;
|
|
25
|
+
/** null when no contracts are declared: the rule is conditional on having any. */
|
|
26
|
+
throughOurs: boolean | null;
|
|
27
|
+
qualifies: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function checkReceipt(receipt: ReceiptLike | null | undefined, context: {
|
|
30
|
+
pool: string;
|
|
31
|
+
ours?: string[];
|
|
32
|
+
}): Verdict;
|
|
33
|
+
/** One line a person can read, for a UI that has no room for four booleans. */
|
|
34
|
+
export declare function describeVerdict(verdict: Verdict): string;
|
|
35
|
+
/**
|
|
36
|
+
* Hashes listed more than once, by value.
|
|
37
|
+
*
|
|
38
|
+
* A manifest that names the same transaction three times has one transaction in
|
|
39
|
+
* it, and a checker that answers "3 of 3 would count" has told the team exactly
|
|
40
|
+
* the thing that gets them rejected. Compared as felts rather than strings,
|
|
41
|
+
* because `0x0abc` and `0xabc` are one hash written two ways and the padded
|
|
42
|
+
* variant is the version somebody pastes without noticing.
|
|
43
|
+
*
|
|
44
|
+
* Returns each repeated hash once, in the order it first appeared.
|
|
45
|
+
*/
|
|
46
|
+
export declare function findDuplicates(hashes: string[]): string[];
|
|
47
|
+
/** Distinct transactions in a list, by felt value. */
|
|
48
|
+
export declare function countDistinct(hashes: string[]): number;
|
|
49
|
+
export interface Manifest {
|
|
50
|
+
transactions: string[];
|
|
51
|
+
contracts: string[];
|
|
52
|
+
demoVideo: string;
|
|
53
|
+
demoUrl: string;
|
|
54
|
+
}
|
|
55
|
+
export type ManifestResult = {
|
|
56
|
+
ok: true;
|
|
57
|
+
manifest: Manifest;
|
|
58
|
+
} | {
|
|
59
|
+
ok: false;
|
|
60
|
+
reason: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Reads a strk20.json, or says why it cannot.
|
|
64
|
+
*
|
|
65
|
+
* Shared by the offline checker and the web one so a team gets the same answer
|
|
66
|
+
* whichever they use. Pure, because these branches only run when a file is
|
|
67
|
+
* already wrong, and that is exactly where untested code rots.
|
|
68
|
+
*
|
|
69
|
+
* The null case is not hypothetical: `JSON.parse('null')` succeeds and returns
|
|
70
|
+
* null, so reading a property off the result is a TypeError - a crash for a
|
|
71
|
+
* file anybody can commit and hand to a public endpoint.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseManifest(text: string, limits?: {
|
|
74
|
+
transactions: number;
|
|
75
|
+
contracts: number;
|
|
76
|
+
}): ManifestResult;
|
|
77
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,MAAM,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB,WAAW,EAAE,OAAO,CAAA;IACpB,kFAAkF;IAClF,WAAW,EAAE,OAAO,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,OAAO,CAAA;CACnB;AAoBD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EACvC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACzC,OAAO,CAkBT;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAUxD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAyBzD;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAUtD;AAED,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GAChC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjC;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAwC,GACxF,cAAc,CA2ChB"}
|
package/dist/receipt.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does a transaction count?
|
|
3
|
+
*
|
|
4
|
+
* The sprint rules ask four things of every listed transaction: it exists, it
|
|
5
|
+
* succeeded, it touched the STRK20 pool, and — if the project deployed contracts
|
|
6
|
+
* — it ran through one of them. All four are decidable from the receipt, so
|
|
7
|
+
* nobody has to wait for a panel to find out.
|
|
8
|
+
*
|
|
9
|
+
* The rules live here rather than in the script that checks the manifest,
|
|
10
|
+
* because the demo asks the same question of a transaction it just submitted,
|
|
11
|
+
* and two copies of a rule are two rules.
|
|
12
|
+
*/
|
|
13
|
+
const NOT_FOUND = {
|
|
14
|
+
exists: false,
|
|
15
|
+
succeeded: false,
|
|
16
|
+
touchedPool: false,
|
|
17
|
+
throughOurs: null,
|
|
18
|
+
qualifies: false,
|
|
19
|
+
};
|
|
20
|
+
function sameAddress(a, b) {
|
|
21
|
+
try {
|
|
22
|
+
// 0x0abc and 0xABC are one address written two ways. Comparing the strings
|
|
23
|
+
// would report a transaction as missing the pool it plainly touched.
|
|
24
|
+
return BigInt(a) === BigInt(b);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function checkReceipt(receipt, context) {
|
|
31
|
+
if (!receipt)
|
|
32
|
+
return NOT_FOUND;
|
|
33
|
+
const succeeded = (receipt.execution_status ?? receipt.finality_status) === 'SUCCEEDED';
|
|
34
|
+
const emitters = (receipt.events ?? []).map((event) => event.from_address);
|
|
35
|
+
const touchedPool = emitters.some((address) => sameAddress(address, context.pool));
|
|
36
|
+
const ours = context.ours ?? [];
|
|
37
|
+
const throughOurs = ours.length === 0 ? null : emitters.some((a) => ours.some((o) => sameAddress(a, o)));
|
|
38
|
+
return {
|
|
39
|
+
exists: true,
|
|
40
|
+
succeeded,
|
|
41
|
+
touchedPool,
|
|
42
|
+
throughOurs,
|
|
43
|
+
qualifies: succeeded && touchedPool && (throughOurs === null || throughOurs),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** One line a person can read, for a UI that has no room for four booleans. */
|
|
47
|
+
export function describeVerdict(verdict) {
|
|
48
|
+
if (!verdict.exists)
|
|
49
|
+
return 'not found on chain yet';
|
|
50
|
+
if (!verdict.succeeded)
|
|
51
|
+
return 'reverted';
|
|
52
|
+
if (!verdict.touchedPool)
|
|
53
|
+
return 'succeeded, but touched no pool event — it would not count';
|
|
54
|
+
if (verdict.throughOurs === false) {
|
|
55
|
+
return 'touched the pool, but not through a contract of ours — it would not count';
|
|
56
|
+
}
|
|
57
|
+
return verdict.throughOurs === null
|
|
58
|
+
? 'counts: succeeded and touched the pool'
|
|
59
|
+
: 'counts: succeeded, touched the pool, ran through our contract';
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Hashes listed more than once, by value.
|
|
63
|
+
*
|
|
64
|
+
* A manifest that names the same transaction three times has one transaction in
|
|
65
|
+
* it, and a checker that answers "3 of 3 would count" has told the team exactly
|
|
66
|
+
* the thing that gets them rejected. Compared as felts rather than strings,
|
|
67
|
+
* because `0x0abc` and `0xabc` are one hash written two ways and the padded
|
|
68
|
+
* variant is the version somebody pastes without noticing.
|
|
69
|
+
*
|
|
70
|
+
* Returns each repeated hash once, in the order it first appeared.
|
|
71
|
+
*/
|
|
72
|
+
export function findDuplicates(hashes) {
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
const repeated = new Set();
|
|
75
|
+
const order = [];
|
|
76
|
+
for (const hash of hashes) {
|
|
77
|
+
let key;
|
|
78
|
+
try {
|
|
79
|
+
key = BigInt(hash).toString();
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Not a felt. Whatever else is wrong with it, it is not a duplicate.
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (seen.has(key)) {
|
|
86
|
+
if (!repeated.has(key)) {
|
|
87
|
+
repeated.add(key);
|
|
88
|
+
order.push(hash);
|
|
89
|
+
}
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
seen.add(key);
|
|
93
|
+
}
|
|
94
|
+
return order;
|
|
95
|
+
}
|
|
96
|
+
/** Distinct transactions in a list, by felt value. */
|
|
97
|
+
export function countDistinct(hashes) {
|
|
98
|
+
const seen = new Set();
|
|
99
|
+
for (const hash of hashes) {
|
|
100
|
+
try {
|
|
101
|
+
seen.add(BigInt(hash).toString());
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Ignored: an unparseable entry is not a transaction.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return seen.size;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Reads a strk20.json, or says why it cannot.
|
|
111
|
+
*
|
|
112
|
+
* Shared by the offline checker and the web one so a team gets the same answer
|
|
113
|
+
* whichever they use. Pure, because these branches only run when a file is
|
|
114
|
+
* already wrong, and that is exactly where untested code rots.
|
|
115
|
+
*
|
|
116
|
+
* The null case is not hypothetical: `JSON.parse('null')` succeeds and returns
|
|
117
|
+
* null, so reading a property off the result is a TypeError - a crash for a
|
|
118
|
+
* file anybody can commit and hand to a public endpoint.
|
|
119
|
+
*/
|
|
120
|
+
export function parseManifest(text, limits = { transactions: 20, contracts: 16 }) {
|
|
121
|
+
let parsed;
|
|
122
|
+
try {
|
|
123
|
+
parsed = JSON.parse(text);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return { ok: false, reason: 'that file is not JSON' };
|
|
127
|
+
}
|
|
128
|
+
// A string, a number and an array all survive property access and would read
|
|
129
|
+
// as an empty manifest. Only an object is one.
|
|
130
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
131
|
+
return { ok: false, reason: 'strk20.json must be a JSON object' };
|
|
132
|
+
}
|
|
133
|
+
const raw = parsed;
|
|
134
|
+
const felts = (value, limit, field) => {
|
|
135
|
+
if (value === undefined || value === null)
|
|
136
|
+
return [];
|
|
137
|
+
if (!Array.isArray(value))
|
|
138
|
+
return `${field} must be an array`;
|
|
139
|
+
if (value.length > limit)
|
|
140
|
+
return `${field} lists more than ${limit} entries`;
|
|
141
|
+
for (const entry of value) {
|
|
142
|
+
if (typeof entry !== 'string' || !/^0x[0-9a-fA-F]{1,64}$/.test(entry)) {
|
|
143
|
+
return `${field} contains something that is not a felt`;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return value;
|
|
147
|
+
};
|
|
148
|
+
const transactions = felts(raw.transactions, limits.transactions, 'transactions');
|
|
149
|
+
if (typeof transactions === 'string')
|
|
150
|
+
return { ok: false, reason: transactions };
|
|
151
|
+
const contracts = felts(raw.contracts, limits.contracts, 'contracts');
|
|
152
|
+
if (typeof contracts === 'string')
|
|
153
|
+
return { ok: false, reason: contracts };
|
|
154
|
+
return {
|
|
155
|
+
ok: true,
|
|
156
|
+
manifest: {
|
|
157
|
+
transactions,
|
|
158
|
+
contracts,
|
|
159
|
+
demoVideo: typeof raw.demo_video === 'string' ? raw.demo_video : '',
|
|
160
|
+
demoUrl: typeof raw.demo_url === 'string' ? raw.demo_url : '',
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan recipes.
|
|
3
|
+
*
|
|
4
|
+
* These are conveniences, not capabilities. The router has no idea what a
|
|
5
|
+
* deposit is - every one of these produces the same `Step` any other call would,
|
|
6
|
+
* which is the point: a protocol does not need a Jalin adapter to be reachable
|
|
7
|
+
* from inside a private transaction, it just needs an ABI.
|
|
8
|
+
*
|
|
9
|
+
* Only shapes checked against a live mainnet contract live here. A recipe that
|
|
10
|
+
* encodes a guessed argument order is worse than no recipe: it reads as
|
|
11
|
+
* supported, and it fails after a proof has already been paid for.
|
|
12
|
+
*/
|
|
13
|
+
import { type Felt, type Plan, type Step } from './plan.ts';
|
|
14
|
+
export interface CallStep {
|
|
15
|
+
/** Contract to call. */
|
|
16
|
+
target: Felt;
|
|
17
|
+
/** `selector!("...")` for the entry point. */
|
|
18
|
+
selector: Felt;
|
|
19
|
+
/** Token the step is allowed to move, and how much. */
|
|
20
|
+
spend?: {
|
|
21
|
+
token: Felt;
|
|
22
|
+
amount: bigint;
|
|
23
|
+
};
|
|
24
|
+
/** Calldata after any `spend` is approved. Already flattened to felts. */
|
|
25
|
+
calldata: Felt[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The general case, and the one to reach for. Anything with an ABI is reachable
|
|
29
|
+
* through it, including every venue that has no recipe here.
|
|
30
|
+
*/
|
|
31
|
+
export declare function callStep({ target, selector, spend, calldata }: CallStep): Step;
|
|
32
|
+
/**
|
|
33
|
+
* A deposit into an ERC-4626 vault: `deposit(assets: u256, receiver: ContractAddress)`.
|
|
34
|
+
*
|
|
35
|
+
* Verified against Endur's xSTRK vault on Starknet mainnet
|
|
36
|
+
* (`0x28d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a`), whose
|
|
37
|
+
* `asset()` is STRK. ERC-4626 fixes the argument order, which is why this shape
|
|
38
|
+
* is safe to encode once and reuse - unlike a swap, where every venue differs.
|
|
39
|
+
*
|
|
40
|
+
* `receiver` should be the router: the shares have to land somewhere the pool can
|
|
41
|
+
* pull them from, and the router holds nothing after the transaction ends.
|
|
42
|
+
*/
|
|
43
|
+
export declare function depositStep(args: {
|
|
44
|
+
market: Felt;
|
|
45
|
+
selector: Felt;
|
|
46
|
+
asset: Felt;
|
|
47
|
+
amount: bigint;
|
|
48
|
+
receiver: Felt;
|
|
49
|
+
}): Step;
|
|
50
|
+
/**
|
|
51
|
+
* A plan that credits nothing back.
|
|
52
|
+
*
|
|
53
|
+
* Legal, and not an oversight: the pool accepts an empty `Span`, which is what
|
|
54
|
+
* makes bridging value off Starknet, funding an escrow, or casting a ballot
|
|
55
|
+
* expressible at all. Invariant I4 is what keeps it honest - every token a step
|
|
56
|
+
* could move must still end the transaction at zero, so "it all left" is checked
|
|
57
|
+
* rather than assumed.
|
|
58
|
+
*/
|
|
59
|
+
export declare function oneWay(step: Step): Plan;
|
|
60
|
+
//# sourceMappingURL=recipes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipes.d.ts","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAQ,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAEjE,MAAM,WAAW,QAAQ;IACvB,wBAAwB;IACxB,MAAM,EAAE,IAAI,CAAA;IACZ,8CAA8C;IAC9C,QAAQ,EAAE,IAAI,CAAA;IACd,uDAAuD;IACvD,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,0EAA0E;IAC1E,QAAQ,EAAE,IAAI,EAAE,CAAA;CACjB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,QAAQ,GAAG,IAAI,CAO9E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,MAAM,EAAE,IAAI,CAAA;IACZ,QAAQ,EAAE,IAAI,CAAA;IACd,KAAK,EAAE,IAAI,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,IAAI,CAAA;CACf,GAAG,IAAI,CAOP;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAEvC"}
|
package/dist/recipes.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan recipes.
|
|
3
|
+
*
|
|
4
|
+
* These are conveniences, not capabilities. The router has no idea what a
|
|
5
|
+
* deposit is - every one of these produces the same `Step` any other call would,
|
|
6
|
+
* which is the point: a protocol does not need a Jalin adapter to be reachable
|
|
7
|
+
* from inside a private transaction, it just needs an ABI.
|
|
8
|
+
*
|
|
9
|
+
* Only shapes checked against a live mainnet contract live here. A recipe that
|
|
10
|
+
* encodes a guessed argument order is worse than no recipe: it reads as
|
|
11
|
+
* supported, and it fails after a proof has already been paid for.
|
|
12
|
+
*/
|
|
13
|
+
import { u256 } from "./plan.js";
|
|
14
|
+
/**
|
|
15
|
+
* The general case, and the one to reach for. Anything with an ABI is reachable
|
|
16
|
+
* through it, including every venue that has no recipe here.
|
|
17
|
+
*/
|
|
18
|
+
export function callStep({ target, selector, spend, calldata }) {
|
|
19
|
+
return {
|
|
20
|
+
target,
|
|
21
|
+
selector,
|
|
22
|
+
approvals: spend ? [{ token: spend.token, amount: spend.amount }] : [],
|
|
23
|
+
calldata,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A deposit into an ERC-4626 vault: `deposit(assets: u256, receiver: ContractAddress)`.
|
|
28
|
+
*
|
|
29
|
+
* Verified against Endur's xSTRK vault on Starknet mainnet
|
|
30
|
+
* (`0x28d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a`), whose
|
|
31
|
+
* `asset()` is STRK. ERC-4626 fixes the argument order, which is why this shape
|
|
32
|
+
* is safe to encode once and reuse - unlike a swap, where every venue differs.
|
|
33
|
+
*
|
|
34
|
+
* `receiver` should be the router: the shares have to land somewhere the pool can
|
|
35
|
+
* pull them from, and the router holds nothing after the transaction ends.
|
|
36
|
+
*/
|
|
37
|
+
export function depositStep(args) {
|
|
38
|
+
return callStep({
|
|
39
|
+
target: args.market,
|
|
40
|
+
selector: args.selector,
|
|
41
|
+
spend: { token: args.asset, amount: args.amount },
|
|
42
|
+
calldata: [...u256(args.amount), args.receiver],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A plan that credits nothing back.
|
|
47
|
+
*
|
|
48
|
+
* Legal, and not an oversight: the pool accepts an empty `Span`, which is what
|
|
49
|
+
* makes bridging value off Starknet, funding an escrow, or casting a ballot
|
|
50
|
+
* expressible at all. Invariant I4 is what keeps it honest - every token a step
|
|
51
|
+
* could move must still end the transaction at zero, so "it all left" is checked
|
|
52
|
+
* rather than assumed.
|
|
53
|
+
*/
|
|
54
|
+
export function oneWay(step) {
|
|
55
|
+
return { steps: [step], outputs: [] };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=recipes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recipes.js","sourceRoot":"","sources":["../src/recipes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,IAAI,EAAmC,MAAM,WAAW,CAAA;AAajE;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAY;IACtE,OAAO;QACL,MAAM;QACN,QAAQ;QACR,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;QACtE,QAAQ;KACT,CAAA;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAM3B;IACC,OAAO,QAAQ,CAAC;QACd,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;QACjD,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC;KAChD,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CAAC,IAAU;IAC/B,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAA;AACvC,CAAC"}
|