@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.
@@ -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[];
@@ -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;