@stellaris-lab/por-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/README.md +77 -0
- package/dist/audit.d.ts +53 -0
- package/dist/audit.js +48 -0
- package/dist/backend.d.ts +70 -0
- package/dist/backend.js +91 -0
- package/dist/codec.d.ts +50 -0
- package/dist/codec.js +110 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.js +60 -0
- package/dist/domain.d.ts +199 -0
- package/dist/domain.js +57 -0
- package/dist/encoding.d.ts +67 -0
- package/dist/encoding.js +120 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.js +54 -0
- package/dist/events.d.ts +78 -0
- package/dist/events.js +66 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/manifest.d.ts +22 -0
- package/dist/manifest.js +54 -0
- package/dist/operations.d.ts +109 -0
- package/dist/operations.js +164 -0
- package/dist/persistence.d.ts +67 -0
- package/dist/persistence.js +154 -0
- package/dist/pipeline.d.ts +37 -0
- package/dist/pipeline.js +95 -0
- package/dist/policy.d.ts +27 -0
- package/dist/policy.js +60 -0
- package/dist/prove.d.ts +27 -0
- package/dist/prove.js +76 -0
- package/dist/reconciler.d.ts +65 -0
- package/dist/reconciler.js +152 -0
- package/dist/registry.d.ts +71 -0
- package/dist/registry.js +167 -0
- package/dist/signals.d.ts +36 -0
- package/dist/signals.js +194 -0
- package/dist/stellar.d.ts +129 -0
- package/dist/stellar.js +256 -0
- package/dist/transport.d.ts +70 -0
- package/dist/transport.js +94 -0
- package/package.json +50 -0
package/dist/registry.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attestation registry/indexing layer.
|
|
3
|
+
*
|
|
4
|
+
* Protocol SDKs usually expose a state-oriented subsystem in addition to direct
|
|
5
|
+
* RPC calls. This module reconciles contract state into a local store, detects
|
|
6
|
+
* period gaps/replays, and gives backend services a stable query surface without
|
|
7
|
+
* introducing frontend/demo code.
|
|
8
|
+
*/
|
|
9
|
+
import { StellarisError } from "./errors.js";
|
|
10
|
+
export class InMemoryAttestationStore {
|
|
11
|
+
records = new Map();
|
|
12
|
+
put(record) {
|
|
13
|
+
this.records.set(keyOf(record.attestation.issuer, record.attestation.periodId), record);
|
|
14
|
+
}
|
|
15
|
+
get(issuer, periodId) {
|
|
16
|
+
return this.records.get(keyOf(issuer, periodId)) ?? null;
|
|
17
|
+
}
|
|
18
|
+
list(issuer) {
|
|
19
|
+
return [...this.records.values()]
|
|
20
|
+
.filter((record) => record.attestation.issuer === issuer)
|
|
21
|
+
.sort((a, b) => comparePeriod(a.attestation.periodId, b.attestation.periodId));
|
|
22
|
+
}
|
|
23
|
+
clear(issuer) {
|
|
24
|
+
if (issuer === undefined) {
|
|
25
|
+
this.records.clear();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const key of [...this.records.keys()]) {
|
|
29
|
+
if (key.startsWith(`${issuer}:`)) {
|
|
30
|
+
this.records.delete(key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class AttestationRegistry {
|
|
36
|
+
client;
|
|
37
|
+
store;
|
|
38
|
+
constructor(input) {
|
|
39
|
+
this.client = input.client;
|
|
40
|
+
this.store = input.store ?? new InMemoryAttestationStore();
|
|
41
|
+
}
|
|
42
|
+
async put(record) {
|
|
43
|
+
validateIndexedAttestation(record, this.client.deployment);
|
|
44
|
+
await this.store.put(record);
|
|
45
|
+
}
|
|
46
|
+
async get(issuer, periodId) {
|
|
47
|
+
const cached = await this.store.get(issuer, periodId);
|
|
48
|
+
if (cached) {
|
|
49
|
+
return cached;
|
|
50
|
+
}
|
|
51
|
+
const attestation = await this.client.getAttestation(issuer, periodId);
|
|
52
|
+
if (!attestation) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const record = indexAttestation(attestation, this.client.deployment, "contract");
|
|
56
|
+
await this.store.put(record);
|
|
57
|
+
return record;
|
|
58
|
+
}
|
|
59
|
+
async refreshIssuer(issuer, options = {}) {
|
|
60
|
+
const periods = dedupeAndSort(await this.client.listPeriods(issuer));
|
|
61
|
+
const source = options.source ?? "contract";
|
|
62
|
+
for (const periodId of periods) {
|
|
63
|
+
const attestation = await this.client.getAttestation(issuer, periodId);
|
|
64
|
+
if (!attestation) {
|
|
65
|
+
if (options.failOnMissingAttestation ?? true) {
|
|
66
|
+
throw StellarisError.contract("contract listed period without attestation", {
|
|
67
|
+
issuer,
|
|
68
|
+
periodId: periodId.toString(),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
await this.store.put(indexAttestation(attestation, this.client.deployment, source));
|
|
74
|
+
}
|
|
75
|
+
return this.snapshot(issuer);
|
|
76
|
+
}
|
|
77
|
+
async snapshot(issuer) {
|
|
78
|
+
const attestations = await this.store.list(issuer);
|
|
79
|
+
const periods = attestations.map((record) => record.attestation.periodId);
|
|
80
|
+
return {
|
|
81
|
+
issuer,
|
|
82
|
+
deployment: this.client.deployment,
|
|
83
|
+
indexedAt: new Date().toISOString(),
|
|
84
|
+
periods,
|
|
85
|
+
attestations,
|
|
86
|
+
diagnostics: analyzeRegistry(attestations),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function indexAttestation(attestation, deployment, source = "manual") {
|
|
91
|
+
return {
|
|
92
|
+
attestation,
|
|
93
|
+
deployment,
|
|
94
|
+
source,
|
|
95
|
+
indexedAt: new Date().toISOString(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function analyzeRegistry(records) {
|
|
99
|
+
const periods = records.map((record) => record.attestation.periodId).sort(comparePeriod);
|
|
100
|
+
const duplicatePeriods = findDuplicatePeriods(periods);
|
|
101
|
+
const missingPeriods = findPeriodGaps(dedupeAndSort(periods));
|
|
102
|
+
const nonSolventPeriods = records
|
|
103
|
+
.filter((record) => !record.attestation.solvent)
|
|
104
|
+
.map((record) => record.attestation.periodId)
|
|
105
|
+
.sort(comparePeriod);
|
|
106
|
+
return {
|
|
107
|
+
duplicatePeriods,
|
|
108
|
+
missingPeriods,
|
|
109
|
+
nonSolventPeriods,
|
|
110
|
+
newestPeriod: periods.length === 0 ? null : periods[periods.length - 1] ?? null,
|
|
111
|
+
oldestPeriod: periods.length === 0 ? null : periods[0] ?? null,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export function findPeriodGaps(periods) {
|
|
115
|
+
const sorted = dedupeAndSort(periods);
|
|
116
|
+
const gaps = [];
|
|
117
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
118
|
+
const previous = sorted[i - 1];
|
|
119
|
+
const current = sorted[i];
|
|
120
|
+
if (previous === undefined || current === undefined || current <= previous + 1n) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const missing = [];
|
|
124
|
+
for (let period = previous + 1n; period < current; period++) {
|
|
125
|
+
missing.push(period);
|
|
126
|
+
}
|
|
127
|
+
gaps.push({ after: previous, before: current, missing });
|
|
128
|
+
}
|
|
129
|
+
return gaps;
|
|
130
|
+
}
|
|
131
|
+
export function findDuplicatePeriods(periods) {
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
const duplicated = new Set();
|
|
134
|
+
for (const period of periods) {
|
|
135
|
+
const key = period.toString();
|
|
136
|
+
if (seen.has(key)) {
|
|
137
|
+
duplicated.add(key);
|
|
138
|
+
}
|
|
139
|
+
seen.add(key);
|
|
140
|
+
}
|
|
141
|
+
return [...duplicated].map((period) => BigInt(period)).sort(comparePeriod);
|
|
142
|
+
}
|
|
143
|
+
export function dedupeAndSort(periods) {
|
|
144
|
+
return [...new Set(periods.map((period) => period.toString()))]
|
|
145
|
+
.map((period) => BigInt(period))
|
|
146
|
+
.sort(comparePeriod);
|
|
147
|
+
}
|
|
148
|
+
export function comparePeriod(a, b) {
|
|
149
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
150
|
+
}
|
|
151
|
+
function validateIndexedAttestation(record, deployment) {
|
|
152
|
+
if (record.deployment.contractId !== deployment.contractId) {
|
|
153
|
+
throw StellarisError.validation("indexed attestation belongs to a different contract", {
|
|
154
|
+
expected: deployment.contractId,
|
|
155
|
+
actual: record.deployment.contractId,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (record.deployment.networkPassphrase !== deployment.networkPassphrase) {
|
|
159
|
+
throw StellarisError.validation("indexed attestation belongs to a different network", {
|
|
160
|
+
expected: deployment.networkPassphrase,
|
|
161
|
+
actual: record.deployment.networkPassphrase,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function keyOf(issuer, periodId) {
|
|
166
|
+
return `${issuer}:${periodId}`;
|
|
167
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Public signal parsing and validation. */
|
|
2
|
+
import { FieldElement, PublicSignals, PublicSignalsV2, PublicSignalsV3 } from "./domain.js";
|
|
3
|
+
export declare function assertFieldElement(value: string, label: string): void;
|
|
4
|
+
export declare function parsePublicSignals(signals: readonly FieldElement[]): PublicSignals;
|
|
5
|
+
export declare function encodePublicSignals(signals: PublicSignals): readonly FieldElement[];
|
|
6
|
+
/**
|
|
7
|
+
* Parse the v2 public-signal layout, mirroring the on-chain
|
|
8
|
+
* `parse_public_signals_v2` (contracts/stellaris/src/signals.rs):
|
|
9
|
+
* [ solvent, reserveCommitment, liabRoot, liabTotal, period ]
|
|
10
|
+
*
|
|
11
|
+
* `reserveCommitment` and `liabRoot` are full-width field elements (hashes) kept
|
|
12
|
+
* as decimal strings; `liabTotal` is the SNARK-proven total liabilities.
|
|
13
|
+
*/
|
|
14
|
+
export declare function parsePublicSignalsV2(signals: readonly FieldElement[]): PublicSignalsV2;
|
|
15
|
+
export declare function encodePublicSignalsV2(signals: PublicSignalsV2): readonly FieldElement[];
|
|
16
|
+
/**
|
|
17
|
+
* Parse the v3 multi-asset public-signal layout, mirroring the on-chain
|
|
18
|
+
* `parse_public_signals_v3` (contracts/stellaris/src/signals.rs):
|
|
19
|
+
* [ aggregateSolvent, reserveCommitment, priceCommitment,
|
|
20
|
+
* assetSolvent[0..N_ASSETS_V3-1], period ]
|
|
21
|
+
*
|
|
22
|
+
* `reserveCommitment`/`priceCommitment` are full-width field elements kept as
|
|
23
|
+
* decimal strings; the aggregate flag and each per-asset flag must be 0 or 1.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parsePublicSignalsV3(signals: readonly FieldElement[]): PublicSignalsV3;
|
|
26
|
+
/**
|
|
27
|
+
* Encode the v3 multi-asset public-signal layout, the inverse of
|
|
28
|
+
* {@link parsePublicSignalsV3}. Produces the exact decimal-string vector the
|
|
29
|
+
* on-chain `parse_public_signals_v3` expects:
|
|
30
|
+
* [ aggregateSolvent, reserveCommitment, priceCommitment,
|
|
31
|
+
* assetSolvent[0..N_ASSETS_V3-1], period ]
|
|
32
|
+
*
|
|
33
|
+
* `assetSolvent` must contain exactly `N_ASSETS_V3` flags; a different length is
|
|
34
|
+
* a programming error and is rejected rather than silently padded/truncated.
|
|
35
|
+
*/
|
|
36
|
+
export declare function encodePublicSignalsV3(signals: PublicSignalsV3): readonly FieldElement[];
|
package/dist/signals.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/** Public signal parsing and validation. */
|
|
2
|
+
import { N_PUBLIC_SIGNALS, N_PUBLIC_SIGNALS_V2, SIGNAL_INDEX_COMMITMENT, SIGNAL_INDEX_LIABILITIES, SIGNAL_INDEX_PERIOD, SIGNAL_INDEX_SOLVENT, SIGNAL_INDEX_V2_LIAB_ROOT, SIGNAL_INDEX_V2_LIAB_TOTAL, SIGNAL_INDEX_V2_PERIOD, SIGNAL_INDEX_V2_RESERVE_COMMITMENT, SIGNAL_INDEX_V2_SOLVENT, N_ASSETS_V3, N_PUBLIC_SIGNALS_V3, SIGNAL_INDEX_V3_AGGREGATE_SOLVENT, SIGNAL_INDEX_V3_ASSET_SOLVENT_BASE, SIGNAL_INDEX_V3_PERIOD, SIGNAL_INDEX_V3_PRICE_COMMITMENT, SIGNAL_INDEX_V3_RESERVE_COMMITMENT, } from "./constants.js";
|
|
3
|
+
import { StellarisError } from "./errors.js";
|
|
4
|
+
const FIELD_DECIMAL = /^(0|[1-9][0-9]*)$/;
|
|
5
|
+
export function assertFieldElement(value, label) {
|
|
6
|
+
if (!FIELD_DECIMAL.test(value)) {
|
|
7
|
+
throw StellarisError.validation(`${label} must be a decimal field element string`, { value });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function parsePublicSignals(signals) {
|
|
11
|
+
if (signals.length !== N_PUBLIC_SIGNALS) {
|
|
12
|
+
throw StellarisError.validation(`expected ${N_PUBLIC_SIGNALS} public signals, got ${signals.length}`);
|
|
13
|
+
}
|
|
14
|
+
for (let i = 0; i < signals.length; i++) {
|
|
15
|
+
const value = signals[i];
|
|
16
|
+
if (value === undefined) {
|
|
17
|
+
throw StellarisError.validation(`publicSignals[${i}] is missing`);
|
|
18
|
+
}
|
|
19
|
+
assertFieldElement(value, `publicSignals[${i}]`);
|
|
20
|
+
}
|
|
21
|
+
const solventRaw = signals[SIGNAL_INDEX_SOLVENT];
|
|
22
|
+
const commitment = signals[SIGNAL_INDEX_COMMITMENT];
|
|
23
|
+
const liabilities = signals[SIGNAL_INDEX_LIABILITIES];
|
|
24
|
+
const period = signals[SIGNAL_INDEX_PERIOD];
|
|
25
|
+
if (solventRaw === undefined ||
|
|
26
|
+
commitment === undefined ||
|
|
27
|
+
liabilities === undefined ||
|
|
28
|
+
period === undefined) {
|
|
29
|
+
throw StellarisError.validation("public signals are missing required entries");
|
|
30
|
+
}
|
|
31
|
+
if (solventRaw !== "0" && solventRaw !== "1") {
|
|
32
|
+
throw StellarisError.validation("solvent public signal must be 0 or 1", { solventRaw });
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
solvent: solventRaw === "1",
|
|
36
|
+
commitment,
|
|
37
|
+
liabilities: BigInt(liabilities),
|
|
38
|
+
periodId: BigInt(period),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function encodePublicSignals(signals) {
|
|
42
|
+
if (signals.liabilities < 0n) {
|
|
43
|
+
throw StellarisError.validation("liabilities must be non-negative");
|
|
44
|
+
}
|
|
45
|
+
if (signals.periodId < 0n) {
|
|
46
|
+
throw StellarisError.validation("periodId must be non-negative");
|
|
47
|
+
}
|
|
48
|
+
assertFieldElement(signals.commitment, "commitment");
|
|
49
|
+
return [
|
|
50
|
+
signals.solvent ? "1" : "0",
|
|
51
|
+
signals.commitment,
|
|
52
|
+
signals.liabilities.toString(),
|
|
53
|
+
signals.periodId.toString(),
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse the v2 public-signal layout, mirroring the on-chain
|
|
58
|
+
* `parse_public_signals_v2` (contracts/stellaris/src/signals.rs):
|
|
59
|
+
* [ solvent, reserveCommitment, liabRoot, liabTotal, period ]
|
|
60
|
+
*
|
|
61
|
+
* `reserveCommitment` and `liabRoot` are full-width field elements (hashes) kept
|
|
62
|
+
* as decimal strings; `liabTotal` is the SNARK-proven total liabilities.
|
|
63
|
+
*/
|
|
64
|
+
export function parsePublicSignalsV2(signals) {
|
|
65
|
+
if (signals.length !== N_PUBLIC_SIGNALS_V2) {
|
|
66
|
+
throw StellarisError.validation(`expected ${N_PUBLIC_SIGNALS_V2} v2 public signals, got ${signals.length}`);
|
|
67
|
+
}
|
|
68
|
+
for (let i = 0; i < signals.length; i++) {
|
|
69
|
+
const value = signals[i];
|
|
70
|
+
if (value === undefined) {
|
|
71
|
+
throw StellarisError.validation(`publicSignals[${i}] is missing`);
|
|
72
|
+
}
|
|
73
|
+
assertFieldElement(value, `publicSignals[${i}]`);
|
|
74
|
+
}
|
|
75
|
+
const solventRaw = signals[SIGNAL_INDEX_V2_SOLVENT];
|
|
76
|
+
const reserveCommitment = signals[SIGNAL_INDEX_V2_RESERVE_COMMITMENT];
|
|
77
|
+
const liabRoot = signals[SIGNAL_INDEX_V2_LIAB_ROOT];
|
|
78
|
+
const liabTotal = signals[SIGNAL_INDEX_V2_LIAB_TOTAL];
|
|
79
|
+
const period = signals[SIGNAL_INDEX_V2_PERIOD];
|
|
80
|
+
if (solventRaw === undefined ||
|
|
81
|
+
reserveCommitment === undefined ||
|
|
82
|
+
liabRoot === undefined ||
|
|
83
|
+
liabTotal === undefined ||
|
|
84
|
+
period === undefined) {
|
|
85
|
+
throw StellarisError.validation("v2 public signals are missing required entries");
|
|
86
|
+
}
|
|
87
|
+
if (solventRaw !== "0" && solventRaw !== "1") {
|
|
88
|
+
throw StellarisError.validation("solvent public signal must be 0 or 1", { solventRaw });
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
solvent: solventRaw === "1",
|
|
92
|
+
reserveCommitment,
|
|
93
|
+
liabRoot,
|
|
94
|
+
liabTotal: BigInt(liabTotal),
|
|
95
|
+
periodId: BigInt(period),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function encodePublicSignalsV2(signals) {
|
|
99
|
+
if (signals.liabTotal < 0n) {
|
|
100
|
+
throw StellarisError.validation("liabTotal must be non-negative");
|
|
101
|
+
}
|
|
102
|
+
if (signals.periodId < 0n) {
|
|
103
|
+
throw StellarisError.validation("periodId must be non-negative");
|
|
104
|
+
}
|
|
105
|
+
assertFieldElement(signals.reserveCommitment, "reserveCommitment");
|
|
106
|
+
assertFieldElement(signals.liabRoot, "liabRoot");
|
|
107
|
+
return [
|
|
108
|
+
signals.solvent ? "1" : "0",
|
|
109
|
+
signals.reserveCommitment,
|
|
110
|
+
signals.liabRoot,
|
|
111
|
+
signals.liabTotal.toString(),
|
|
112
|
+
signals.periodId.toString(),
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Parse the v3 multi-asset public-signal layout, mirroring the on-chain
|
|
117
|
+
* `parse_public_signals_v3` (contracts/stellaris/src/signals.rs):
|
|
118
|
+
* [ aggregateSolvent, reserveCommitment, priceCommitment,
|
|
119
|
+
* assetSolvent[0..N_ASSETS_V3-1], period ]
|
|
120
|
+
*
|
|
121
|
+
* `reserveCommitment`/`priceCommitment` are full-width field elements kept as
|
|
122
|
+
* decimal strings; the aggregate flag and each per-asset flag must be 0 or 1.
|
|
123
|
+
*/
|
|
124
|
+
export function parsePublicSignalsV3(signals) {
|
|
125
|
+
if (signals.length !== N_PUBLIC_SIGNALS_V3) {
|
|
126
|
+
throw StellarisError.validation(`expected ${N_PUBLIC_SIGNALS_V3} v3 public signals, got ${signals.length}`);
|
|
127
|
+
}
|
|
128
|
+
for (let i = 0; i < signals.length; i++) {
|
|
129
|
+
const value = signals[i];
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
throw StellarisError.validation(`publicSignals[${i}] is missing`);
|
|
132
|
+
}
|
|
133
|
+
assertFieldElement(value, `publicSignals[${i}]`);
|
|
134
|
+
}
|
|
135
|
+
const parseFlag = (value, label) => {
|
|
136
|
+
if (value === "1")
|
|
137
|
+
return true;
|
|
138
|
+
if (value === "0")
|
|
139
|
+
return false;
|
|
140
|
+
throw StellarisError.validation(`${label} must be 0 or 1`, { value });
|
|
141
|
+
};
|
|
142
|
+
const aggregateRaw = signals[SIGNAL_INDEX_V3_AGGREGATE_SOLVENT];
|
|
143
|
+
const reserveCommitment = signals[SIGNAL_INDEX_V3_RESERVE_COMMITMENT];
|
|
144
|
+
const priceCommitment = signals[SIGNAL_INDEX_V3_PRICE_COMMITMENT];
|
|
145
|
+
const period = signals[SIGNAL_INDEX_V3_PERIOD];
|
|
146
|
+
if (aggregateRaw === undefined ||
|
|
147
|
+
reserveCommitment === undefined ||
|
|
148
|
+
priceCommitment === undefined ||
|
|
149
|
+
period === undefined) {
|
|
150
|
+
throw StellarisError.validation("v3 public signals are missing required entries");
|
|
151
|
+
}
|
|
152
|
+
const assetSolvent = [];
|
|
153
|
+
for (let a = 0; a < N_ASSETS_V3; a++) {
|
|
154
|
+
const flag = signals[SIGNAL_INDEX_V3_ASSET_SOLVENT_BASE + a];
|
|
155
|
+
if (flag === undefined) {
|
|
156
|
+
throw StellarisError.validation(`assetSolvent[${a}] is missing`);
|
|
157
|
+
}
|
|
158
|
+
assetSolvent.push(parseFlag(flag, `assetSolvent[${a}]`));
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
aggregateSolvent: parseFlag(aggregateRaw, "aggregateSolvent"),
|
|
162
|
+
reserveCommitment,
|
|
163
|
+
priceCommitment,
|
|
164
|
+
assetSolvent,
|
|
165
|
+
periodId: BigInt(period),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Encode the v3 multi-asset public-signal layout, the inverse of
|
|
170
|
+
* {@link parsePublicSignalsV3}. Produces the exact decimal-string vector the
|
|
171
|
+
* on-chain `parse_public_signals_v3` expects:
|
|
172
|
+
* [ aggregateSolvent, reserveCommitment, priceCommitment,
|
|
173
|
+
* assetSolvent[0..N_ASSETS_V3-1], period ]
|
|
174
|
+
*
|
|
175
|
+
* `assetSolvent` must contain exactly `N_ASSETS_V3` flags; a different length is
|
|
176
|
+
* a programming error and is rejected rather than silently padded/truncated.
|
|
177
|
+
*/
|
|
178
|
+
export function encodePublicSignalsV3(signals) {
|
|
179
|
+
if (signals.assetSolvent.length !== N_ASSETS_V3) {
|
|
180
|
+
throw StellarisError.validation(`assetSolvent must have exactly ${N_ASSETS_V3} entries, got ${signals.assetSolvent.length}`);
|
|
181
|
+
}
|
|
182
|
+
if (signals.periodId < 0n) {
|
|
183
|
+
throw StellarisError.validation("periodId must be non-negative");
|
|
184
|
+
}
|
|
185
|
+
assertFieldElement(signals.reserveCommitment, "reserveCommitment");
|
|
186
|
+
assertFieldElement(signals.priceCommitment, "priceCommitment");
|
|
187
|
+
return [
|
|
188
|
+
signals.aggregateSolvent ? "1" : "0",
|
|
189
|
+
signals.reserveCommitment,
|
|
190
|
+
signals.priceCommitment,
|
|
191
|
+
...signals.assetSolvent.map((flag) => (flag ? "1" : "0")),
|
|
192
|
+
signals.periodId.toString(),
|
|
193
|
+
];
|
|
194
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level Stellaris contract client.
|
|
3
|
+
*
|
|
4
|
+
* This module composes codec + transport + domain types. It does not contain
|
|
5
|
+
* UI or demo mocks. Integrators can supply a real SorobanTransport backed by
|
|
6
|
+
* @stellar/stellar-sdk, a server signer, or a test harness.
|
|
7
|
+
*/
|
|
8
|
+
import { Attestation, AttestationReceipt, AttestationV2, AttestationReceiptV2, AttestationV3, AttestationReceiptV3, ContractDeployment, FieldElement, ProofBundle, ProofBundleV2, ProofBundleV3, PublicKey, VerificationKeyDocument } from "./domain.js";
|
|
9
|
+
import { TransactionSigner, SorobanTransport, TransactionPlan } from "./transport.js";
|
|
10
|
+
export declare enum ContractErrorCode {
|
|
11
|
+
NotInitialized = 1,
|
|
12
|
+
AlreadyInitialized = 2,
|
|
13
|
+
Unauthorized = 3,
|
|
14
|
+
ProofInvalid = 4,
|
|
15
|
+
NotSolvent = 5,
|
|
16
|
+
PeriodAlreadyAttested = 6,
|
|
17
|
+
BadPublicSignals = 7,
|
|
18
|
+
BadProofEncoding = 8,
|
|
19
|
+
BadLiabilityRoot = 9,
|
|
20
|
+
WrongVerifierVersion = 10,
|
|
21
|
+
OracleMismatch = 11,
|
|
22
|
+
OracleNotConfigured = 12,
|
|
23
|
+
CustodianNotConfigured = 13,
|
|
24
|
+
CustodianSigInvalid = 14
|
|
25
|
+
}
|
|
26
|
+
export declare class ContractError extends Error {
|
|
27
|
+
readonly code: ContractErrorCode;
|
|
28
|
+
constructor(code: number);
|
|
29
|
+
}
|
|
30
|
+
export interface StellarisClientOptions {
|
|
31
|
+
readonly deployment: ContractDeployment;
|
|
32
|
+
readonly transport?: SorobanTransport;
|
|
33
|
+
}
|
|
34
|
+
export interface InitParams {
|
|
35
|
+
readonly admin: PublicKey;
|
|
36
|
+
readonly verificationKey: VerificationKeyDocument;
|
|
37
|
+
readonly signer: TransactionSigner;
|
|
38
|
+
}
|
|
39
|
+
export interface AttestParams {
|
|
40
|
+
readonly issuer: PublicKey;
|
|
41
|
+
readonly bundle: ProofBundle;
|
|
42
|
+
readonly signer: TransactionSigner;
|
|
43
|
+
}
|
|
44
|
+
export interface AttestV2Params {
|
|
45
|
+
readonly issuer: PublicKey;
|
|
46
|
+
readonly bundle: ProofBundleV2;
|
|
47
|
+
readonly signer: TransactionSigner;
|
|
48
|
+
}
|
|
49
|
+
export interface AttestV3Params {
|
|
50
|
+
readonly issuer: PublicKey;
|
|
51
|
+
readonly bundle: ProofBundleV3;
|
|
52
|
+
readonly signer: TransactionSigner;
|
|
53
|
+
}
|
|
54
|
+
export declare class StellarisClient {
|
|
55
|
+
readonly deployment: ContractDeployment;
|
|
56
|
+
private readonly transport;
|
|
57
|
+
constructor(options: StellarisClientOptions);
|
|
58
|
+
init(params: InitParams): Promise<void>;
|
|
59
|
+
attest(params: AttestParams): Promise<AttestationReceipt>;
|
|
60
|
+
getAttestation(issuer: PublicKey, periodId: bigint): Promise<Attestation | null>;
|
|
61
|
+
/**
|
|
62
|
+
* v2 attest: solvency with a SNARK-proven liability total. The proof bundle's
|
|
63
|
+
* public signals carry the circuit-computed `liabRoot`/`liabTotal`; the
|
|
64
|
+
* contract codec serializes the raw snarkjs proof via the shared encoder.
|
|
65
|
+
*/
|
|
66
|
+
attestV2(params: AttestV2Params): Promise<AttestationReceiptV2>;
|
|
67
|
+
getAttestationV2(issuer: PublicKey, periodId: bigint): Promise<AttestationV2 | null>;
|
|
68
|
+
/**
|
|
69
|
+
* v3 attest: multi-asset solvency with an oracle-priced aggregate. The proof
|
|
70
|
+
* bundle's public signals carry the per-asset solvency flags + the aggregate
|
|
71
|
+
* flag + the reserve/price commitments; the contract requires the AGGREGATE to
|
|
72
|
+
* be solvent and stores the per-asset breakdown for transparency.
|
|
73
|
+
*/
|
|
74
|
+
attestV3(params: AttestV3Params): Promise<AttestationReceiptV3>;
|
|
75
|
+
getAttestationV3(issuer: PublicKey, periodId: bigint): Promise<AttestationV3 | null>;
|
|
76
|
+
listPeriods(issuer: PublicKey): Promise<readonly bigint[]>;
|
|
77
|
+
getVerificationKey(): Promise<VerificationKeyDocument | null>;
|
|
78
|
+
getAdmin(): Promise<PublicKey | null>;
|
|
79
|
+
/**
|
|
80
|
+
* Designate the price-oracle authority (admin-gated on-chain). `admin` must be
|
|
81
|
+
* the contract admin and must sign.
|
|
82
|
+
*/
|
|
83
|
+
setOracle(params: {
|
|
84
|
+
readonly admin: PublicKey;
|
|
85
|
+
readonly oracle: PublicKey;
|
|
86
|
+
readonly signer: TransactionSigner;
|
|
87
|
+
}): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Publish the authoritative price commitment for a period (oracle-gated
|
|
90
|
+
* on-chain). `oracle` must be the designated oracle and must sign. A later
|
|
91
|
+
* `attestV3` for the same period must present a matching `priceCommitment` or
|
|
92
|
+
* be rejected with `OracleMismatch`.
|
|
93
|
+
*/
|
|
94
|
+
publishOracleCommitment(params: {
|
|
95
|
+
readonly oracle: PublicKey;
|
|
96
|
+
readonly periodId: bigint;
|
|
97
|
+
readonly commitment: FieldElement;
|
|
98
|
+
readonly signer: TransactionSigner;
|
|
99
|
+
}): Promise<void>;
|
|
100
|
+
getOracle(): Promise<PublicKey | null>;
|
|
101
|
+
getOracleCommitment(periodId: bigint): Promise<FieldElement | null>;
|
|
102
|
+
/**
|
|
103
|
+
* Designate the custodian BLS12-381 public key (G2), admin-gated on-chain.
|
|
104
|
+
* `pk` is the serialized G2 point (the transport/codec encodes it); `admin`
|
|
105
|
+
* must be the contract admin and must sign.
|
|
106
|
+
*/
|
|
107
|
+
setCustodian(params: {
|
|
108
|
+
readonly admin: PublicKey;
|
|
109
|
+
readonly custodianPublicKey: unknown;
|
|
110
|
+
readonly signer: TransactionSigner;
|
|
111
|
+
}): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Attest multi-asset solvency WITH a custodian BLS signature over the
|
|
114
|
+
* reserveCommitment. Like `attestV3` but additionally requires (and the
|
|
115
|
+
* contract verifies on-chain) a real BLS12-381 signature from the designated
|
|
116
|
+
* custodian; on success the attestation is stamped `custodianBound=true`.
|
|
117
|
+
* `custodianSig` is the serialized G1 signature point.
|
|
118
|
+
*/
|
|
119
|
+
attestV3Signed(params: {
|
|
120
|
+
readonly issuer: PublicKey;
|
|
121
|
+
readonly bundle: ProofBundleV3;
|
|
122
|
+
readonly custodianSig: unknown;
|
|
123
|
+
readonly signer: TransactionSigner;
|
|
124
|
+
}): Promise<AttestationReceiptV3>;
|
|
125
|
+
getCustodian(): Promise<string | null>;
|
|
126
|
+
plan(operation: TransactionPlan["operation"], args: readonly unknown[]): TransactionPlan;
|
|
127
|
+
private assertSigner;
|
|
128
|
+
}
|
|
129
|
+
export declare function createStellarisClient(options: StellarisClientOptions): StellarisClient;
|