@loopprotocol/sdk-byoaa 0.1.0-alpha.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,1046 @@
1
+ import { PublicKey, Commitment, ConnectionConfig, Connection, TransactionSignature, AccountInfo } from '@solana/web3.js';
2
+
3
+ /**
4
+ * Core BYOAA types + helpers.
5
+ *
6
+ * Spec reference: docs/roadmap/08-byoaa-sdk.md § "SDK surface"
7
+ *
8
+ * `BankReceipt` is the in-memory shape an attested agent constructs from a
9
+ * single bank transaction. Helpers normalize the fields so the same
10
+ * underlying transaction collides on the same on-chain PDA across re-fetches.
11
+ */
12
+ /** Receipt payload carried by an attested receipt proof. */
13
+ type ReceiptPayload = BankReceipt;
14
+ /** Stable proof discriminants for the BYOAA receipt wire contract. */
15
+ type ReceiptProofType = "cose_sign1_x509" | "risc0_v1" | "groth16_alt_bn128";
16
+ /** Today's implemented proof arm: COSE_Sign1 signature plus X.509 chain. */
17
+ interface CoseSign1X509Proof {
18
+ proof_type: "cose_sign1_x509";
19
+ signature: Uint8Array;
20
+ cert_chain: Uint8Array[];
21
+ }
22
+ /** Reserved v1 ZK arm. The verifier dispatch is present; implementation throws today. */
23
+ interface Risc0V1Proof {
24
+ proof_type: "risc0_v1";
25
+ receipt: Uint8Array;
26
+ image_id: Uint8Array;
27
+ }
28
+ /** Reserved compact on-chain proof arm. The verifier dispatch is present; implementation throws today. */
29
+ interface Groth16AltBn128Proof {
30
+ proof_type: "groth16_alt_bn128";
31
+ proof: Uint8Array;
32
+ public_inputs: Uint8Array;
33
+ }
34
+ /**
35
+ * Wire shape for receipts whose payload proof may evolve without retargeting
36
+ * the published SDK contract. New proof systems append new proof_type arms;
37
+ * existing arms must not change meaning.
38
+ */
39
+ interface AttestedReceipt {
40
+ payload: ReceiptPayload;
41
+ proof: CoseSign1X509Proof | Risc0V1Proof | Groth16AltBn128Proof;
42
+ }
43
+ /**
44
+ * One bank transaction, as constructed inside an attested agent and submitted
45
+ * to `loop-shopping::submit_attested_receipt`.
46
+ *
47
+ * All fields except `mcc` and `accountLast4` are required. The on-chain
48
+ * handler will reject submissions where `amountCents == 0`, where
49
+ * `merchantNameRaw` is non-ASCII or > 64 bytes, or where `postedAt` is
50
+ * outside the [now - 365d, now] window.
51
+ */
52
+ interface BankReceipt {
53
+ /**
54
+ * 32-byte stable hash of the transaction. Derived from the deterministic
55
+ * normalize-then-hash recipe in `deriveBankTxnId()`. The PDA seeds
56
+ * `[b"attested_receipt", vault, bank_txn_id]` mean two submissions of the
57
+ * same underlying transaction collide on the same account, so the second
58
+ * `init` call fails with `AccountAlreadyInUse` — natural double-submit
59
+ * protection without per-vault dedupe state.
60
+ */
61
+ bankTxnId: Uint8Array;
62
+ /**
63
+ * Merchant name as it appeared on the bank statement, normalized via
64
+ * `normalizeMerchantName()` (uppercase, whitespace-collapsed). Must be
65
+ * ASCII and ≤ 64 bytes. The on-chain account stores this raw; off-chain
66
+ * matchers normalize further when joining to merchant claim names.
67
+ */
68
+ merchantNameRaw: string;
69
+ /** MCC (merchant category code) if the bank exposed it. 0 = unknown. */
70
+ mcc: number;
71
+ /** Transaction amount in cents. Must be > 0; capped server-side. */
72
+ amountCents: bigint;
73
+ /**
74
+ * ISO-8601 posted date as Unix seconds at midnight UTC. The on-chain
75
+ * handler enforces a [now - 365d, now] window.
76
+ */
77
+ postedAt: bigint;
78
+ /**
79
+ * Last 4 of the funding card/account, if the bank exposed it. 0 = unknown.
80
+ * Used by the off-chain matcher to disambiguate a user's connected
81
+ * accounts when there are multiple.
82
+ */
83
+ accountLast4: number;
84
+ }
85
+ /**
86
+ * Maximum receipt amount in cents enforced by the on-chain handler. Mirrors
87
+ * `MAX_RECEIPT_CENTS` in `programs/loop-shopping/src/lib.rs`. Clients may
88
+ * front-run this check to surface a clearer error.
89
+ */
90
+ declare const MAX_RECEIPT_CENTS: bigint;
91
+ /**
92
+ * Maximum merchant_name_raw length in bytes enforced on-chain. Mirrors
93
+ * `MAX_MERCHANT_NAME_RAW_LEN`.
94
+ */
95
+ declare const MAX_MERCHANT_NAME_RAW_LEN = 64;
96
+ /**
97
+ * Maximum age of a receipt's `postedAt` enforced on-chain (365 days).
98
+ */
99
+ declare const MAX_RECEIPT_AGE_SECONDS: bigint;
100
+ /**
101
+ * Normalize a bank-statement merchant name to the on-chain canonical form:
102
+ *
103
+ * 1. Trim leading/trailing whitespace.
104
+ * 2. Collapse internal whitespace runs to a single space.
105
+ * 3. Uppercase.
106
+ * 4. Strip any non-ASCII characters (accents, currency symbols, etc.).
107
+ *
108
+ * The result is what the on-chain handler stores and what off-chain
109
+ * matchers compare against `MerchantClaimableNames` entries.
110
+ *
111
+ * @example
112
+ * normalizeMerchantName(" starbucks #12345 SEATTLE WA ")
113
+ * // → "STARBUCKS #12345 SEATTLE WA"
114
+ *
115
+ * normalizeMerchantName("Café Du Monde — New Orleans")
116
+ * // → "CAF DU MONDE NEW ORLEANS" (accents + em-dash stripped; the
117
+ * // whitespace they left collapses)
118
+ */
119
+ declare function normalizeMerchantName(raw: string): string;
120
+ /**
121
+ * Derive a stable 32-byte transaction id from the normalized fields of a
122
+ * bank transaction. The same underlying transaction across re-fetches MUST
123
+ * produce the same id, so:
124
+ *
125
+ * - merchant name is run through `normalizeMerchantName` first
126
+ * - amount is taken in cents (no float rounding)
127
+ * - posted date is taken at midnight UTC seconds (caller is responsible
128
+ * for snapping; we don't truncate here)
129
+ * - account_last4 is included so two different cards at the same merchant
130
+ * for the same amount on the same day produce different ids
131
+ *
132
+ * Hashing is sha256. Output is exactly 32 bytes.
133
+ */
134
+ declare function deriveBankTxnId(input: {
135
+ merchantNameRaw: string;
136
+ amountCents: bigint;
137
+ postedAt: bigint;
138
+ accountLast4: number;
139
+ }): Uint8Array;
140
+ /**
141
+ * Validation result returned from `validateReceipt()`. `ok: true` means the
142
+ * receipt should pass the on-chain handler's pre-flight checks; `ok: false`
143
+ * surfaces the specific error so callers can present a clear message before
144
+ * paying RPC + rent.
145
+ */
146
+ type ReceiptValidationResult = {
147
+ ok: true;
148
+ } | {
149
+ ok: false;
150
+ error: ReceiptValidationError;
151
+ };
152
+ type ReceiptValidationError = "merchant_name_empty" | "merchant_name_too_long" | "merchant_name_not_ascii" | "amount_zero" | "amount_too_large" | "posted_at_in_future" | "posted_at_too_old" | "bank_txn_id_wrong_length";
153
+ /**
154
+ * Validate a receipt against the same bounds the on-chain handler enforces.
155
+ * Cheap, no I/O. Run this before `AttestedReceiptSubmitter.submit()` to
156
+ * fail fast without paying network fees or enclave round-trips.
157
+ *
158
+ * `nowSeconds` defaults to `Math.floor(Date.now() / 1000)`. Callers
159
+ * targeting a specific clock (e.g. cluster slot time) can override.
160
+ */
161
+ declare function validateReceipt(receipt: BankReceipt, nowSeconds?: bigint): ReceiptValidationResult;
162
+
163
+ /**
164
+ * Network-switchable configuration for the BYOAA SDK.
165
+ *
166
+ * Mirrors the pattern in `@loopprotocol/sdk` `src/network.ts` so that
167
+ * flipping a deployment between devnet and mainnet is a single config
168
+ * change, never a code change. Same hot-swap discipline as the on-chain
169
+ * programs (per-network `declare_id`, per-network
170
+ * `LOOP_VAULT_PROGRAM_ID_BYTES`).
171
+ *
172
+ * The BYOAA SDK only needs three things to talk to the chain:
173
+ * 1. The Solana RPC connection
174
+ * 2. The loop-shopping program id (carries `submit_attested_receipt`)
175
+ * 3. The loop-vault program id (used for the `session.owner` cross-program
176
+ * check; the shopping handler does the same check in Rust)
177
+ *
178
+ * Everything else is derived from these. Constructors accept either a
179
+ * `Network` literal (the convenience path) or a fully-resolved
180
+ * `LoopByoaaConfig` (the override path — RPC URL, custom program ids for
181
+ * a sandbox or a forked cluster).
182
+ */
183
+
184
+ type Network = "devnet" | "mainnet-beta" | "localnet";
185
+ /**
186
+ * Devnet program ids. Authoritative for BYOAA work today — all 6 anchor
187
+ * programs are deployed and the shopping binary on devnet contains
188
+ * `submit_attested_receipt` as of `81aa56e` (truthful 4/4 NO-SKIPS rehearsal).
189
+ *
190
+ * Pulled into this module rather than imported from the main SDK so that
191
+ * the SDK can ship without forcing a parent-SDK version match on every
192
+ * BYOAA point release. When the parent SDK rotates ids, bump here to match.
193
+ */
194
+ declare const DEVNET_PROGRAM_IDS: {
195
+ readonly SHOPPING: PublicKey;
196
+ readonly VAULT: PublicKey;
197
+ };
198
+ /**
199
+ * Mainnet program ids. Placeholder until Bundle 2 deploys (loop-shopping
200
+ * program upgrade carrying spec 08 + 08a + 08b + 09). At that point this
201
+ * map flips to the live mainnet ids and consumers re-deploy with
202
+ * `network: "mainnet-beta"`.
203
+ *
204
+ * The on-chain BYOAA surface is **not on mainnet today** — the deployed
205
+ * binary at `b407bae` covers specs 01-05 only. Don't switch consumers to
206
+ * `mainnet-beta` until the upgrade lands AND the mainnet ShoppingState
207
+ * bump=0 corruption is repaired (Burt P0 brief 2026-05-03).
208
+ */
209
+ declare const MAINNET_PROGRAM_IDS: {
210
+ readonly SHOPPING: PublicKey;
211
+ readonly VAULT: PublicKey;
212
+ };
213
+ interface LoopByoaaConfig {
214
+ /** Which Solana cluster the BYOAA SDK targets. */
215
+ network: Network;
216
+ /** RPC endpoint. Defaults to the public cluster RPC for `network`. */
217
+ rpcUrl?: string;
218
+ /**
219
+ * Override the loop-shopping program id. Useful for sandbox forks where
220
+ * you've redeployed the program at a custom address (see
221
+ * `scripts/sandbox/`).
222
+ */
223
+ shoppingProgramId?: PublicKey;
224
+ /** Override the loop-vault program id. Same use-case as above. */
225
+ vaultProgramId?: PublicKey;
226
+ /**
227
+ * Commitment level for confirmations + reads. Defaults to "confirmed".
228
+ * BYOAA receipts are fire-and-forget once confirmed; "finalized" only
229
+ * matters if you need rollback resistance against deep reorgs.
230
+ */
231
+ commitment?: Commitment;
232
+ /** Extra connection config (forwarded to web3.js Connection). */
233
+ connectionConfig?: ConnectionConfig;
234
+ }
235
+ /**
236
+ * Resolved internal shape — everything is a concrete value, never undefined.
237
+ * Internal modules use this; public APIs accept the looser
238
+ * `LoopByoaaConfig` and resolve via `resolveConfig`.
239
+ */
240
+ interface ResolvedConfig {
241
+ network: Network;
242
+ connection: Connection;
243
+ shoppingProgramId: PublicKey;
244
+ vaultProgramId: PublicKey;
245
+ }
246
+ /**
247
+ * Resolve a public `LoopByoaaConfig` (or just a network literal) into a
248
+ * fully-populated internal config. Pure: no I/O, no side effects.
249
+ */
250
+ declare function resolveConfig(input: LoopByoaaConfig | Network): ResolvedConfig;
251
+ /**
252
+ * Look up the canonical program ids for a given network. Localnet reuses
253
+ * the devnet ids by convention — sandbox forks should override via
254
+ * `LoopByoaaConfig.shoppingProgramId` / `vaultProgramId` instead of
255
+ * adding a new network case.
256
+ */
257
+ declare function getProgramIds(network: Network): {
258
+ readonly SHOPPING: PublicKey;
259
+ readonly VAULT: PublicKey;
260
+ };
261
+ /** Default public RPC for each cluster. */
262
+ declare function defaultRpcUrl(network: Network): string;
263
+ /**
264
+ * True iff this network is mainnet. Used by callers to gate "real-money"
265
+ * UX (extra confirmation modals, larger fonts on amounts, etc.).
266
+ */
267
+ declare function isMainnet(network: Network): boolean;
268
+
269
+ /**
270
+ * PDA derivations for BYOAA accounts.
271
+ *
272
+ * Authoritative seeds live in `programs/loop-shopping/src/lib.rs` next to
273
+ * the `BankAttestedReceipt` struct. Mirror them here exactly — drift would
274
+ * silently corrupt receipt addresses.
275
+ */
276
+
277
+ /**
278
+ * `BankAttestedReceipt` PDA.
279
+ *
280
+ * Seeds: `[b"attested_receipt", vault.key().as_ref(), bank_txn_id.as_ref()]`
281
+ *
282
+ * Per-vault-per-txn uniqueness — same bank transaction can't be submitted
283
+ * twice for the same user (the second `init` collides with the first
284
+ * account and fails).
285
+ *
286
+ * @returns the receipt PDA + bump seed
287
+ */
288
+ declare function deriveBankAttestedReceiptPda(args: {
289
+ shoppingProgramId: PublicKey;
290
+ vault: PublicKey;
291
+ bankTxnId: Uint8Array;
292
+ }): {
293
+ pda: PublicKey;
294
+ bump: number;
295
+ };
296
+ /**
297
+ * `ShoppingState` PDA (loop-shopping).
298
+ *
299
+ * Seeds: `[b"state"]`
300
+ *
301
+ * Global state for the loop-shopping program. `submit_attested_receipt`
302
+ * takes this as a read-only input to gate on `is_paused`.
303
+ */
304
+ declare function deriveShoppingStatePda(args: {
305
+ shoppingProgramId: PublicKey;
306
+ }): {
307
+ pda: PublicKey;
308
+ bump: number;
309
+ };
310
+ /**
311
+ * `AgentSessionRegistration` PDA (loop-vault).
312
+ *
313
+ * Seeds: `[b"agent_session", vault.key().as_ref(), session_pubkey.as_ref()]`
314
+ *
315
+ * Mirrored from spec 07a so SDK consumers can resolve the session account
316
+ * without a separate import from `@loopprotocol/sdk`.
317
+ */
318
+ declare function deriveAgentSessionPda(args: {
319
+ vaultProgramId: PublicKey;
320
+ vault: PublicKey;
321
+ sessionPubkey: PublicKey;
322
+ }): {
323
+ pda: PublicKey;
324
+ bump: number;
325
+ };
326
+
327
+ /**
328
+ * Hand-rolled Anchor wire encoding for the BYOAA instructions.
329
+ *
330
+ * We intentionally do NOT depend on the loop-shopping IDL artifact:
331
+ *
332
+ * 1. The IDL goes stale every time the on-chain program upgrades, and
333
+ * sdk-byoaa needs to ship + npm-publish independently of the
334
+ * anchor-build cycle.
335
+ * 2. Anchor's BorshCoder pulls in @coral-xyz/anchor as a hard dep with a
336
+ * large transitive footprint. sdk-byoaa stays lean.
337
+ * 3. The SDK only constructs ONE instruction (`submit_attested_receipt`).
338
+ * Hand-rolling that one is ~30 lines of Borsh; cheaper than the IDL
339
+ * machinery.
340
+ *
341
+ * This module covers:
342
+ * - Anchor's instruction discriminator (`sha256("global:<name>")[0..8]`)
343
+ * - Borsh primitive writers (u16, u64, i64, [u8; N], String)
344
+ * - The `AttestedReceiptArgs` struct encoder
345
+ *
346
+ * Reference for the on-chain shape:
347
+ * programs/loop-shopping/src/lib.rs
348
+ * - `pub fn submit_attested_receipt(ctx, args: AttestedReceiptArgs)`
349
+ * - `pub struct AttestedReceiptArgs { bank_txn_id, merchant_name_raw,
350
+ * mcc, amount_cents, posted_at, account_last4 }`
351
+ */
352
+ /**
353
+ * Compute the 8-byte Anchor instruction discriminator.
354
+ * Convention: `sha256("global:" + snake_case_name)[0..8]`.
355
+ */
356
+ declare function instructionDiscriminator(snakeName: string): Uint8Array;
357
+ /**
358
+ * Borsh-encode the `AttestedReceiptArgs` struct exactly as the on-chain
359
+ * handler will deserialize it. Field order is load-bearing.
360
+ *
361
+ * pub struct AttestedReceiptArgs {
362
+ * pub bank_txn_id: [u8; 32], // raw 32 bytes
363
+ * pub merchant_name_raw: String, // u32 LE length + UTF-8 bytes
364
+ * pub mcc: u16, // 2 bytes LE
365
+ * pub amount_cents: u64, // 8 bytes LE
366
+ * pub posted_at: i64, // 8 bytes LE (two's complement)
367
+ * pub account_last4: u16, // 2 bytes LE
368
+ * }
369
+ */
370
+ declare function encodeAttestedReceiptArgs(args: {
371
+ bankTxnId: Uint8Array;
372
+ merchantNameRaw: string;
373
+ mcc: number;
374
+ amountCents: bigint;
375
+ postedAt: bigint;
376
+ accountLast4: number;
377
+ }): Uint8Array;
378
+ /**
379
+ * Build the full instruction payload (discriminator || args) for
380
+ * `submit_attested_receipt`. This is what goes into
381
+ * `TransactionInstruction.data`.
382
+ */
383
+ declare function encodeSubmitAttestedReceiptIxData(args: {
384
+ bankTxnId: Uint8Array;
385
+ merchantNameRaw: string;
386
+ mcc: number;
387
+ amountCents: bigint;
388
+ postedAt: bigint;
389
+ accountLast4: number;
390
+ }): Uint8Array;
391
+
392
+ /**
393
+ * AttestedReceiptSubmitter — sign + submit `submit_attested_receipt` txs.
394
+ *
395
+ * Spec 08 § "Enclave-side API". This class is what an attested agent
396
+ * (running inside an AWS Nitro / GCP Confidential Space / Anthropic
397
+ * Claude Computer Use enclave) uses to publish bank receipts on-chain.
398
+ *
399
+ * Trust boundary:
400
+ * - The session **secret** lives only in the enclave; this class never
401
+ * holds it. Signing is delegated to an `EnclaveSigner` (typically
402
+ * the `EnclaveClient` from `@loopprotocol/sdk`, which talks to the
403
+ * enclave via vsock or an HTTPS broker).
404
+ * - This class assembles transactions, derives PDAs, and shepherds
405
+ * network I/O. It can run anywhere — host, browser, an unattested
406
+ * proxy — without weakening the security model.
407
+ */
408
+
409
+ /**
410
+ * Bit position of `SUBMIT_ATTESTED_RECEIPT` in the
411
+ * `AgentSessionRegistration::allowed_instructions` bitfield.
412
+ *
413
+ * Mirrors `loop-shopping::SESSION_BIT_SUBMIT_ATTESTED_RECEIPT = 1 << 5`.
414
+ * `loop-vault` writes the numeric bitmap during session registration; bit 5
415
+ * is reserved for shopping attested receipts and must not be retargeted.
416
+ * Drift here = enclave's policy filter rejects valid signing requests,
417
+ * or worse, accepts wrong ones. Keep in lockstep with the on-chain const.
418
+ */
419
+ declare const SESSION_INSTRUCTION_INDEX_SUBMIT_ATTESTED_RECEIPT = 5;
420
+ /**
421
+ * Signing requests passed to the enclave. The enclave's `policy.rs`
422
+ * uses the policy fields to verify the signature request matches a
423
+ * receipt the enclave actually scraped (defense in depth against a
424
+ * compromised host trying to inject forged receipts through the
425
+ * legitimate signing channel).
426
+ */
427
+ interface PolicyInputs {
428
+ /** Amount in cents, mirrored from the receipt being signed. */
429
+ amountCents: bigint;
430
+ /** Normalized merchant name, mirrored from the receipt. */
431
+ merchantNameRaw: string;
432
+ /** Bank-side posted-at timestamp, mirrored from the receipt. */
433
+ postedAt: bigint;
434
+ /** 32-byte receipt id, mirrored from the receipt. */
435
+ bankTxnId: Uint8Array;
436
+ }
437
+ /**
438
+ * The minimal contract sdk-byoaa needs to sign a transaction message
439
+ * inside an attested enclave. Decoupled from any specific transport so
440
+ * the SDK works against:
441
+ * - The Loop-operated HTTPS broker (production)
442
+ * - A local vsock listener (developer running the EIF on-host)
443
+ * - A test double (unit tests; see tests/enclave.test.ts)
444
+ *
445
+ * `signInstruction` MUST return a 64-byte ed25519 signature over `message`
446
+ * produced by the session keypair bound to `getSessionPubkey()`.
447
+ */
448
+ interface EnclaveSigner {
449
+ /** The enclave's bound session public key (same one registered on-chain). */
450
+ getSessionPubkey(): Promise<PublicKey>;
451
+ /**
452
+ * Request the enclave to sign a transaction message. Returns the raw
453
+ * 64-byte ed25519 signature. The enclave is expected to enforce its
454
+ * own policy filter — instruction index allowlist, well-formedness,
455
+ * rate limits — before signing.
456
+ */
457
+ signInstruction(args: {
458
+ instructionIndex: number;
459
+ message: Uint8Array;
460
+ policyInputs: PolicyInputs;
461
+ }): Promise<Uint8Array>;
462
+ }
463
+ interface AttestedReceiptSubmitterOptions {
464
+ /** Network selector or fully-resolved config. Defaults to "devnet". */
465
+ network?: Network | LoopByoaaConfig;
466
+ /** Enclave signer abstraction (see `EnclaveSigner`). */
467
+ enclaveSigner: EnclaveSigner;
468
+ /** User vault that the receipts are for (must match the session's vault). */
469
+ vault: PublicKey;
470
+ /**
471
+ * The session ed25519 pubkey that was registered on-chain via
472
+ * `register_agent_session` — i.e. the public side of the keypair the
473
+ * enclave signs with. The submitter derives the on-chain
474
+ * `AgentSessionRegistration` PDA from `[b"agent_session", vault,
475
+ * sessionPubkey]` against the configured vault program; consumers
476
+ * never have to compute the PDA themselves.
477
+ */
478
+ sessionPubkey: PublicKey;
479
+ /** Confirmation level for `submit()`. Defaults to "confirmed". */
480
+ commitment?: Commitment;
481
+ }
482
+ /** Outcome of a single `submit()` call. */
483
+ interface SubmitOk {
484
+ ok: true;
485
+ signature: TransactionSignature;
486
+ receiptPda: PublicKey;
487
+ }
488
+ interface SubmitFail {
489
+ ok: false;
490
+ error: SubmitError;
491
+ /** Optional underlying error message for diagnostics. */
492
+ detail?: string;
493
+ }
494
+ type SubmitResult = SubmitOk | SubmitFail;
495
+ type SubmitError = "validation_failed" | "session_pubkey_mismatch" | "blockhash_fetch_failed" | "enclave_sign_failed" | "transaction_send_failed" | "transaction_confirm_failed";
496
+ interface SubmitBatchOptions {
497
+ /** Maximum in-flight submissions. Defaults to 4. */
498
+ concurrency?: number;
499
+ /** Minimum ms between starting consecutive submissions. Defaults to 0. */
500
+ rateLimitMs?: number;
501
+ /** Per-attempt commitment override. */
502
+ commitment?: Commitment;
503
+ /** Optional progress callback fired after each receipt resolves. */
504
+ onProgress?: (event: {
505
+ index: number;
506
+ total: number;
507
+ result: SubmitResult;
508
+ }) => void;
509
+ }
510
+ /**
511
+ * Sign + submit BYOAA receipts to the Loop Protocol clearing rail.
512
+ *
513
+ * Standard usage:
514
+ *
515
+ * ```ts
516
+ * const submitter = new AttestedReceiptSubmitter({
517
+ * network: "devnet",
518
+ * enclaveSigner, // talks to your Nitro enclave
519
+ * vault: userVaultPda,
520
+ * session: agentSessionPda,
521
+ * });
522
+ *
523
+ * const r = await submitter.submit(receipt);
524
+ * if (r.ok) console.log("receipt:", r.receiptPda.toBase58(), "tx:", r.signature);
525
+ * ```
526
+ *
527
+ * For batched ingestion (a daily cron pulling N transactions), use
528
+ * `submitBatch` — it bounds concurrency + applies an inter-request delay
529
+ * so RPC rate limits don't trip mid-run.
530
+ */
531
+ declare class AttestedReceiptSubmitter {
532
+ private readonly resolvedConfig;
533
+ private readonly enclaveSigner;
534
+ private readonly vault;
535
+ /**
536
+ * The on-chain `AgentSessionRegistration` PDA, derived once from
537
+ * `[b"agent_session", vault, sessionPubkey]` against the vault program.
538
+ * The on-chain handler reads it as `session_registration`.
539
+ */
540
+ private readonly sessionRegistrationPda;
541
+ private readonly commitment;
542
+ /** Cached so we don't round-trip the enclave on every submit. */
543
+ private cachedSessionPubkey;
544
+ constructor(opts: AttestedReceiptSubmitterOptions);
545
+ /** Network selector this submitter was constructed against. */
546
+ get network(): Network;
547
+ /**
548
+ * Resolve the receipt PDA for a given txn id without submitting.
549
+ * Useful for off-chain state checks ("have we already submitted X?")
550
+ * before paying RPC fees.
551
+ */
552
+ deriveReceiptPda(bankTxnId: Uint8Array): PublicKey;
553
+ /**
554
+ * Sign + submit one receipt. Returns the on-chain signature + the
555
+ * receipt's PDA on success. Failures are returned as discriminated
556
+ * unions rather than thrown — batch consumers want fail-isolation.
557
+ */
558
+ submit(receipt: BankReceipt): Promise<SubmitResult>;
559
+ /**
560
+ * Submit N receipts with bounded concurrency + optional inter-request
561
+ * spacing. Fail-isolated: one bad receipt does not poison the rest.
562
+ *
563
+ * Order of `results` matches the order of `receipts`.
564
+ */
565
+ submitBatch(receipts: BankReceipt[], opts?: SubmitBatchOptions): Promise<SubmitResult[]>;
566
+ private getSessionPubkey;
567
+ private buildSubmitIx;
568
+ }
569
+
570
+ /**
571
+ * AttestedReceiptVerifier — fetch + decode `BankAttestedReceipt` accounts.
572
+ *
573
+ * Spec 08 § "Consumer-side API". Used by:
574
+ * - Merchant intel dashboards (joining receipts to merchant claim names)
575
+ * - Off-chain indexers backfilling Supabase mirror tables
576
+ * - Forensics / audit tooling cross-checking PCR0 against the
577
+ * EnclaveImageRegistry to flag unaudited submissions
578
+ *
579
+ * This module talks to chain via the same `ResolvedConfig` the submitter
580
+ * uses, so verification is network-switchable in lockstep — devnet today,
581
+ * mainnet the moment Bundle 2 deploys.
582
+ *
583
+ * Hand-rolled decoder (no Anchor IDL dep) so the package ships
584
+ * independently of the protocol's anchor-build cycle. The decoder mirrors
585
+ * the on-chain struct field order in `programs/loop-shopping/src/lib.rs`
586
+ * (`BankAttestedReceipt`) and `programs/loop-vault/src/lib.rs`
587
+ * (`EnclaveImageRegistry`).
588
+ */
589
+
590
+ type ReceiptStatus = "recorded" | "honored" | "invalidated";
591
+ interface VerifiedReceipt extends BankReceipt {
592
+ /** Address of the on-chain `BankAttestedReceipt` PDA. */
593
+ pda: PublicKey;
594
+ /** Owner vault — should match the user's vault you queried for. */
595
+ vault: PublicKey;
596
+ /** The enclave session pubkey that signed the submission. */
597
+ sessionPubkey: PublicKey;
598
+ /** PCR0 of the enclave image that produced the receipt. */
599
+ pcr0: Uint8Array;
600
+ /** Block timestamp at submission (Solana clock). */
601
+ submittedAt: bigint;
602
+ /**
603
+ * Lifecycle:
604
+ * - "recorded" — submitted, not yet honored
605
+ * - "honored" — a merchant claimed + paid via spec 08a
606
+ * - "invalidated" — admin-revoked (fraud signal)
607
+ */
608
+ status: ReceiptStatus;
609
+ /**
610
+ * `true` iff this receipt's `pcr0` matches a currently-approved entry in
611
+ * the on-chain `EnclaveImageRegistry`. Always populated by `fetch()` —
612
+ * pass `requireAuditedImage: true` to throw when false.
613
+ *
614
+ * `null` when the verifier was constructed with `skipImageAudit: true`
615
+ * (lighter-weight; useful when the caller already knows audit state).
616
+ */
617
+ imageAudited: boolean | null;
618
+ }
619
+ interface AttestedReceiptVerifierOptions {
620
+ network?: Network | LoopByoaaConfig;
621
+ /**
622
+ * Cache the EnclaveImageRegistry decode for this many ms between
623
+ * fetches. Defaults to 60s — short enough to pick up admin updates
624
+ * within a minute, long enough that bulk verification doesn't
625
+ * hammer the RPC.
626
+ */
627
+ registryCacheMs?: number;
628
+ /**
629
+ * Skip the EnclaveImageRegistry cross-check entirely. `imageAudited`
630
+ * on returned receipts will be `null`. Use only when you don't care
631
+ * about audit state and want to save an RPC round-trip.
632
+ */
633
+ skipImageAudit?: boolean;
634
+ }
635
+ interface FetchOptions {
636
+ /**
637
+ * If `true`, throw `ReceiptNotAuditedError` when the receipt's PCR0
638
+ * doesn't match a current `EnclaveImageRegistry` entry. Default `false`
639
+ * — the field is populated and the caller decides.
640
+ */
641
+ requireAuditedImage?: boolean;
642
+ }
643
+ declare class ReceiptNotAuditedError extends Error {
644
+ readonly receipt: VerifiedReceipt;
645
+ constructor(receipt: VerifiedReceipt);
646
+ }
647
+ declare class ReceiptNotFoundError extends Error {
648
+ readonly pda: PublicKey;
649
+ constructor(pda: PublicKey);
650
+ }
651
+ declare class UnsupportedProofTypeError extends Error {
652
+ readonly proofType: ReceiptProofType;
653
+ constructor(proofType: ReceiptProofType);
654
+ }
655
+ type AttestedReceiptProofVerification = {
656
+ ok: true;
657
+ proof_type: "cose_sign1_x509";
658
+ };
659
+ /**
660
+ * Dispatch verifier for the BYOAA attested-receipt proof wire contract.
661
+ *
662
+ * Today only the COSE+X.509 arm is accepted. Reserved ZK arms are part of the
663
+ * wire shape now so future SDKs can implement them without retargeting the
664
+ * published contract; until then they fail closed with UnsupportedProofTypeError.
665
+ */
666
+ declare function verifyAttestedReceiptProof(receipt: AttestedReceipt): AttestedReceiptProofVerification;
667
+ declare class AttestedReceiptVerifier {
668
+ private readonly resolvedConfig;
669
+ private readonly registryCacheMs;
670
+ private readonly skipImageAudit;
671
+ /** Cache of approved PCR0s. */
672
+ private approvedPcr0s;
673
+ private approvedPcr0sExpiresAt;
674
+ constructor(opts?: AttestedReceiptVerifierOptions);
675
+ get network(): Network;
676
+ /**
677
+ * Fetch one receipt by PDA. Throws `ReceiptNotFoundError` if the
678
+ * account is missing or `ReceiptNotAuditedError` when
679
+ * `requireAuditedImage` is set and the PCR0 doesn't match the registry.
680
+ */
681
+ fetch(pda: PublicKey, opts?: FetchOptions): Promise<VerifiedReceipt>;
682
+ /**
683
+ * Stream every receipt for a given vault. Uses `getProgramAccounts`
684
+ * with a memcmp filter on the `vault` field at fixed offset 8 (right
685
+ * after the 8-byte Anchor discriminator).
686
+ *
687
+ * Order is undefined — sort client-side if you need deterministic
688
+ * ordering (e.g. by `submittedAt` or `postedAt`).
689
+ */
690
+ forVault(vault: PublicKey): AsyncIterable<VerifiedReceipt>;
691
+ /**
692
+ * Stream receipts whose `merchantNameRaw` (after `normalizeMerchantName`)
693
+ * matches the given normalized name.
694
+ *
695
+ * **Implementation note:** `merchant_name_raw` lives at variable byte
696
+ * offset (it follows a 4-byte length prefix that varies per receipt),
697
+ * so memcmp filtering on-chain is impractical. This method therefore
698
+ * fetches every `BankAttestedReceipt` account from the program and
699
+ * filters client-side. **Use the off-chain `attested_receipt_index`
700
+ * Supabase mirror (loop-site-v2 PR #27) for production-scale
701
+ * merchant queries.** This SDK method is fine for low-volume audits
702
+ * + correctness testing.
703
+ */
704
+ forMerchant(merchantNameNormalized: string): AsyncIterable<VerifiedReceipt>;
705
+ /**
706
+ * Force a refresh of the EnclaveImageRegistry cache. Useful in long-
707
+ * running daemons that want to pick up admin updates immediately.
708
+ */
709
+ refreshImageRegistry(): Promise<void>;
710
+ private auditPcr0;
711
+ private loadApprovedPcr0s;
712
+ }
713
+ /**
714
+ * Decode a `BankAttestedReceipt` account.
715
+ *
716
+ * On-chain layout (per programs/loop-shopping/src/lib.rs):
717
+ * 0..8 disc 8 bytes
718
+ * 8..40 vault Pubkey (32)
719
+ * 40..72 session_pubkey Pubkey (32)
720
+ * 72..120 pcr0 [u8; 48]
721
+ * 120..152 bank_txn_id [u8; 32]
722
+ * 152..156 merchant_name length u32 LE
723
+ * 156.. merchant_name bytes N bytes (variable)
724
+ * then mcc u16 LE
725
+ * amount_cents u64 LE
726
+ * posted_at i64 LE
727
+ * account_last4 u16 LE
728
+ * submitted_at i64 LE
729
+ * status u8
730
+ * bump u8
731
+ *
732
+ * Anchor's `#[max_len(64)]` allocates space for the maximum but the
733
+ * serialized form uses the actual length, so we read sequentially.
734
+ */
735
+ declare function decodeBankAttestedReceipt(pda: PublicKey, account: AccountInfo<Buffer>): VerifiedReceipt;
736
+ /**
737
+ * Decode the EnclaveImageRegistry account into a Set of approved PCR0
738
+ * hex strings.
739
+ *
740
+ * On-chain layout (per programs/loop-vault/src/lib.rs):
741
+ * 0..8 disc
742
+ * 8..40 admin Pubkey
743
+ * 40..44 approved_images vec len u32 LE
744
+ * then N entries of ApprovedImage:
745
+ * pcr0 [u8; 48]
746
+ * pcr1 [u8; 48]
747
+ * pcr2 [u8; 48]
748
+ * label [u8; 32]
749
+ * approved_at i64 LE
750
+ * end bump (u8)
751
+ */
752
+ declare function decodeRegistryPcr0Set(account: AccountInfo<Buffer>): Set<string>;
753
+
754
+ type LoopByoaaErrorCode = "auth" | "validation" | "rate_limit" | "quota" | "permission_denied" | "not_found" | "conflict" | "server_error" | "network_error" | "unknown";
755
+ interface LoopByoaaErrorContext {
756
+ status?: number;
757
+ request_id?: string;
758
+ documentation_url?: string;
759
+ retry_after_seconds?: number;
760
+ meter_name?: string;
761
+ upgrade_url?: string;
762
+ details?: unknown;
763
+ }
764
+ /** Base SDK error. Catch this when you want one branch for all Loop BYOAA failures. */
765
+ declare class LoopByoaaError extends Error {
766
+ readonly code: LoopByoaaErrorCode | string;
767
+ readonly status?: number;
768
+ readonly request_id?: string;
769
+ readonly documentation_url?: string;
770
+ readonly details?: unknown;
771
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
772
+ }
773
+ declare class LoopByoaaApiKeyError extends LoopByoaaError {
774
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
775
+ }
776
+ declare class LoopByoaaPermissionError extends LoopByoaaError {
777
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
778
+ }
779
+ declare class LoopByoaaRateLimitError extends LoopByoaaError {
780
+ readonly retry_after_seconds?: number;
781
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
782
+ }
783
+ declare class LoopByoaaQuotaError extends LoopByoaaError {
784
+ readonly meter_name?: string;
785
+ readonly upgrade_url?: string;
786
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
787
+ }
788
+ declare class LoopByoaaValidationError extends LoopByoaaError {
789
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
790
+ }
791
+ declare class LoopByoaaServerError extends LoopByoaaError {
792
+ constructor(code: LoopByoaaErrorCode | string, message: string, context?: LoopByoaaErrorContext);
793
+ }
794
+ declare class LoopByoaaNetworkError extends LoopByoaaError {
795
+ constructor(message: string, context?: LoopByoaaErrorContext);
796
+ }
797
+ type AgentRiskTier = "low" | "medium" | "high" | "critical";
798
+ type AgentStatus = "active" | "suspended" | "revoked" | "test" | "archived";
799
+ interface AgentAttestation {
800
+ type: string;
801
+ issuer?: string;
802
+ evidence?: Record<string, unknown>;
803
+ issued_at?: string;
804
+ }
805
+ interface AgentRegisterInput {
806
+ external_id: string;
807
+ display_name: string;
808
+ purpose: string;
809
+ risk_tier: AgentRiskTier;
810
+ attestations?: AgentAttestation[];
811
+ metadata?: Record<string, unknown>;
812
+ }
813
+ interface AgentRecord {
814
+ id: string;
815
+ external_id?: string;
816
+ agent_key?: string;
817
+ display_name: string;
818
+ purpose?: string;
819
+ risk_tier: AgentRiskTier | string;
820
+ status: AgentStatus | string;
821
+ attestations?: AgentAttestation[];
822
+ metadata?: Record<string, unknown>;
823
+ created_at: string;
824
+ updated_at?: string;
825
+ }
826
+ interface AgentListParams {
827
+ status?: AgentStatus | string;
828
+ page?: number;
829
+ limit?: number;
830
+ }
831
+ type AgentUpdateInput = Partial<Pick<AgentRegisterInput, "display_name" | "purpose" | "risk_tier" | "attestations" | "metadata">>;
832
+ interface PermissionConditions {
833
+ [key: string]: unknown;
834
+ }
835
+ interface PermissionGrantInput {
836
+ agent_id: string;
837
+ on_behalf_of_user_id: string;
838
+ layer: string;
839
+ action_class: string;
840
+ amount_cap_cents?: number;
841
+ amount_cap_period?: "day" | "week" | "month" | "total";
842
+ valid_from?: Date | string;
843
+ valid_until?: Date | string;
844
+ conditions?: PermissionConditions;
845
+ metadata?: Record<string, unknown>;
846
+ }
847
+ interface PermissionGrant {
848
+ id: string;
849
+ grant_id?: string;
850
+ agent_id: string;
851
+ on_behalf_of_user_id?: string;
852
+ layer?: string;
853
+ action_class?: string;
854
+ status: "active" | "suspended" | "revoked" | "expired" | string;
855
+ amount_cap_cents?: number;
856
+ amount_cap_period?: string;
857
+ valid_from?: string;
858
+ valid_until?: string;
859
+ conditions?: PermissionConditions;
860
+ created_at: string;
861
+ updated_at?: string;
862
+ }
863
+ interface PermissionListParams {
864
+ agent_id?: string;
865
+ user_id?: string;
866
+ status?: string;
867
+ page?: number;
868
+ limit?: number;
869
+ }
870
+ interface PermissionRevokeInput {
871
+ reason: string;
872
+ evidence?: string;
873
+ }
874
+ interface IntendedAction {
875
+ layer: string;
876
+ action_class: string;
877
+ amount_cents?: number;
878
+ target?: Record<string, unknown>;
879
+ }
880
+ interface StepUpEvidence {
881
+ type: string;
882
+ evidence_ref?: string;
883
+ payload?: Record<string, unknown>;
884
+ }
885
+ interface DecisionRequestInput {
886
+ agent_id: string;
887
+ on_behalf_of_user_id: string;
888
+ intended_action: IntendedAction;
889
+ step_up_evidence?: StepUpEvidence;
890
+ policy_mode?: "default" | "restricted" | "bounded" | "permissive";
891
+ metadata?: Record<string, unknown>;
892
+ }
893
+ interface DecisionEnvelope {
894
+ outcome: "allow" | "review" | "deny" | "review_required" | string;
895
+ envelope_id: string;
896
+ review_request_id?: string;
897
+ reason?: string;
898
+ reasons?: string[];
899
+ issued_at: string;
900
+ signature_ref?: string;
901
+ authority_bundle_ref?: string;
902
+ decision_envelope_ref?: string;
903
+ audit_ref?: string;
904
+ metadata?: Record<string, unknown>;
905
+ }
906
+ interface DecisionListParams {
907
+ agent_id?: string;
908
+ user_id?: string;
909
+ outcome?: string;
910
+ since?: Date | string;
911
+ until?: Date | string;
912
+ page?: number;
913
+ limit?: number;
914
+ }
915
+ interface AuditExportInput {
916
+ range: {
917
+ from: Date | string;
918
+ to: Date | string;
919
+ };
920
+ scope?: {
921
+ agent_ids?: string[];
922
+ include_authority_bundles?: boolean;
923
+ include_decision_envelopes?: boolean;
924
+ include_audit_events?: boolean;
925
+ };
926
+ countersign?: boolean;
927
+ format?: "zip" | "jsonl" | "json";
928
+ }
929
+ interface AuditPack {
930
+ pack_id?: string;
931
+ manifest: Record<string, unknown>;
932
+ body?: unknown;
933
+ format?: "zip" | "jsonl" | "json" | string;
934
+ signature_bundle?: unknown;
935
+ exported_at?: string;
936
+ }
937
+ interface AuditEvent {
938
+ id: string;
939
+ audit_ref: string;
940
+ decision_envelope_ref: string;
941
+ event_type: string;
942
+ payload_digest?: string;
943
+ signature_bundle?: unknown;
944
+ prev_audit_hash?: string;
945
+ this_audit_hash?: string;
946
+ nonce?: string;
947
+ created_at: string;
948
+ }
949
+ interface AuditListParams {
950
+ since?: Date | string;
951
+ until?: Date | string;
952
+ agent_id?: string;
953
+ page?: number;
954
+ limit?: number;
955
+ }
956
+ interface Paginated<T> {
957
+ items: T[];
958
+ page?: number;
959
+ limit?: number;
960
+ total?: number;
961
+ next_cursor?: string;
962
+ }
963
+ type LoopByoaaEnvironment = "dev" | "staging" | "prod";
964
+ interface ParsedLoopByoaaSdkKey {
965
+ env: LoopByoaaEnvironment;
966
+ prefix: string;
967
+ hashable: string;
968
+ }
969
+ interface LoopByoaaClientOptions {
970
+ /** Customer SDK key. Parsed at construction time and sent as bearer auth. */
971
+ apiKey: string;
972
+ /** OAR API root. Explicit overrides are kept for tests and federated tiers. */
973
+ baseUrl?: string;
974
+ timeout?: number;
975
+ fetch?: typeof fetch;
976
+ }
977
+ /**
978
+ * Parse a customer SDK key without making a network call.
979
+ *
980
+ * The suffix is intentionally restricted to 32 base62 characters for E.2 so
981
+ * malformed keys fail before the first request. Server-side key hashing can
982
+ * use `hashable` directly without the SDK needing to understand key storage.
983
+ */
984
+ declare function parseLoopByoaaSdkKey(apiKey: string): ParsedLoopByoaaSdkKey;
985
+ type HttpMethod = "GET" | "POST" | "PATCH" | "DELETE";
986
+ declare class HttpClient {
987
+ private readonly apiKey;
988
+ private readonly baseUrl;
989
+ private readonly fetchImpl;
990
+ private readonly timeout;
991
+ constructor(apiKey: string, baseUrl: string, fetchImpl: typeof fetch, timeout: number);
992
+ request<T>(method: HttpMethod, path: string, body?: unknown, query?: object): Promise<T>;
993
+ }
994
+ declare class LoopByoaaClient {
995
+ readonly apiKey: string;
996
+ readonly env: LoopByoaaEnvironment;
997
+ readonly parsedKey: ParsedLoopByoaaSdkKey;
998
+ readonly baseUrl: string;
999
+ readonly agents: AgentsNamespace;
1000
+ readonly permissions: PermissionsNamespace;
1001
+ readonly decisions: DecisionsNamespace;
1002
+ readonly audit: AuditNamespace;
1003
+ constructor(options: LoopByoaaClientOptions);
1004
+ }
1005
+ declare class AgentsNamespace {
1006
+ private readonly http;
1007
+ constructor(http: HttpClient);
1008
+ register(input: AgentRegisterInput): Promise<AgentRecord>;
1009
+ list(params?: AgentListParams): Promise<Paginated<AgentRecord>>;
1010
+ get(agentIdOrExternalId: string): Promise<AgentRecord>;
1011
+ update(id: string, input: AgentUpdateInput): Promise<AgentRecord>;
1012
+ suspend(id: string, reason: string): Promise<AgentRecord>;
1013
+ revoke(id: string, reason: string): Promise<AgentRecord>;
1014
+ }
1015
+ declare class PermissionsNamespace {
1016
+ private readonly http;
1017
+ constructor(http: HttpClient);
1018
+ grant(input: PermissionGrantInput): Promise<PermissionGrant>;
1019
+ list(params?: PermissionListParams): Promise<Paginated<PermissionGrant>>;
1020
+ get(grantId: string): Promise<PermissionGrant>;
1021
+ revoke(grantId: string, input: PermissionRevokeInput): Promise<PermissionGrant>;
1022
+ }
1023
+ declare class DecisionsNamespace {
1024
+ private readonly http;
1025
+ constructor(http: HttpClient);
1026
+ request(input: DecisionRequestInput): Promise<DecisionEnvelope>;
1027
+ get(envelopeId: string): Promise<DecisionEnvelope>;
1028
+ list(params?: DecisionListParams): Promise<Paginated<DecisionEnvelope>>;
1029
+ }
1030
+ declare class AuditNamespace {
1031
+ private readonly http;
1032
+ constructor(http: HttpClient);
1033
+ /**
1034
+ * Export a tamper-evident KYA audit pack.
1035
+ *
1036
+ * TODO(F.5): switch this to the public audit-pack REST route once the
1037
+ * customer-facing endpoint ships. E.1 intentionally points at the current
1038
+ * internal export bridge so the method shape is stable for quickstart code.
1039
+ */
1040
+ exportPack(input: AuditExportInput): Promise<AuditPack>;
1041
+ export(input: AuditExportInput): Promise<AuditPack>;
1042
+ list(params?: AuditListParams): Promise<Paginated<AuditEvent>>;
1043
+ get(eventId: string): Promise<AuditEvent>;
1044
+ }
1045
+
1046
+ export { type AgentAttestation, type AgentListParams, type AgentRecord, type AgentRegisterInput, type AgentRiskTier, type AgentStatus, type AgentUpdateInput, AgentsNamespace, type AttestedReceipt, AttestedReceiptSubmitter, type AttestedReceiptSubmitterOptions, AttestedReceiptVerifier, type AttestedReceiptVerifierOptions, type AuditEvent, type AuditExportInput, type AuditListParams, AuditNamespace, type AuditPack, type BankReceipt, type CoseSign1X509Proof, DEVNET_PROGRAM_IDS, type DecisionEnvelope, type DecisionListParams, type DecisionRequestInput, DecisionsNamespace, type EnclaveSigner, type FetchOptions, type Groth16AltBn128Proof, type IntendedAction, LoopByoaaApiKeyError, LoopByoaaClient, type LoopByoaaClientOptions, type LoopByoaaConfig, type LoopByoaaEnvironment, LoopByoaaError, type LoopByoaaErrorCode, type LoopByoaaErrorContext, LoopByoaaNetworkError, LoopByoaaPermissionError, LoopByoaaQuotaError, LoopByoaaRateLimitError, LoopByoaaServerError, LoopByoaaValidationError, MAINNET_PROGRAM_IDS, MAX_MERCHANT_NAME_RAW_LEN, MAX_RECEIPT_AGE_SECONDS, MAX_RECEIPT_CENTS, type Network, type Paginated, type ParsedLoopByoaaSdkKey, type PermissionConditions, type PermissionGrant, type PermissionGrantInput, type PermissionListParams, type PermissionRevokeInput, PermissionsNamespace, type PolicyInputs, ReceiptNotAuditedError, ReceiptNotFoundError, type ReceiptPayload, type ReceiptProofType, type ReceiptStatus, type ReceiptValidationError, type ReceiptValidationResult, type ResolvedConfig, type Risc0V1Proof, SESSION_INSTRUCTION_INDEX_SUBMIT_ATTESTED_RECEIPT, type StepUpEvidence, type SubmitBatchOptions, type SubmitError, type SubmitFail, type SubmitOk, type SubmitResult, UnsupportedProofTypeError, type VerifiedReceipt, decodeBankAttestedReceipt, decodeRegistryPcr0Set, defaultRpcUrl, deriveAgentSessionPda, deriveBankAttestedReceiptPda, deriveBankTxnId, deriveShoppingStatePda, encodeAttestedReceiptArgs, encodeSubmitAttestedReceiptIxData, getProgramIds, instructionDiscriminator, isMainnet, normalizeMerchantName, parseLoopByoaaSdkKey, resolveConfig, validateReceipt, verifyAttestedReceiptProof };