@umbra-privacy/sdk 1.0.0 → 2.0.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 +104 -25
- package/dist/{addresses-Brzgurv_.d.ts → addresses-B7HybtbJ.d.ts} +2 -1
- package/dist/{addresses-D_0YAS6B.d.cts → addresses-CTVY1oi7.d.cts} +2 -1
- package/dist/arcium-BXXlryfe.d.cts +20 -0
- package/dist/arcium-BXXlryfe.d.ts +20 -0
- package/dist/chunk-4RHXVBNI.js +203 -0
- package/dist/chunk-4RHXVBNI.js.map +1 -0
- package/dist/chunk-4TZVXB5G.js +324 -0
- package/dist/chunk-4TZVXB5G.js.map +1 -0
- package/dist/chunk-5GUSMQ74.cjs +549 -0
- package/dist/chunk-5GUSMQ74.cjs.map +1 -0
- package/dist/chunk-5KPQXPQM.js +36 -0
- package/dist/chunk-5KPQXPQM.js.map +1 -0
- package/dist/chunk-AXD7LXYY.cjs +405 -0
- package/dist/chunk-AXD7LXYY.cjs.map +1 -0
- package/dist/{chunk-HOEXDXRC.cjs → chunk-BL6WXLPV.cjs} +32 -360
- package/dist/chunk-BL6WXLPV.cjs.map +1 -0
- package/dist/chunk-CFFLOE7D.cjs +598 -0
- package/dist/chunk-CFFLOE7D.cjs.map +1 -0
- package/dist/{chunk-BM7N6N7E.js → chunk-CFTW5WNG.js} +3 -325
- package/dist/chunk-CFTW5WNG.js.map +1 -0
- package/dist/chunk-DD2WCK4C.js +327 -0
- package/dist/chunk-DD2WCK4C.js.map +1 -0
- package/dist/chunk-DMPMQ74B.cjs +246 -0
- package/dist/chunk-DMPMQ74B.cjs.map +1 -0
- package/dist/{chunk-2Q75CQQJ.js → chunk-EEKF4553.js} +2 -2
- package/dist/chunk-EEKF4553.js.map +1 -0
- package/dist/chunk-ENVYYEM4.cjs +113 -0
- package/dist/chunk-ENVYYEM4.cjs.map +1 -0
- package/dist/chunk-FQX6ZYGJ.js +500 -0
- package/dist/chunk-FQX6ZYGJ.js.map +1 -0
- package/dist/chunk-FSK2ICMB.cjs +39 -0
- package/dist/chunk-FSK2ICMB.cjs.map +1 -0
- package/dist/chunk-FZYWLQAF.cjs +355 -0
- package/dist/chunk-FZYWLQAF.cjs.map +1 -0
- package/dist/chunk-GP26R377.js +436 -0
- package/dist/chunk-GP26R377.js.map +1 -0
- package/dist/chunk-HA5FLM63.js +393 -0
- package/dist/chunk-HA5FLM63.js.map +1 -0
- package/dist/chunk-INJ73LXQ.js +1107 -0
- package/dist/chunk-INJ73LXQ.js.map +1 -0
- package/dist/chunk-JPDF7BIT.cjs +10892 -0
- package/dist/chunk-JPDF7BIT.cjs.map +1 -0
- package/dist/{chunk-MDFSBU5W.cjs → chunk-LTCKPTZC.cjs} +2 -351
- package/dist/chunk-LTCKPTZC.cjs.map +1 -0
- package/dist/chunk-MKNCBUFA.js +564 -0
- package/dist/chunk-MKNCBUFA.js.map +1 -0
- package/dist/chunk-NKVMSABR.cjs +207 -0
- package/dist/chunk-NKVMSABR.cjs.map +1 -0
- package/dist/chunk-OFDWNWCL.js +70 -0
- package/dist/chunk-OFDWNWCL.js.map +1 -0
- package/dist/chunk-QJAUUYZU.cjs +331 -0
- package/dist/chunk-QJAUUYZU.cjs.map +1 -0
- package/dist/chunk-RVUYPKKD.js +10750 -0
- package/dist/chunk-RVUYPKKD.js.map +1 -0
- package/dist/chunk-TLR7A64G.js +103 -0
- package/dist/chunk-TLR7A64G.js.map +1 -0
- package/dist/{chunk-MVKTV3FT.cjs → chunk-TQQZGNOI.cjs} +2 -2
- package/dist/chunk-TQQZGNOI.cjs.map +1 -0
- package/dist/chunk-UOFYS6M3.js +219 -0
- package/dist/chunk-UOFYS6M3.js.map +1 -0
- package/dist/chunk-UXMQI6B7.js +2406 -0
- package/dist/chunk-UXMQI6B7.js.map +1 -0
- package/dist/chunk-WN75ORDT.js +571 -0
- package/dist/chunk-WN75ORDT.js.map +1 -0
- package/dist/chunk-Y55PYKXH.cjs +595 -0
- package/dist/chunk-Y55PYKXH.cjs.map +1 -0
- package/dist/chunk-YEZBTYCP.cjs +77 -0
- package/dist/chunk-YEZBTYCP.cjs.map +1 -0
- package/dist/chunk-ZQOIYCGA.cjs +1126 -0
- package/dist/chunk-ZQOIYCGA.cjs.map +1 -0
- package/dist/chunk-ZY3TSHMJ.cjs +2665 -0
- package/dist/chunk-ZY3TSHMJ.cjs.map +1 -0
- package/dist/client-DkVBHMWb.d.cts +2613 -0
- package/dist/client-V4AF6Bz9.d.ts +2613 -0
- package/dist/common/pda/index.cjs +145 -0
- package/dist/common/pda/index.cjs.map +1 -0
- package/dist/common/pda/index.d.cts +1250 -0
- package/dist/common/pda/index.d.ts +1250 -0
- package/dist/common/pda/index.js +8 -0
- package/dist/common/pda/index.js.map +1 -0
- package/dist/constants/index.cjs +38 -164
- package/dist/constants/index.cjs.map +1 -1
- package/dist/constants/index.d.cts +8 -425
- package/dist/constants/index.d.ts +8 -425
- package/dist/constants/index.js +15 -124
- package/dist/constants/index.js.map +1 -1
- package/dist/crypto/index.cjs +583 -0
- package/dist/crypto/index.cjs.map +1 -0
- package/dist/crypto/index.d.cts +6731 -0
- package/dist/crypto/index.d.ts +6731 -0
- package/dist/crypto/index.js +14 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/{cryptography-BTGC72u-.d.ts → cryptography-BFSJcvi6.d.ts} +3 -2465
- package/dist/{cryptography-BTGC72u-.d.cts → cryptography-D6tPDh-Y.d.cts} +3 -2465
- package/dist/errors/index.cjs +64 -54
- package/dist/errors/index.d.cts +7 -797
- package/dist/errors/index.d.ts +7 -797
- package/dist/errors/index.js +3 -1
- package/dist/errors-B9EoPeWV.d.cts +593 -0
- package/dist/errors-B9EoPeWV.d.ts +593 -0
- package/dist/errors-DAIrstEL.d.cts +300 -0
- package/dist/errors-DPNMfyh0.d.ts +300 -0
- package/dist/index-BG0yjL7C.d.cts +6006 -0
- package/dist/index-ByynoyBO.d.ts +6006 -0
- package/dist/index.cjs +5126 -16118
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1031 -7685
- package/dist/index.d.ts +1031 -7685
- package/dist/index.js +3219 -14905
- package/dist/index.js.map +1 -1
- package/dist/interfaces/index.d.cts +14 -6
- package/dist/interfaces/index.d.ts +14 -6
- package/dist/interfaces-43cReBcS.d.cts +3346 -0
- package/dist/interfaces-B8xKNl_6.d.ts +997 -0
- package/dist/interfaces-D2NO6kDD.d.cts +997 -0
- package/dist/interfaces-z_xYJlgV.d.ts +3346 -0
- package/dist/math/index.cjs +115 -0
- package/dist/math/index.cjs.map +1 -0
- package/dist/math/index.d.cts +1327 -0
- package/dist/math/index.d.ts +1327 -0
- package/dist/math/index.js +10 -0
- package/dist/math/index.js.map +1 -0
- package/dist/networks-RMd3abPE.d.ts +44 -0
- package/dist/networks-yAoO8peQ.d.cts +44 -0
- package/dist/relayer-NRRMSMNB.js +4 -0
- package/dist/relayer-NRRMSMNB.js.map +1 -0
- package/dist/relayer-RJHEIXJG.cjs +21 -0
- package/dist/relayer-RJHEIXJG.cjs.map +1 -0
- package/dist/solana/index.cjs +56 -0
- package/dist/solana/index.cjs.map +1 -0
- package/dist/solana/index.d.cts +105 -0
- package/dist/solana/index.d.ts +105 -0
- package/dist/solana/index.js +7 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/{index-CLj_zWSD.d.ts → temporal-BbRaEPoO.d.ts} +1 -1
- package/dist/{index-CX6_pIRS.d.cts → temporal-oUj7iCaq.d.cts} +1 -1
- package/dist/transaction-forwarder-5mAMTjw6.d.ts +1155 -0
- package/dist/transaction-forwarder-C6gMUG7a.d.cts +1155 -0
- package/dist/types/index.cjs +232 -231
- package/dist/types/index.d.cts +15 -1485
- package/dist/types/index.d.ts +15 -1485
- package/dist/types/index.js +2 -1
- package/dist/types-BohhvPth.d.cts +87 -0
- package/dist/types-CW0oTT0j.d.ts +87 -0
- package/dist/types-C_V_CaKK.d.cts +2468 -0
- package/dist/types-C_V_CaKK.d.ts +2468 -0
- package/dist/types-Ca7frykr.d.ts +793 -0
- package/dist/types-CuKeoI19.d.cts +1296 -0
- package/dist/types-CxfTIpN9.d.ts +1052 -0
- package/dist/{types-n-sHFcgr.d.ts → types-D1jDUjfN.d.ts} +2 -2
- package/dist/types-DKEDUlH9.d.ts +1296 -0
- package/dist/types-EKuIfxTz.d.cts +1052 -0
- package/dist/{types-BBuELtY8.d.cts → types-IMGYmlv-.d.cts} +2 -2
- package/dist/types-PwNLi_2k.d.cts +793 -0
- package/dist/utils/index.cjs +823 -525
- package/dist/utils/index.d.cts +1711 -4021
- package/dist/utils/index.d.ts +1711 -4021
- package/dist/utils/index.js +9 -3
- package/dist/{versions-D9PqsEvj.d.cts → versions-BRlR36EA.d.cts} +1 -0
- package/dist/{versions-D9PqsEvj.d.ts → versions-BRlR36EA.d.ts} +1 -0
- package/package.json +79 -18
- package/dist/chunk-2Q75CQQJ.js.map +0 -1
- package/dist/chunk-BM7N6N7E.js.map +0 -1
- package/dist/chunk-GXKSUB2U.cjs +0 -4416
- package/dist/chunk-GXKSUB2U.cjs.map +0 -1
- package/dist/chunk-HOEXDXRC.cjs.map +0 -1
- package/dist/chunk-MDFSBU5W.cjs.map +0 -1
- package/dist/chunk-MQY7HDIA.js +0 -600
- package/dist/chunk-MQY7HDIA.js.map +0 -1
- package/dist/chunk-MVKTV3FT.cjs.map +0 -1
- package/dist/chunk-PG2J6V6Y.js +0 -4094
- package/dist/chunk-PG2J6V6Y.js.map +0 -1
- package/dist/chunk-VEGLTTYQ.cjs +0 -621
- package/dist/chunk-VEGLTTYQ.cjs.map +0 -1
- package/dist/chunk-WVHQ46DD.js +0 -758
- package/dist/chunk-WVHQ46DD.js.map +0 -1
- package/dist/index-B9pDY73x.d.ts +0 -12933
- package/dist/index-D33yo0qB.d.cts +0 -12933
- package/dist/networks-C-orpSFW.d.ts +0 -65
- package/dist/networks-FxYERGD1.d.cts +0 -65
|
@@ -0,0 +1,2613 @@
|
|
|
1
|
+
import { Address, Commitment, MaybeEncodedAccount, Blockhash, createSolanaRpc, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, Transaction, TransactionWithBlockhashLifetime, SignatureBytes } from '@solana/kit';
|
|
2
|
+
import { j as U32, e as U256LeBytes, k as U64, U as U128, l as U128LeBytes, h as U512 } from './types-C_V_CaKK.cjs';
|
|
3
|
+
import { A as AesKey, a as AesCiphertextWithMetadata, b as AesPlaintext, X as X25519PublicKey, c as X25519PrivateKey, M as MasterSeed, d as MasterViewingKey, P as PoseidonKey, Y as YearlyViewingKey, e as MonthlyViewingKey, D as DailyViewingKey, f as MintViewingKey, B as Bn254FieldElement, C as Curve25519FieldElement } from './cryptography-D6tPDh-Y.cjs';
|
|
4
|
+
import { S as SignedTransaction, T as TransactionSignature, M as MasterSeedLoaderFunction, a as MasterSeedStorerFunction, b as MasterSeedGeneratorFunction, L as LoaderFunction, c as StorerFunction, G as GeneratorFunction, P as ParameterizedLoaderFunction, d as ParameterizedStorerFunction, e as ParameterizedGeneratorFunction } from './types-EKuIfxTz.cjs';
|
|
5
|
+
import { Y as Year, M as Month, D as Day, H as Hour, a as Minute, S as Second } from './types-IMGYmlv-.cjs';
|
|
6
|
+
import { N as Network, f as ProtocolVersionSpecifierFunction, a as AlgorithmVersionSpecifierFunction, g as SchemeVersionSpecifierFunction, e as NetworkSpecifierFunction } from './versions-BRlR36EA.cjs';
|
|
7
|
+
import { N as NetworkConfig } from './networks-yAoO8peQ.cjs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* AES Encryption Interfaces
|
|
11
|
+
*
|
|
12
|
+
* This module defines function type interfaces for AES-256-GCM encryption
|
|
13
|
+
* and decryption operations used in UTXO recovery data encryption.
|
|
14
|
+
*
|
|
15
|
+
* ## Overview
|
|
16
|
+
*
|
|
17
|
+
* AES-256-GCM is used in Umbra to encrypt UTXO recovery data, enabling either
|
|
18
|
+
* the sender (ephemeral unlocker) or the receiver (receiver unlocker) to
|
|
19
|
+
* recover the information required to claim a UTXO at a later time. It is
|
|
20
|
+
* distinct from the Rescue cipher used for confidential token account balances.
|
|
21
|
+
*
|
|
22
|
+
* ## Key Derivation
|
|
23
|
+
*
|
|
24
|
+
* - **Ephemeral Unlocker Key**: Derived from `KDF(masterSeed, generationIndex)`.
|
|
25
|
+
* Allows the sender to recover UTXOs they created, even without knowing the
|
|
26
|
+
* recipient's key material.
|
|
27
|
+
* - **Receiver Unlocker Key**: Derived from the X25519 Diffie-Hellman shared
|
|
28
|
+
* secret between the sender's ephemeral private key and the recipient's
|
|
29
|
+
* X25519 public key. Allows the recipient to independently recover the UTXO.
|
|
30
|
+
*
|
|
31
|
+
* ## Ciphertext Format
|
|
32
|
+
*
|
|
33
|
+
* Both functions operate on the following byte layout:
|
|
34
|
+
* ```
|
|
35
|
+
* [IV (12 bytes) | AuthTag (16 bytes) | Ciphertext (N bytes)]
|
|
36
|
+
* ```
|
|
37
|
+
* This format is self-contained: all information needed for decryption (except
|
|
38
|
+
* the key) is present in the byte array.
|
|
39
|
+
*
|
|
40
|
+
* @packageDocumentation
|
|
41
|
+
* @module interfaces/cryptography/aes
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Function type for AES-256-GCM encryption.
|
|
47
|
+
*
|
|
48
|
+
* Encrypts plaintext data using AES-256-GCM with the provided key.
|
|
49
|
+
* The function generates a random IV internally and returns a combined
|
|
50
|
+
* ciphertext format: `[IV (12) | AuthTag (16) | Ciphertext (N)]`.
|
|
51
|
+
*
|
|
52
|
+
* @param key - The AES-256 encryption key. Must be exactly 32 bytes.
|
|
53
|
+
* Keys should be derived using a secure KDF (e.g. HKDF, Rescue-Prime).
|
|
54
|
+
* @param plaintext - The data to encrypt. May be any length including zero bytes.
|
|
55
|
+
* For UTXO recovery the plaintext is 68 bytes: amount (8) + modified
|
|
56
|
+
* generation index (16) + destination address (32) + domain separator (12).
|
|
57
|
+
* @returns A promise that resolves to the combined ciphertext byte array with
|
|
58
|
+
* layout `[IV (12) | AuthTag (16) | EncryptedData (N)]`.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* ## Implementation Contract
|
|
62
|
+
*
|
|
63
|
+
* Implementations of this type **must** guarantee:
|
|
64
|
+
* 1. A **fresh cryptographically random 12-byte IV** is generated for each call.
|
|
65
|
+
* Reusing an IV with the same key is catastrophic for GCM security.
|
|
66
|
+
* 2. The authentication tag is **128 bits** (16 bytes) as recommended by NIST SP 800-38D.
|
|
67
|
+
* 3. The output is returned in the canonical format `[IV | AuthTag | Ciphertext]`
|
|
68
|
+
* (note: NOT the @noble/ciphers internal order of `[Ciphertext | AuthTag]`).
|
|
69
|
+
* 4. The function is **referentially transparent** given the same key and plaintext
|
|
70
|
+
* except for the random IV (which makes each ciphertext unique).
|
|
71
|
+
*
|
|
72
|
+
* ## Security Considerations
|
|
73
|
+
*
|
|
74
|
+
* - **Never reuse** an IV with the same key. With a 12-byte IV and a CSPRNG, the
|
|
75
|
+
* probability of accidental collision over 2^32 invocations is approximately 2^-32,
|
|
76
|
+
* which is acceptable for practical use.
|
|
77
|
+
* - The 128-bit authentication tag provides strong integrity and authenticity.
|
|
78
|
+
* - AES-GCM is an AEAD scheme: an attacker who modifies any byte of the ciphertext
|
|
79
|
+
* will cause decryption to throw (tag verification failure), protecting against
|
|
80
|
+
* tampering.
|
|
81
|
+
* - The ciphertext is computationally indistinguishable from random bytes.
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const encryptor: AesEncryptorFunction = getAesEncryptor();
|
|
88
|
+
*
|
|
89
|
+
* // Derive key from seed + generation index
|
|
90
|
+
* const key = deriveEphemeralUnlockerKey(masterSeed, generationIndex);
|
|
91
|
+
*
|
|
92
|
+
* // Build the UTXO recovery plaintext
|
|
93
|
+
* const plaintext = buildRecoveryPlaintext(amount, genIndex, destination);
|
|
94
|
+
*
|
|
95
|
+
* const ciphertext = await encryptor(key, plaintext);
|
|
96
|
+
* // layout: [IV (12) | AuthTag (16) | EncryptedData (68)] = 96 bytes total
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @see {@link AesDecryptorFunction} — the paired decryption function type
|
|
100
|
+
* @see {@link getAesEncryptor} — factory that returns the default implementation
|
|
101
|
+
* @see {@link defaultAesEncryptor} — pre-instantiated convenience singleton
|
|
102
|
+
*/
|
|
103
|
+
type AesEncryptorFunction = (key: AesKey, plaintext: AesPlaintext) => Promise<AesCiphertextWithMetadata>;
|
|
104
|
+
/**
|
|
105
|
+
* Function type for AES-256-GCM decryption.
|
|
106
|
+
*
|
|
107
|
+
* Decrypts ciphertext data using AES-256-GCM with the provided key.
|
|
108
|
+
* The function expects the combined ciphertext format and extracts the
|
|
109
|
+
* IV and authentication tag from the input before decryption.
|
|
110
|
+
*
|
|
111
|
+
* @param key - The AES-256 decryption key. Must be exactly 32 bytes.
|
|
112
|
+
* Must be the same key that was used during encryption.
|
|
113
|
+
* @param ciphertext - The combined ciphertext byte array in the format
|
|
114
|
+
* `[IV (12) | AuthTag (16) | EncryptedData (N)]`. Minimum 28 bytes.
|
|
115
|
+
* @returns A promise that resolves to the decrypted plaintext as an
|
|
116
|
+
* {@link AesPlaintext} byte array.
|
|
117
|
+
*
|
|
118
|
+
* @throws {Error} If the 128-bit authentication tag fails verification. This
|
|
119
|
+
* occurs when the wrong key is provided or when any byte of the ciphertext
|
|
120
|
+
* has been modified. The error message is intentionally generic to avoid
|
|
121
|
+
* leaking information about which byte caused the failure.
|
|
122
|
+
* @throws {Error} If `key` is not a valid 32-byte {@link AesKey}.
|
|
123
|
+
* @throws {Error} If `ciphertext` is not a valid {@link AesCiphertextWithMetadata}
|
|
124
|
+
* (less than 28 bytes = IV + AuthTag minimum).
|
|
125
|
+
*
|
|
126
|
+
* @remarks
|
|
127
|
+
* ## Implementation Contract
|
|
128
|
+
*
|
|
129
|
+
* Implementations of this type **must** guarantee:
|
|
130
|
+
* 1. **IV extraction**: bytes 0–11 of `ciphertext` are used as the GCM IV.
|
|
131
|
+
* 2. **AuthTag extraction**: bytes 12–27 are used as the 128-bit authentication tag.
|
|
132
|
+
* 3. **Ciphertext body**: bytes 28+ are the encrypted data.
|
|
133
|
+
* 4. **Authentication before decryption**: The authentication tag is verified in
|
|
134
|
+
* constant time **before** any plaintext is returned. Partial plaintext
|
|
135
|
+
* must not be exposed on authentication failure.
|
|
136
|
+
* 5. On success, the returned plaintext is a fresh {@link AesPlaintext} byte array.
|
|
137
|
+
*
|
|
138
|
+
* ## Security Considerations
|
|
139
|
+
*
|
|
140
|
+
* - Authentication failure indicates either the wrong key or tampered bytes.
|
|
141
|
+
* The caller **must not** use any partially decrypted output on failure.
|
|
142
|
+
* - Catch authentication errors and treat them as hard failures; do not retry
|
|
143
|
+
* with a modified ciphertext (this would constitute an oracle attack surface).
|
|
144
|
+
* - Clear sensitive decrypted plaintext from memory as soon as it is processed.
|
|
145
|
+
*
|
|
146
|
+
* @public
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```typescript
|
|
150
|
+
* const decryptor: AesDecryptorFunction = getAesDecryptor();
|
|
151
|
+
*
|
|
152
|
+
* const key = deriveEphemeralUnlockerKey(masterSeed, generationIndex);
|
|
153
|
+
*
|
|
154
|
+
* try {
|
|
155
|
+
* const plaintext = await decryptor(key, ciphertext);
|
|
156
|
+
* const { amount, genIndex, destination } = parseRecoveryPlaintext(plaintext);
|
|
157
|
+
* } catch (error) {
|
|
158
|
+
* // Authentication failed — wrong key or corrupted data.
|
|
159
|
+
* // Do NOT attempt to parse or use any output from this call.
|
|
160
|
+
* console.error("Decryption failed:", error);
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Attempting decryption with a different key throws:
|
|
167
|
+
* const wrongKey = deriveEphemeralUnlockerKey(wrongSeed, generationIndex);
|
|
168
|
+
* await decryptor(wrongKey, ciphertext); // throws — authentication tag mismatch
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* @see {@link AesEncryptorFunction} — the paired encryption function type
|
|
172
|
+
* @see {@link getAesDecryptor} — factory that returns the default implementation
|
|
173
|
+
* @see {@link defaultAesDecryptor} — pre-instantiated convenience singleton
|
|
174
|
+
*/
|
|
175
|
+
type AesDecryptorFunction = (key: AesKey, ciphertext: AesCiphertextWithMetadata) => Promise<AesPlaintext>;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Solana Provider and Forwarder Interfaces.
|
|
179
|
+
*
|
|
180
|
+
* This module defines the TypeScript contracts for all Solana RPC abstraction
|
|
181
|
+
* layers used by the Umbra SDK. By depending on these interfaces rather than
|
|
182
|
+
* concrete implementations, SDK components remain testable (mock providers can
|
|
183
|
+
* be injected) and portable (different RPC backends can be substituted without
|
|
184
|
+
* changing call sites).
|
|
185
|
+
*
|
|
186
|
+
* @remarks
|
|
187
|
+
* The interfaces are organized into four groups:
|
|
188
|
+
*
|
|
189
|
+
* - **Account Info Provider** — {@link AccountInfoProviderFunction} for
|
|
190
|
+
* fetching raw encoded account data as a `Map`.
|
|
191
|
+
*
|
|
192
|
+
* - **Blockhash Provider** — {@link LatestBlockhashResult} and
|
|
193
|
+
* {@link GetLatestBlockhash} for fetching the latest blockhash and its
|
|
194
|
+
* validity window.
|
|
195
|
+
*
|
|
196
|
+
* - **Epoch Info Provider** — {@link EpochInfoResult} and {@link GetEpochInfo}
|
|
197
|
+
* for fetching current epoch and slot data (used for Token-2022 fee
|
|
198
|
+
* calculation).
|
|
199
|
+
*
|
|
200
|
+
* - **Transaction Forwarder** — {@link ForwardSequentially},
|
|
201
|
+
* {@link ForwardInParallel}, and {@link TransactionForwarder} for submitting
|
|
202
|
+
* signed transactions to the network.
|
|
203
|
+
*
|
|
204
|
+
* Factory types for `@solana/kit` dependencies are also exported
|
|
205
|
+
* ({@link CreateSolanaRpcFunction}, {@link CreateSolanaRpcSubscriptionsFunction},
|
|
206
|
+
* {@link SendAndConfirmTransactionFactoryFunction}) to allow dependency injection
|
|
207
|
+
* of the underlying `@solana/kit` primitives in tests.
|
|
208
|
+
*
|
|
209
|
+
* @packageDocumentation
|
|
210
|
+
*/
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Object interface for fetching Solana account information from the network.
|
|
214
|
+
*
|
|
215
|
+
* Provides two methods — `fetchAccount` for single-account lookups and
|
|
216
|
+
* `fetchAccounts` for batch lookups — backed by `@solana/kit`'s
|
|
217
|
+
* `fetchEncodedAccount` and `fetchEncodedAccounts` helpers respectively.
|
|
218
|
+
*
|
|
219
|
+
* @remarks
|
|
220
|
+
* Both methods return `MaybeEncodedAccount` values. Callers must check
|
|
221
|
+
* `account.exists` before accessing `account.data` to handle the case where
|
|
222
|
+
* an account has not been initialized on-chain.
|
|
223
|
+
*
|
|
224
|
+
* The returned encoded data is compatible with Codama-generated decoders. Pass
|
|
225
|
+
* the `MaybeEncodedAccount` directly to a decoder such as
|
|
226
|
+
* `decodeEncryptedUserAccount` to obtain a typed program account struct.
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* Injecting a mock `AccountInfoProvider` in tests:
|
|
230
|
+
* ```typescript
|
|
231
|
+
* import type { AccountInfoProvider } from "./interfaces";
|
|
232
|
+
* import { address } from "@solana/kit";
|
|
233
|
+
*
|
|
234
|
+
* const mockProvider: AccountInfoProvider = {
|
|
235
|
+
* fetchAccount: async (addr) => ({
|
|
236
|
+
* exists: true,
|
|
237
|
+
* address: addr,
|
|
238
|
+
* data: new Uint8Array([1, 2, 3, 4]),
|
|
239
|
+
* executable: false,
|
|
240
|
+
* lamports: 1_000_000n,
|
|
241
|
+
* programAddress: address("11111111111111111111111111111111"),
|
|
242
|
+
* }),
|
|
243
|
+
* fetchAccounts: async (addrs) =>
|
|
244
|
+
* addrs.map((addr) => ({
|
|
245
|
+
* exists: false,
|
|
246
|
+
* address: addr,
|
|
247
|
+
* })),
|
|
248
|
+
* };
|
|
249
|
+
* ```
|
|
250
|
+
*
|
|
251
|
+
* @see {@link getRpcAccountInfoProvider} for the default RPC-backed implementation
|
|
252
|
+
* @public
|
|
253
|
+
*/
|
|
254
|
+
/**
|
|
255
|
+
* Function type for batch-fetching Solana account information as a `Map`.
|
|
256
|
+
*
|
|
257
|
+
* This is the primary account-fetching interface used throughout the Umbra SDK.
|
|
258
|
+
* It accepts a readonly array of addresses and resolves to a `Map` keyed by
|
|
259
|
+
* address. This functional interface supports the SDK's dependency injection
|
|
260
|
+
* pattern and makes mocking trivial.
|
|
261
|
+
*
|
|
262
|
+
* @remarks
|
|
263
|
+
* **Deduplication** — Implementations should deduplicate input addresses before
|
|
264
|
+
* issuing RPC calls to avoid wasting slots in a `getMultipleAccounts` request.
|
|
265
|
+
*
|
|
266
|
+
* **Non-existent accounts** — The `Map` must contain an entry for every input
|
|
267
|
+
* address (after deduplication), even if the account does not exist on-chain.
|
|
268
|
+
* Non-existent accounts are represented by a `MaybeEncodedAccount` with
|
|
269
|
+
* `exists: false`.
|
|
270
|
+
*
|
|
271
|
+
* **Empty input** — Passing an empty array should return an empty `Map`
|
|
272
|
+
* immediately without making an RPC call.
|
|
273
|
+
*
|
|
274
|
+
* **Performance** — For large address arrays, implementations may need to chunk
|
|
275
|
+
* requests because `getMultipleAccounts` has a per-call limit (typically 100
|
|
276
|
+
* accounts on public RPC nodes). Consider rate-limit constraints as well.
|
|
277
|
+
*
|
|
278
|
+
* @param addresses - A `ReadonlyArray` of Solana `Address` values to fetch.
|
|
279
|
+
* @param options - Optional configuration. Pass `{ commitment }` to override the
|
|
280
|
+
* default `"confirmed"` commitment level for this fetch.
|
|
281
|
+
* @returns A `Promise` resolving to a `Map<Address, MaybeEncodedAccount>` where
|
|
282
|
+
* each key is an input address and each value is the corresponding account data
|
|
283
|
+
* (or a `{ exists: false }` sentinel for missing accounts).
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* Using the provider to batch-fetch accounts:
|
|
287
|
+
* ```typescript
|
|
288
|
+
* import type { AccountInfoProviderFunction } from "./interfaces";
|
|
289
|
+
* import { address } from "@solana/kit";
|
|
290
|
+
*
|
|
291
|
+
* const fetchAccounts: AccountInfoProviderFunction = getRpcAccountInfoProvider({
|
|
292
|
+
* rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
293
|
+
* });
|
|
294
|
+
*
|
|
295
|
+
* const addresses = [
|
|
296
|
+
* address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
|
|
297
|
+
* address("11111111111111111111111111111111"),
|
|
298
|
+
* ];
|
|
299
|
+
*
|
|
300
|
+
* const accountMap = await fetchAccounts(addresses);
|
|
301
|
+
*
|
|
302
|
+
* for (const [addr, account] of accountMap) {
|
|
303
|
+
* if (account.exists) {
|
|
304
|
+
* console.log(`${addr}: ${account.data.length} bytes`);
|
|
305
|
+
* } else {
|
|
306
|
+
* console.log(`${addr}: does not exist`);
|
|
307
|
+
* }
|
|
308
|
+
* }
|
|
309
|
+
* ```
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* Mock implementation for unit testing:
|
|
313
|
+
* ```typescript
|
|
314
|
+
* const mockProvider: AccountInfoProviderFunction = async (addresses) => {
|
|
315
|
+
* const result = new Map<Address, MaybeEncodedAccount>();
|
|
316
|
+
* for (const addr of addresses) {
|
|
317
|
+
* result.set(addr, {
|
|
318
|
+
* exists: true,
|
|
319
|
+
* address: addr,
|
|
320
|
+
* data: new Uint8Array([0, 1, 2, 3]),
|
|
321
|
+
* executable: false,
|
|
322
|
+
* lamports: 1_000_000n,
|
|
323
|
+
* programAddress: address("11111111111111111111111111111111"),
|
|
324
|
+
* });
|
|
325
|
+
* }
|
|
326
|
+
* return result;
|
|
327
|
+
* };
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
* @see {@link AccountInfoProvider} for the object-oriented alternative
|
|
331
|
+
* @see {@link getRpcAccountInfoProvider} for the default RPC-backed implementation
|
|
332
|
+
* @public
|
|
333
|
+
*/
|
|
334
|
+
type AccountInfoProviderFunction = (addresses: readonly Address[], options?: {
|
|
335
|
+
readonly commitment?: Commitment;
|
|
336
|
+
}) => Promise<Map<Address, MaybeEncodedAccount>>;
|
|
337
|
+
/**
|
|
338
|
+
* The result of fetching the latest blockhash from the Solana network.
|
|
339
|
+
*
|
|
340
|
+
* Contains both the blockhash value and the `lastValidBlockHeight`, which
|
|
341
|
+
* together define the lifetime window for a transaction. The two fields must
|
|
342
|
+
* be passed together to `setTransactionMessageLifetimeUsingBlockhash` from
|
|
343
|
+
* `@solana/kit`.
|
|
344
|
+
*
|
|
345
|
+
* @remarks
|
|
346
|
+
* Transactions using this blockhash must be confirmed before the network
|
|
347
|
+
* reaches `lastValidBlockHeight`, otherwise they will be rejected as expired.
|
|
348
|
+
* On mainnet, the validity window is typically around 150 blocks (~60–90
|
|
349
|
+
* seconds), but this can shrink during periods of high slot skip rates.
|
|
350
|
+
*
|
|
351
|
+
* @see {@link GetLatestBlockhash} for the function that returns this type
|
|
352
|
+
* @public
|
|
353
|
+
*/
|
|
354
|
+
interface LatestBlockhashResult {
|
|
355
|
+
/**
|
|
356
|
+
* A recent blockhash observed from the cluster at the `"confirmed"`
|
|
357
|
+
* commitment level.
|
|
358
|
+
*
|
|
359
|
+
* @remarks
|
|
360
|
+
* This hash serves three purposes:
|
|
361
|
+
* - It sets the transaction's lifetime constraint (expires at `lastValidBlockHeight`).
|
|
362
|
+
* - It prevents replay attacks — once expired, the same blockhash cannot be
|
|
363
|
+
* reused to submit a duplicate.
|
|
364
|
+
* - It anchors the transaction to a specific fork of the chain.
|
|
365
|
+
*/
|
|
366
|
+
readonly blockhash: Blockhash;
|
|
367
|
+
/**
|
|
368
|
+
* The last block height at which this blockhash is considered valid.
|
|
369
|
+
*
|
|
370
|
+
* @remarks
|
|
371
|
+
* After the network surpasses this block height, any transaction referencing
|
|
372
|
+
* `blockhash` will be rejected with `BlockhashNotFound`. Callers must ensure
|
|
373
|
+
* they submit and confirm the transaction before this height is reached.
|
|
374
|
+
*
|
|
375
|
+
* Typical validity window: ~150 blocks (~60–90 seconds on mainnet).
|
|
376
|
+
*/
|
|
377
|
+
readonly lastValidBlockHeight: bigint;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Function type that fetches the latest blockhash and validity window from
|
|
381
|
+
* the Solana network.
|
|
382
|
+
*
|
|
383
|
+
* Every invocation of this function should return a fresh blockhash. Caching
|
|
384
|
+
* is the caller's responsibility; the lifetime window should be factored into
|
|
385
|
+
* any caching strategy.
|
|
386
|
+
*
|
|
387
|
+
* @remarks
|
|
388
|
+
* **Underlying RPC call** — Implementations typically call `getLatestBlockhash`
|
|
389
|
+
* (commitment: `"confirmed"`) on the Solana JSON-RPC API.
|
|
390
|
+
*
|
|
391
|
+
* **Blockhash validity** — Blockhashes are valid for approximately 150 blocks
|
|
392
|
+
* (~1–2 minutes). A transaction must be confirmed before `lastValidBlockHeight`
|
|
393
|
+
* is surpassed; otherwise it will be rejected with `BlockhashNotFound`.
|
|
394
|
+
*
|
|
395
|
+
* **Caching considerations** — Blockhashes can be safely cached for a few
|
|
396
|
+
* seconds to reduce RPC load in high-throughput scenarios, but the cache TTL
|
|
397
|
+
* must be short enough relative to the validity window to avoid expiry.
|
|
398
|
+
*
|
|
399
|
+
* **Integration with `@solana/kit`** — The returned `LatestBlockhashResult` is
|
|
400
|
+
* designed to be passed directly to `setTransactionMessageLifetimeUsingBlockhash`
|
|
401
|
+
* without any transformation:
|
|
402
|
+
*
|
|
403
|
+
* @param options - Optional configuration. Pass `{ commitment }` to override the
|
|
404
|
+
* default `"confirmed"` commitment level for this RPC call.
|
|
405
|
+
* @returns A `Promise` resolving to a {@link LatestBlockhashResult} containing
|
|
406
|
+
* the current blockhash and its last valid block height.
|
|
407
|
+
*
|
|
408
|
+
* @throws May reject if the RPC endpoint is unreachable or returns a malformed
|
|
409
|
+
* response.
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* Using in a transaction-building pipeline with `pipe`:
|
|
413
|
+
* ```typescript
|
|
414
|
+
* import type { GetLatestBlockhash } from "./interfaces";
|
|
415
|
+
* import {
|
|
416
|
+
* createTransactionMessage,
|
|
417
|
+
* setTransactionMessageLifetimeUsingBlockhash,
|
|
418
|
+
* pipe,
|
|
419
|
+
* } from "@solana/kit";
|
|
420
|
+
*
|
|
421
|
+
* async function buildTx(getLatestBlockhash: GetLatestBlockhash) {
|
|
422
|
+
* const blockhashResult = await getLatestBlockhash();
|
|
423
|
+
* return pipe(
|
|
424
|
+
* createTransactionMessage({ version: 0 }),
|
|
425
|
+
* (m) => setTransactionMessageLifetimeUsingBlockhash(blockhashResult, m),
|
|
426
|
+
* );
|
|
427
|
+
* }
|
|
428
|
+
* ```
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* Simple RPC-backed implementation:
|
|
432
|
+
* ```typescript
|
|
433
|
+
* const getLatestBlockhash: GetLatestBlockhash = async () => {
|
|
434
|
+
* const { value } = await rpc.getLatestBlockhash().send();
|
|
435
|
+
* return value;
|
|
436
|
+
* };
|
|
437
|
+
* ```
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* Implementation with short-lived caching:
|
|
441
|
+
* ```typescript
|
|
442
|
+
* const createCachedBlockhashProvider = (rpc: Rpc): GetLatestBlockhash => {
|
|
443
|
+
* let cached: { result: LatestBlockhashResult; fetchedAt: number } | null = null;
|
|
444
|
+
* const CACHE_TTL_MS = 10_000; // 10 seconds
|
|
445
|
+
*
|
|
446
|
+
* return async () => {
|
|
447
|
+
* const now = Date.now();
|
|
448
|
+
* if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
|
|
449
|
+
* return cached.result;
|
|
450
|
+
* }
|
|
451
|
+
* const { value } = await rpc.getLatestBlockhash().send();
|
|
452
|
+
* cached = { result: value, fetchedAt: now };
|
|
453
|
+
* return value;
|
|
454
|
+
* };
|
|
455
|
+
* };
|
|
456
|
+
* ```
|
|
457
|
+
*
|
|
458
|
+
* @see {@link LatestBlockhashResult} for the returned data shape
|
|
459
|
+
* @see {@link getRpcBlockhashProvider} for the default RPC-backed factory
|
|
460
|
+
* @public
|
|
461
|
+
*/
|
|
462
|
+
type GetLatestBlockhash = (options?: {
|
|
463
|
+
readonly commitment?: Commitment;
|
|
464
|
+
}) => Promise<LatestBlockhashResult>;
|
|
465
|
+
/**
|
|
466
|
+
* The result of fetching epoch information from the Solana network.
|
|
467
|
+
*
|
|
468
|
+
* Contains the current epoch number, slot progress within the epoch, absolute
|
|
469
|
+
* slot and block height, and optionally the transaction count for the epoch.
|
|
470
|
+
* The epoch number is particularly important for Token-2022 transfer fee
|
|
471
|
+
* schedule resolution.
|
|
472
|
+
*
|
|
473
|
+
* @remarks
|
|
474
|
+
* Solana mainnet epochs span approximately 432,000 slots (~2–3 days). Epoch
|
|
475
|
+
* boundaries trigger several protocol events: stake reward distribution, leader
|
|
476
|
+
* schedule rotation, and Token-2022 fee schedule transitions.
|
|
477
|
+
*
|
|
478
|
+
* @see {@link GetEpochInfo} for the function that returns this type
|
|
479
|
+
* @public
|
|
480
|
+
*/
|
|
481
|
+
interface EpochInfoResult {
|
|
482
|
+
/**
|
|
483
|
+
* The current epoch number.
|
|
484
|
+
*
|
|
485
|
+
* @remarks
|
|
486
|
+
* Epochs are sequential, starting from 0 at genesis. Used by Token-2022
|
|
487
|
+
* transfer fee logic to determine which fee schedule (older or newer) is
|
|
488
|
+
* active for a given mint. If `currentEpoch >= newerTransferFee.epoch`,
|
|
489
|
+
* the newer schedule is in effect.
|
|
490
|
+
*/
|
|
491
|
+
readonly epoch: bigint;
|
|
492
|
+
/**
|
|
493
|
+
* The current slot index within the epoch (0-based).
|
|
494
|
+
*
|
|
495
|
+
* @remarks
|
|
496
|
+
* Progress through the current epoch: `slotIndex / slotsInEpoch` gives a
|
|
497
|
+
* value in [0, 1). Each slot is approximately 400ms on mainnet.
|
|
498
|
+
*/
|
|
499
|
+
readonly slotIndex: bigint;
|
|
500
|
+
/**
|
|
501
|
+
* Total number of slots in the current epoch.
|
|
502
|
+
*
|
|
503
|
+
* @remarks
|
|
504
|
+
* On mainnet, this is typically 432,000. Divide `slotIndex` by this value
|
|
505
|
+
* to get epoch completion percentage.
|
|
506
|
+
*/
|
|
507
|
+
readonly slotsInEpoch: bigint;
|
|
508
|
+
/**
|
|
509
|
+
* The absolute slot number since genesis.
|
|
510
|
+
*
|
|
511
|
+
* @remarks
|
|
512
|
+
* Unlike `slotIndex`, this counter never resets. It represents the total
|
|
513
|
+
* number of slots that have elapsed since the cluster's genesis block.
|
|
514
|
+
*/
|
|
515
|
+
readonly absoluteSlot: bigint;
|
|
516
|
+
/**
|
|
517
|
+
* The current confirmed block height.
|
|
518
|
+
*
|
|
519
|
+
* @remarks
|
|
520
|
+
* Block height may differ from slot number because some slots are skipped
|
|
521
|
+
* (no block is produced). Block height strictly increases but may have gaps.
|
|
522
|
+
*/
|
|
523
|
+
readonly blockHeight: bigint;
|
|
524
|
+
/**
|
|
525
|
+
* The total number of transactions processed in the current epoch, if reported.
|
|
526
|
+
*
|
|
527
|
+
* @remarks
|
|
528
|
+
* Not all RPC nodes expose this field. When the node returns `null` for
|
|
529
|
+
* `transactionCount`, this field is omitted (set to `undefined`) in the
|
|
530
|
+
* result rather than being present with a null value.
|
|
531
|
+
*/
|
|
532
|
+
readonly transactionCount?: bigint;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Function type that fetches current epoch information from the Solana network.
|
|
536
|
+
*
|
|
537
|
+
* The primary use case for this function in the Umbra SDK is determining which
|
|
538
|
+
* Token-2022 transfer fee schedule is active before computing deposit or
|
|
539
|
+
* transfer amounts.
|
|
540
|
+
*
|
|
541
|
+
* @remarks
|
|
542
|
+
* **Underlying RPC call** — Implementations call `getEpochInfo` (commitment:
|
|
543
|
+
* `"confirmed"`) on the Solana JSON-RPC API.
|
|
544
|
+
*
|
|
545
|
+
* **Token-2022 fee schedule selection** — Token-2022 mints with the Transfer
|
|
546
|
+
* Fee extension may define two consecutive schedules: an "older" schedule and a
|
|
547
|
+
* "newer" schedule that takes effect at a future epoch. The active schedule is:
|
|
548
|
+
* - `newerTransferFee` if `currentEpoch >= newerTransferFee.epoch`
|
|
549
|
+
* - `olderTransferFee` otherwise
|
|
550
|
+
*
|
|
551
|
+
* **Caching** — Epochs on mainnet are ~2–3 days long, so caching the result for
|
|
552
|
+
* minutes is safe in most scenarios. Near an epoch boundary, a stale cache could
|
|
553
|
+
* cause the SDK to use the wrong fee schedule; use a conservative TTL (e.g.,
|
|
554
|
+
* 60 seconds) if precision near boundaries matters.
|
|
555
|
+
*
|
|
556
|
+
* @param options - Optional configuration. Pass `{ commitment }` to override the
|
|
557
|
+
* default `"confirmed"` commitment level for this RPC call.
|
|
558
|
+
* @returns A `Promise` resolving to an {@link EpochInfoResult}.
|
|
559
|
+
*
|
|
560
|
+
* @throws May reject if the RPC endpoint is unreachable or returns a malformed
|
|
561
|
+
* response.
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* Simple RPC-backed implementation:
|
|
565
|
+
* ```typescript
|
|
566
|
+
* const getEpochInfo: GetEpochInfo = async () => {
|
|
567
|
+
* const result = await rpc.getEpochInfo().send();
|
|
568
|
+
* return {
|
|
569
|
+
* epoch: result.epoch,
|
|
570
|
+
* slotIndex: result.slotIndex,
|
|
571
|
+
* slotsInEpoch: result.slotsInEpoch,
|
|
572
|
+
* absoluteSlot: result.absoluteSlot,
|
|
573
|
+
* blockHeight: result.blockHeight,
|
|
574
|
+
* ...(result.transactionCount !== null && {
|
|
575
|
+
* transactionCount: result.transactionCount,
|
|
576
|
+
* }),
|
|
577
|
+
* };
|
|
578
|
+
* };
|
|
579
|
+
* ```
|
|
580
|
+
*
|
|
581
|
+
* @example
|
|
582
|
+
* Using epoch info for Token-2022 fee calculation:
|
|
583
|
+
* ```typescript
|
|
584
|
+
* const epochInfo = await getEpochInfo();
|
|
585
|
+
* const feeConfig = extractTransferFeeConfig(mintAccountData);
|
|
586
|
+
* if (feeConfig) {
|
|
587
|
+
* const fee = calculateTransferFee(feeConfig, epochInfo.epoch, depositAmount);
|
|
588
|
+
* const grossAmount = depositAmount + fee;
|
|
589
|
+
* }
|
|
590
|
+
* ```
|
|
591
|
+
*
|
|
592
|
+
* @see {@link EpochInfoResult} for the returned data shape
|
|
593
|
+
* @see {@link getRpcEpochInfoProvider} for the default RPC-backed factory
|
|
594
|
+
* @public
|
|
595
|
+
*/
|
|
596
|
+
type GetEpochInfo = (options?: {
|
|
597
|
+
readonly commitment?: Commitment;
|
|
598
|
+
}) => Promise<EpochInfoResult>;
|
|
599
|
+
/**
|
|
600
|
+
* Factory function type for creating a Solana JSON-RPC client.
|
|
601
|
+
*
|
|
602
|
+
* Mirrors the signature of `createSolanaRpc` from `@solana/kit`. Expressed as
|
|
603
|
+
* a type alias so that factory functions in the SDK can accept either the real
|
|
604
|
+
* `createSolanaRpc` or a test double without importing the concrete function.
|
|
605
|
+
*
|
|
606
|
+
* @typeParam TUrl - The literal string type of the RPC URL, preserved through
|
|
607
|
+
* the return type for nominal URL typing in `@solana/kit`.
|
|
608
|
+
*
|
|
609
|
+
* @param rpcUrl - The HTTP or HTTPS URL of the Solana JSON-RPC endpoint.
|
|
610
|
+
* @returns A Solana RPC client instance with typed method wrappers.
|
|
611
|
+
*
|
|
612
|
+
* @remarks
|
|
613
|
+
* The default implementation is `createSolanaRpc` from `@solana/kit`. Override
|
|
614
|
+
* this in tests to inject a mock client that returns controlled responses.
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* ```typescript
|
|
618
|
+
* import { createSolanaRpc } from "@solana/kit";
|
|
619
|
+
* import type { CreateSolanaRpcFunction } from "./interfaces";
|
|
620
|
+
*
|
|
621
|
+
* const myFactory: CreateSolanaRpcFunction = (url) => createSolanaRpc(url);
|
|
622
|
+
* ```
|
|
623
|
+
*
|
|
624
|
+
* @public
|
|
625
|
+
*/
|
|
626
|
+
type CreateSolanaRpcFunction = <TUrl extends string>(rpcUrl: TUrl) => ReturnType<typeof createSolanaRpc<TUrl>>;
|
|
627
|
+
/**
|
|
628
|
+
* Factory function type for creating a Solana RPC subscriptions client.
|
|
629
|
+
*
|
|
630
|
+
* Mirrors the signature of `createSolanaRpcSubscriptions` from `@solana/kit`.
|
|
631
|
+
* Used by the websocket-based transaction forwarder to establish a WebSocket
|
|
632
|
+
* connection for real-time transaction confirmation notifications.
|
|
633
|
+
*
|
|
634
|
+
* @typeParam TUrl - The literal string type of the WebSocket URL.
|
|
635
|
+
*
|
|
636
|
+
* @param rpcSubscriptionsUrl - The WebSocket URL of the Solana RPC endpoint
|
|
637
|
+
* (typically starts with `wss://`).
|
|
638
|
+
* @returns A Solana RPC subscriptions client instance.
|
|
639
|
+
*
|
|
640
|
+
* @remarks
|
|
641
|
+
* The default implementation is `createSolanaRpcSubscriptions` from
|
|
642
|
+
* `@solana/kit`. Override in tests to inject a mock subscriptions client.
|
|
643
|
+
*
|
|
644
|
+
* @example
|
|
645
|
+
* ```typescript
|
|
646
|
+
* import { createSolanaRpcSubscriptions } from "@solana/kit";
|
|
647
|
+
* import type { CreateSolanaRpcSubscriptionsFunction } from "./interfaces";
|
|
648
|
+
*
|
|
649
|
+
* const myFactory: CreateSolanaRpcSubscriptionsFunction = (url) =>
|
|
650
|
+
* createSolanaRpcSubscriptions(url);
|
|
651
|
+
* ```
|
|
652
|
+
*
|
|
653
|
+
* @see {@link getWebsocketTransactionForwarder} for the factory that uses this
|
|
654
|
+
* @public
|
|
655
|
+
*/
|
|
656
|
+
type CreateSolanaRpcSubscriptionsFunction = <TUrl extends string>(rpcSubscriptionsUrl: TUrl) => ReturnType<typeof createSolanaRpcSubscriptions<TUrl>>;
|
|
657
|
+
/**
|
|
658
|
+
* Factory function type for `sendAndConfirmTransactionFactory` from `@solana/kit`.
|
|
659
|
+
*
|
|
660
|
+
* Expressed as a type alias to enable dependency injection in the websocket-based
|
|
661
|
+
* transaction forwarder. The factory takes an `{ rpc, rpcSubscriptions }` options
|
|
662
|
+
* object and returns a `sendAndConfirmTransaction` function that submits a signed
|
|
663
|
+
* transaction and waits for confirmation via a WebSocket subscription.
|
|
664
|
+
*
|
|
665
|
+
* @remarks
|
|
666
|
+
* The default implementation is `sendAndConfirmTransactionFactory` from
|
|
667
|
+
* `@solana/kit`. Override in tests to inject a mock that avoids real network
|
|
668
|
+
* I/O.
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* ```typescript
|
|
672
|
+
* import { sendAndConfirmTransactionFactory } from "@solana/kit";
|
|
673
|
+
* import type { SendAndConfirmTransactionFactoryFunction } from "./interfaces";
|
|
674
|
+
*
|
|
675
|
+
* const myFactory: SendAndConfirmTransactionFactoryFunction = (opts) =>
|
|
676
|
+
* sendAndConfirmTransactionFactory(opts);
|
|
677
|
+
* ```
|
|
678
|
+
*
|
|
679
|
+
* @see {@link getWebsocketTransactionForwarder} for the factory that uses this
|
|
680
|
+
* @public
|
|
681
|
+
*/
|
|
682
|
+
type SendAndConfirmTransactionFactoryFunction = typeof sendAndConfirmTransactionFactory;
|
|
683
|
+
/**
|
|
684
|
+
* Function type that forwards signed transactions to the Solana network
|
|
685
|
+
* sequentially, waiting for each to be confirmed before sending the next.
|
|
686
|
+
*
|
|
687
|
+
* Use sequential forwarding when transactions have dependencies — for example,
|
|
688
|
+
* when transaction N creates an account that transaction N+1 writes to. If any
|
|
689
|
+
* transaction fails, subsequent transactions are not attempted.
|
|
690
|
+
*
|
|
691
|
+
* @param transactions - An ordered, readonly array of fully signed transactions.
|
|
692
|
+
* Index 0 is submitted first.
|
|
693
|
+
* @returns A `Promise` resolving to an array of {@link TransactionSignature}
|
|
694
|
+
* values in the same order as the input transactions.
|
|
695
|
+
*
|
|
696
|
+
* @throws Rejects if any transaction in the sequence fails to confirm. The error
|
|
697
|
+
* should identify which transaction failed and may carry the signatures of any
|
|
698
|
+
* previously confirmed transactions in its context.
|
|
699
|
+
*
|
|
700
|
+
* @remarks
|
|
701
|
+
* **Execution semantics:**
|
|
702
|
+
* - Transactions are submitted in array order (index 0 first).
|
|
703
|
+
* - Each transaction is awaited before the next is submitted.
|
|
704
|
+
* - A failure aborts the remaining transactions — they are not retried.
|
|
705
|
+
*
|
|
706
|
+
* **Use cases:**
|
|
707
|
+
* - Creating an account then performing operations on it.
|
|
708
|
+
* - Initializing a program then calling its instructions.
|
|
709
|
+
* - Any scenario where the correctness of transaction N depends on N-1 landing.
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* ```typescript
|
|
713
|
+
* const signatures = await forwarder.forwardSequentially([
|
|
714
|
+
* createAccountTx,
|
|
715
|
+
* depositTx,
|
|
716
|
+
* ]);
|
|
717
|
+
* console.log(`Account created: ${signatures[0]}`);
|
|
718
|
+
* console.log(`Deposit landed: ${signatures[1]}`);
|
|
719
|
+
* ```
|
|
720
|
+
*
|
|
721
|
+
* @see {@link ForwardInParallel} for the parallel alternative
|
|
722
|
+
* @see {@link TransactionForwarder} for the interface that contains both methods
|
|
723
|
+
* @public
|
|
724
|
+
*/
|
|
725
|
+
type ForwardSequentially = (transactions: readonly SignedTransaction[]) => Promise<readonly TransactionSignature[]>;
|
|
726
|
+
/**
|
|
727
|
+
* Function type that forwards signed transactions to the Solana network in
|
|
728
|
+
* parallel, submitting all without waiting for individual confirmations.
|
|
729
|
+
*
|
|
730
|
+
* Use parallel forwarding when transactions are fully independent — they do not
|
|
731
|
+
* share writable accounts and their outcomes do not affect each other. Parallel
|
|
732
|
+
* forwarding is significantly faster than sequential forwarding for large batches.
|
|
733
|
+
*
|
|
734
|
+
* @param transactions - A readonly array of fully signed transactions to submit
|
|
735
|
+
* simultaneously.
|
|
736
|
+
* @returns A `Promise` resolving to an array of {@link TransactionSignature}
|
|
737
|
+
* values in the same order as the input transactions (not confirmation order).
|
|
738
|
+
*
|
|
739
|
+
* @throws Rejects if any transaction fails. Because all transactions are
|
|
740
|
+
* submitted simultaneously, some may succeed while others fail. The error
|
|
741
|
+
* should indicate which transactions failed.
|
|
742
|
+
*
|
|
743
|
+
* @remarks
|
|
744
|
+
* **Execution semantics:**
|
|
745
|
+
* - All transactions are submitted at approximately the same time.
|
|
746
|
+
* - Confirmations may arrive in any order; results are always returned in input order.
|
|
747
|
+
* - Partial failure is possible: some transactions may confirm while others fail.
|
|
748
|
+
*
|
|
749
|
+
* **Performance considerations:**
|
|
750
|
+
* - Ideal for batch transfers to independent recipients.
|
|
751
|
+
* - May hit per-second rate limits on public RPC nodes with large batches.
|
|
752
|
+
* - The `onTransactionConfirmed` callback is not invoked during parallel forwarding.
|
|
753
|
+
*
|
|
754
|
+
* @example
|
|
755
|
+
* ```typescript
|
|
756
|
+
* const transferTxs = recipients.map((r) => buildTransfer(sender, r, amount));
|
|
757
|
+
* const signatures = await forwarder.forwardInParallel(transferTxs);
|
|
758
|
+
* signatures.forEach((sig, i) => {
|
|
759
|
+
* console.log(`Transfer to ${recipients[i]}: ${sig}`);
|
|
760
|
+
* });
|
|
761
|
+
* ```
|
|
762
|
+
*
|
|
763
|
+
* @see {@link ForwardSequentially} for the sequential alternative
|
|
764
|
+
* @see {@link TransactionForwarder} for the interface that contains both methods
|
|
765
|
+
* @public
|
|
766
|
+
*/
|
|
767
|
+
type ForwardInParallel = (transactions: readonly SignedTransaction[]) => Promise<readonly TransactionSignature[]>;
|
|
768
|
+
/**
|
|
769
|
+
* Interface for forwarding fully signed transactions to the Solana network.
|
|
770
|
+
*
|
|
771
|
+
* This interface provides two complementary strategies for transaction
|
|
772
|
+
* submission. Choosing the right strategy affects both correctness and
|
|
773
|
+
* performance:
|
|
774
|
+
*
|
|
775
|
+
* - Use `forwardSequentially` when transactions have dependencies (state
|
|
776
|
+
* produced by one transaction is consumed by the next).
|
|
777
|
+
* - Use `forwardInParallel` when transactions are independent (no shared
|
|
778
|
+
* writable accounts, no ordering requirement).
|
|
779
|
+
*
|
|
780
|
+
* @remarks
|
|
781
|
+
* **Compile-time safety** — Both methods accept only `SignedTransaction`, a
|
|
782
|
+
* branded type that indicates the transaction has at least one signature and
|
|
783
|
+
* carries a blockhash lifetime constraint. Unsigned or partially unsigned
|
|
784
|
+
* transactions produce a compile-time error, preventing accidental submission
|
|
785
|
+
* of incomplete transactions.
|
|
786
|
+
*
|
|
787
|
+
* **Implementation requirements** — Concrete implementations should handle:
|
|
788
|
+
* - RPC endpoint selection, failover, and connection management.
|
|
789
|
+
* - Retry logic for transient send failures.
|
|
790
|
+
* - Blockhash expiry detection (the transaction window closes at
|
|
791
|
+
* `lastValidBlockHeight`).
|
|
792
|
+
* - Structured error propagation with diagnostic context.
|
|
793
|
+
*
|
|
794
|
+
* **Available implementations:**
|
|
795
|
+
* - {@link getWebsocketTransactionForwarder} — uses WebSocket subscriptions
|
|
796
|
+
* for efficient confirmation (lower latency, requires WebSocket access).
|
|
797
|
+
* - {@link getPollingTransactionForwarder} — polls `getSignatureStatuses`
|
|
798
|
+
* (works in environments that block WebSocket; configurable timeouts and
|
|
799
|
+
* per-transaction callbacks).
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* Using the interface polymorphically:
|
|
803
|
+
* ```typescript
|
|
804
|
+
* import type { TransactionForwarder } from "./interfaces";
|
|
805
|
+
*
|
|
806
|
+
* async function submitOperations(
|
|
807
|
+
* forwarder: TransactionForwarder,
|
|
808
|
+
* initTx: SignedTransaction,
|
|
809
|
+
* operationTxs: readonly SignedTransaction[],
|
|
810
|
+
* ) {
|
|
811
|
+
* // Init must land before operations can reference the new account
|
|
812
|
+
* await forwarder.forwardSequentially([initTx]);
|
|
813
|
+
*
|
|
814
|
+
* // Operations are independent of each other
|
|
815
|
+
* const sigs = await forwarder.forwardInParallel(operationTxs);
|
|
816
|
+
* return sigs;
|
|
817
|
+
* }
|
|
818
|
+
* ```
|
|
819
|
+
*
|
|
820
|
+
* @see {@link ForwardSequentially} for the sequential method type
|
|
821
|
+
* @see {@link ForwardInParallel} for the parallel method type
|
|
822
|
+
* @see {@link getWebsocketTransactionForwarder} for the WebSocket implementation
|
|
823
|
+
* @see {@link getPollingTransactionForwarder} for the polling implementation
|
|
824
|
+
* @public
|
|
825
|
+
*/
|
|
826
|
+
interface TransactionForwarder {
|
|
827
|
+
/**
|
|
828
|
+
* Forwards transactions sequentially.
|
|
829
|
+
*
|
|
830
|
+
* Each transaction is submitted and confirmed before the next transaction
|
|
831
|
+
* is sent. Aborts on the first failure.
|
|
832
|
+
*
|
|
833
|
+
* @see {@link ForwardSequentially}
|
|
834
|
+
*/
|
|
835
|
+
forwardSequentially: ForwardSequentially;
|
|
836
|
+
/**
|
|
837
|
+
* Forwards transactions in parallel.
|
|
838
|
+
*
|
|
839
|
+
* All transactions are submitted simultaneously. Confirmations are awaited
|
|
840
|
+
* concurrently. Results are returned in input order regardless of confirmation
|
|
841
|
+
* order.
|
|
842
|
+
*
|
|
843
|
+
* @see {@link ForwardInParallel}
|
|
844
|
+
*/
|
|
845
|
+
forwardInParallel: ForwardInParallel;
|
|
846
|
+
/**
|
|
847
|
+
* Sends a transaction without waiting for confirmation.
|
|
848
|
+
*
|
|
849
|
+
* Use for best-effort operations like rent reclamation where blocking
|
|
850
|
+
* on confirmation is unnecessary.
|
|
851
|
+
*/
|
|
852
|
+
fireAndForget: FireAndForget;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Sends a single signed transaction to the network without awaiting confirmation.
|
|
856
|
+
* Returns the signature immediately after submission.
|
|
857
|
+
*/
|
|
858
|
+
type FireAndForget = (transaction: SignedTransaction) => Promise<TransactionSignature>;
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Arcium Computation Monitor Implementations.
|
|
862
|
+
*
|
|
863
|
+
* Monitors an Arcium `ComputationAccount` for finalization or pruning after
|
|
864
|
+
* a `queue_computation` transaction has been sent. The monitor uses **slot-based
|
|
865
|
+
* timeout**: it reads the `slot` field from the on-chain `ComputationAccount`
|
|
866
|
+
* (the slot when the computation was queued) and compares against the current
|
|
867
|
+
* slot to determine if the computation has been pruned.
|
|
868
|
+
*
|
|
869
|
+
* Two factory functions produce {@link ComputationMonitor} implementations:
|
|
870
|
+
*
|
|
871
|
+
* - **{@link getWebsocketComputationMonitor}** — Subscribes to account change
|
|
872
|
+
* notifications via WebSocket for low-latency finalization detection.
|
|
873
|
+
*
|
|
874
|
+
* - **{@link getPollingComputationMonitor}** — Periodically fetches the
|
|
875
|
+
* ComputationAccount via HTTP. Suitable for environments without WebSocket.
|
|
876
|
+
*
|
|
877
|
+
* ## Outcomes
|
|
878
|
+
*
|
|
879
|
+
* - **Finalized** — The MPC callback landed successfully. The `ComputationAccount.status`
|
|
880
|
+
* transitioned to `Finalized` within the slot window.
|
|
881
|
+
* - **Pruned** — The slot window elapsed without finalization. The MPC nodes either
|
|
882
|
+
* failed to complete or all callback attempts failed. The computation will not
|
|
883
|
+
* finalize and should be considered abandoned.
|
|
884
|
+
*
|
|
885
|
+
* ## Slot-Based Timeout
|
|
886
|
+
*
|
|
887
|
+
* Arcium MPC callbacks typically arrive within ~180 slots (~72 seconds) of queueing.
|
|
888
|
+
* The monitor uses a configurable slot window ({@link DEFAULT_MAX_SLOT_WINDOW} = 200)
|
|
889
|
+
* measured from the `ComputationAccount.slot` field. A wall-clock safety timeout
|
|
890
|
+
* ({@link DEFAULT_SAFETY_TIMEOUT_MS} = 300s) provides a fallback if RPC is unresponsive.
|
|
891
|
+
*
|
|
892
|
+
* @packageDocumentation
|
|
893
|
+
* @module arcium/computation-monitor
|
|
894
|
+
* @since 2.0.0
|
|
895
|
+
*/
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Default maximum slot window. If `currentSlot - queuedSlot` exceeds this
|
|
899
|
+
* value, the computation is considered pruned.
|
|
900
|
+
*
|
|
901
|
+
* @remarks
|
|
902
|
+
* Arcium callbacks typically arrive within ~180 slots (~72 seconds).
|
|
903
|
+
* The default of 200 provides a ~20-slot buffer.
|
|
904
|
+
*
|
|
905
|
+
* @since 2.0.0
|
|
906
|
+
* @public
|
|
907
|
+
*/
|
|
908
|
+
declare const DEFAULT_MAX_SLOT_WINDOW = 200;
|
|
909
|
+
/**
|
|
910
|
+
* Default wall-clock safety timeout in milliseconds. This is a fallback
|
|
911
|
+
* for cases where the RPC is unresponsive and slot progression cannot be
|
|
912
|
+
* tracked. Under normal conditions, the slot-based window resolves first.
|
|
913
|
+
*
|
|
914
|
+
* @since 2.0.0
|
|
915
|
+
* @public
|
|
916
|
+
*/
|
|
917
|
+
declare const DEFAULT_SAFETY_TIMEOUT_MS = 300000;
|
|
918
|
+
/**
|
|
919
|
+
* Default interval in milliseconds between successive poll attempts.
|
|
920
|
+
*
|
|
921
|
+
* @since 2.0.0
|
|
922
|
+
* @public
|
|
923
|
+
*/
|
|
924
|
+
declare const DEFAULT_POLLING_INTERVAL_MS = 2000;
|
|
925
|
+
/**
|
|
926
|
+
* Default limit for the `getSignaturesForAddress` RPC call when retrieving
|
|
927
|
+
* the callback transaction signature after finalization.
|
|
928
|
+
*
|
|
929
|
+
* @since 2.0.0
|
|
930
|
+
* @public
|
|
931
|
+
*/
|
|
932
|
+
declare const DEFAULT_SIGNATURE_RETRIEVAL_LIMIT = 20;
|
|
933
|
+
/**
|
|
934
|
+
* Result when the MPC computation finalizes successfully.
|
|
935
|
+
*
|
|
936
|
+
* @since 2.0.0
|
|
937
|
+
* @public
|
|
938
|
+
*/
|
|
939
|
+
interface ComputationFinalizedResult {
|
|
940
|
+
/** The computation completed and the callback landed on-chain. */
|
|
941
|
+
readonly status: "finalized";
|
|
942
|
+
/** Wall-clock milliseconds from monitor start to finalization detection. */
|
|
943
|
+
readonly elapsedMs: number;
|
|
944
|
+
/** The slot at which the computation was originally queued. */
|
|
945
|
+
readonly queuedSlot: bigint;
|
|
946
|
+
/**
|
|
947
|
+
* The callback transaction signature, if `retrieveCallbackSignature` was `true`.
|
|
948
|
+
* `undefined` otherwise.
|
|
949
|
+
*/
|
|
950
|
+
readonly callbackSignature?: TransactionSignature;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Result when the computation is pruned (no successful callback within the slot window).
|
|
954
|
+
*
|
|
955
|
+
* @since 2.0.0
|
|
956
|
+
* @public
|
|
957
|
+
*/
|
|
958
|
+
interface ComputationPrunedResult {
|
|
959
|
+
/** The slot window elapsed without a successful callback. */
|
|
960
|
+
readonly status: "pruned";
|
|
961
|
+
/** Wall-clock milliseconds from monitor start to pruning detection. */
|
|
962
|
+
readonly elapsedMs: number;
|
|
963
|
+
/** The slot at which the computation was originally queued. */
|
|
964
|
+
readonly queuedSlot: bigint;
|
|
965
|
+
/** The current slot at the time pruning was detected. */
|
|
966
|
+
readonly currentSlot: bigint;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Discriminated union of all computation monitoring outcomes.
|
|
970
|
+
*
|
|
971
|
+
* @remarks
|
|
972
|
+
* Callers must check `result.status` to determine the outcome:
|
|
973
|
+
*
|
|
974
|
+
* ```typescript
|
|
975
|
+
* const result = await monitor.awaitComputation(address);
|
|
976
|
+
* if (result.status === "finalized") {
|
|
977
|
+
* // MPC succeeded — continue with the encrypted result
|
|
978
|
+
* } else {
|
|
979
|
+
* // Pruned — the computation will never finalize
|
|
980
|
+
* console.log(`Pruned: ${result.currentSlot - result.queuedSlot} slots elapsed`);
|
|
981
|
+
* }
|
|
982
|
+
* ```
|
|
983
|
+
*
|
|
984
|
+
* @since 2.0.0
|
|
985
|
+
* @public
|
|
986
|
+
*/
|
|
987
|
+
type ComputationMonitorResult = ComputationFinalizedResult | ComputationPrunedResult;
|
|
988
|
+
/**
|
|
989
|
+
* Extended finalized result with a guaranteed callback signature.
|
|
990
|
+
* Returned when `retrieveCallbackSignature` is `true` and the computation finalized.
|
|
991
|
+
*
|
|
992
|
+
* @since 2.0.0
|
|
993
|
+
* @public
|
|
994
|
+
*/
|
|
995
|
+
interface ComputationFinalizedResultWithSignature extends Omit<ComputationFinalizedResult, "callbackSignature"> {
|
|
996
|
+
/** The base58-encoded transaction signature of the successful Arcium callback. */
|
|
997
|
+
readonly callbackSignature: TransactionSignature;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Result type when `retrieveCallbackSignature` is `true`.
|
|
1001
|
+
* If finalized, the callback signature is guaranteed present.
|
|
1002
|
+
* If pruned, no signature is available.
|
|
1003
|
+
*
|
|
1004
|
+
* @since 2.0.0
|
|
1005
|
+
* @public
|
|
1006
|
+
*/
|
|
1007
|
+
type ComputationMonitorResultWithSignature = ComputationFinalizedResultWithSignature | ComputationPrunedResult;
|
|
1008
|
+
/**
|
|
1009
|
+
* Progress event types fired via the `onProgress` callback.
|
|
1010
|
+
*
|
|
1011
|
+
* @remarks
|
|
1012
|
+
* - `queued` — the ComputationAccount was found with status `Queued`
|
|
1013
|
+
* - `finalized` — the computation status transitioned to `Finalized`
|
|
1014
|
+
* - `pruned` — the slot window elapsed without finalization
|
|
1015
|
+
*
|
|
1016
|
+
* @since 2.0.0
|
|
1017
|
+
* @public
|
|
1018
|
+
*/
|
|
1019
|
+
type ComputationMonitorProgressEvent = {
|
|
1020
|
+
readonly type: "queued";
|
|
1021
|
+
readonly queuedSlot: bigint;
|
|
1022
|
+
} | {
|
|
1023
|
+
readonly type: "finalized";
|
|
1024
|
+
readonly callbackSignature?: TransactionSignature;
|
|
1025
|
+
} | {
|
|
1026
|
+
readonly type: "pruned";
|
|
1027
|
+
readonly queuedSlot: bigint;
|
|
1028
|
+
readonly currentSlot: bigint;
|
|
1029
|
+
};
|
|
1030
|
+
/**
|
|
1031
|
+
* Options for a computation monitoring invocation.
|
|
1032
|
+
*
|
|
1033
|
+
* @since 2.0.0
|
|
1034
|
+
* @public
|
|
1035
|
+
*/
|
|
1036
|
+
interface ComputationMonitorOptions {
|
|
1037
|
+
/**
|
|
1038
|
+
* Maximum number of slots after the queued slot before the computation
|
|
1039
|
+
* is considered pruned. The monitor reads the `slot` field from the
|
|
1040
|
+
* `ComputationAccount` and compares against the current on-chain slot.
|
|
1041
|
+
*
|
|
1042
|
+
* @defaultValue {@link DEFAULT_MAX_SLOT_WINDOW} (200 slots ≈ 80 seconds)
|
|
1043
|
+
*/
|
|
1044
|
+
readonly maxSlotWindow?: number;
|
|
1045
|
+
/**
|
|
1046
|
+
* Wall-clock safety timeout in milliseconds. This is a fallback that fires
|
|
1047
|
+
* only if slot progression cannot be tracked (e.g. RPC is unresponsive).
|
|
1048
|
+
* Under normal conditions, the slot-based window resolves first.
|
|
1049
|
+
*
|
|
1050
|
+
* @remarks
|
|
1051
|
+
* When this fires, a {@link ComputationMonitorError} with stage `"timeout"`
|
|
1052
|
+
* is thrown. This indicates an infrastructure problem, not a pruned computation.
|
|
1053
|
+
*
|
|
1054
|
+
* @defaultValue {@link DEFAULT_SAFETY_TIMEOUT_MS} (300,000 ms / 5 minutes)
|
|
1055
|
+
*/
|
|
1056
|
+
readonly safetyTimeoutMs?: number;
|
|
1057
|
+
/**
|
|
1058
|
+
* If `true`, perform a `getSignaturesForAddress` call after finalization
|
|
1059
|
+
* to retrieve the successful callback transaction signature.
|
|
1060
|
+
* Has no effect if the computation is pruned.
|
|
1061
|
+
*
|
|
1062
|
+
* @defaultValue `false`
|
|
1063
|
+
*/
|
|
1064
|
+
readonly retrieveCallbackSignature?: boolean;
|
|
1065
|
+
/**
|
|
1066
|
+
* Progress callback fired as status changes occur.
|
|
1067
|
+
*
|
|
1068
|
+
* @remarks
|
|
1069
|
+
* This callback is synchronous and should not perform blocking operations.
|
|
1070
|
+
* It is informational and does not affect the monitoring loop.
|
|
1071
|
+
*/
|
|
1072
|
+
readonly onProgress?: (event: ComputationMonitorProgressEvent) => void;
|
|
1073
|
+
/**
|
|
1074
|
+
* Commitment level for account fetches and subscriptions.
|
|
1075
|
+
*
|
|
1076
|
+
* @defaultValue `"confirmed"`
|
|
1077
|
+
*/
|
|
1078
|
+
readonly commitment?: Commitment;
|
|
1079
|
+
/**
|
|
1080
|
+
* An `AbortSignal` for external cancellation of the monitoring operation.
|
|
1081
|
+
*
|
|
1082
|
+
* @remarks
|
|
1083
|
+
* When aborted, the monitor throws a {@link ComputationMonitorError} with
|
|
1084
|
+
* stage `"timeout"`. This enables callers to cancel from external code
|
|
1085
|
+
* (user navigation, component unmount).
|
|
1086
|
+
*/
|
|
1087
|
+
readonly signal?: AbortSignal;
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Polling-specific options extending the base monitor options.
|
|
1091
|
+
*
|
|
1092
|
+
* @since 2.0.0
|
|
1093
|
+
* @public
|
|
1094
|
+
*/
|
|
1095
|
+
interface PollingComputationMonitorOptions extends ComputationMonitorOptions {
|
|
1096
|
+
/**
|
|
1097
|
+
* Time in milliseconds between successive account fetch attempts.
|
|
1098
|
+
*
|
|
1099
|
+
* @defaultValue {@link DEFAULT_POLLING_INTERVAL_MS} (2,000 ms)
|
|
1100
|
+
*/
|
|
1101
|
+
readonly pollingIntervalMs?: number;
|
|
1102
|
+
}
|
|
1103
|
+
/**
|
|
1104
|
+
* Monitors an Arcium `ComputationAccount` for finalization or pruning.
|
|
1105
|
+
*
|
|
1106
|
+
* @since 2.0.0
|
|
1107
|
+
* @public
|
|
1108
|
+
*/
|
|
1109
|
+
interface ComputationMonitor {
|
|
1110
|
+
/**
|
|
1111
|
+
* Monitors a ComputationAccount until it finalizes or is pruned.
|
|
1112
|
+
*
|
|
1113
|
+
* @param computationAddress - The on-chain address of the ComputationAccount PDA.
|
|
1114
|
+
* @param options - Monitor options.
|
|
1115
|
+
* @returns A promise resolving to a finalized or pruned result.
|
|
1116
|
+
*
|
|
1117
|
+
* @throws {ComputationMonitorError} With stage `"timeout"` if the safety wall-clock
|
|
1118
|
+
* timeout fires (indicates RPC issues, not normal pruning).
|
|
1119
|
+
* @throws {ComputationMonitorError} With stage `"subscription"` if WebSocket fails.
|
|
1120
|
+
* @throws {ComputationMonitorError} With stage `"signature-retrieval"` if callback
|
|
1121
|
+
* signature lookup fails after finalization.
|
|
1122
|
+
*/
|
|
1123
|
+
awaitComputation: {
|
|
1124
|
+
(computationAddress: Address, options?: ComputationMonitorOptions & {
|
|
1125
|
+
retrieveCallbackSignature?: false;
|
|
1126
|
+
}): Promise<ComputationMonitorResult>;
|
|
1127
|
+
(computationAddress: Address, options: ComputationMonitorOptions & {
|
|
1128
|
+
retrieveCallbackSignature: true;
|
|
1129
|
+
}): Promise<ComputationMonitorResultWithSignature>;
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Configuration for the WebSocket-based computation monitor.
|
|
1134
|
+
*
|
|
1135
|
+
* @since 2.0.0
|
|
1136
|
+
* @public
|
|
1137
|
+
*/
|
|
1138
|
+
interface WebsocketBasedComputationMonitorConfig {
|
|
1139
|
+
/** HTTP/HTTPS URL of the Solana JSON-RPC endpoint. */
|
|
1140
|
+
readonly rpcUrl: string;
|
|
1141
|
+
/** WebSocket URL of the Solana RPC subscriptions endpoint. */
|
|
1142
|
+
readonly rpcSubscriptionsUrl: string;
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Optional dependencies for the WebSocket-based computation monitor.
|
|
1146
|
+
*
|
|
1147
|
+
* @since 2.0.0
|
|
1148
|
+
* @public
|
|
1149
|
+
*/
|
|
1150
|
+
interface WebsocketBasedComputationMonitorDeps {
|
|
1151
|
+
/** Override `createSolanaRpc` from `@solana/kit`. */
|
|
1152
|
+
readonly createRpc?: CreateSolanaRpcFunction;
|
|
1153
|
+
/** Override `createSolanaRpcSubscriptions` from `@solana/kit`. */
|
|
1154
|
+
readonly createRpcSubscriptions?: CreateSolanaRpcSubscriptionsFunction;
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Configuration for the polling-based computation monitor.
|
|
1158
|
+
*
|
|
1159
|
+
* @since 2.0.0
|
|
1160
|
+
* @public
|
|
1161
|
+
*/
|
|
1162
|
+
interface PollingBasedComputationMonitorConfig {
|
|
1163
|
+
/** HTTP/HTTPS URL of the Solana JSON-RPC endpoint. */
|
|
1164
|
+
readonly rpcUrl: string;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Optional dependencies for the polling-based computation monitor.
|
|
1168
|
+
*
|
|
1169
|
+
* @since 2.0.0
|
|
1170
|
+
* @public
|
|
1171
|
+
*/
|
|
1172
|
+
interface PollingBasedComputationMonitorDeps {
|
|
1173
|
+
/** Override `createSolanaRpc` from `@solana/kit`. */
|
|
1174
|
+
readonly createRpc?: CreateSolanaRpcFunction;
|
|
1175
|
+
}
|
|
1176
|
+
/**
|
|
1177
|
+
* Creates a {@link ComputationMonitor} that uses WebSocket account change
|
|
1178
|
+
* subscriptions for real-time detection of computation finalization or pruning.
|
|
1179
|
+
*
|
|
1180
|
+
* @remarks
|
|
1181
|
+
* **Detection strategy:** Subscribes to `accountNotifications` for the
|
|
1182
|
+
* computation account. On each notification, decodes the account and checks
|
|
1183
|
+
* the `status` field. Periodically fetches the current slot to detect pruning
|
|
1184
|
+
* when the slot window is exceeded.
|
|
1185
|
+
*
|
|
1186
|
+
* **Initial state check:** Before subscribing, performs an HTTP fetch to handle
|
|
1187
|
+
* the case where the computation already finalized before the monitor started.
|
|
1188
|
+
*
|
|
1189
|
+
* @param config - HTTP RPC URL and WebSocket URL.
|
|
1190
|
+
* @param deps - Optional dependency overrides for testing.
|
|
1191
|
+
* @returns A {@link ComputationMonitor} instance.
|
|
1192
|
+
*
|
|
1193
|
+
* @throws {ComputationMonitorError} Stage `"timeout"` if safety timeout fires.
|
|
1194
|
+
* @throws {ComputationMonitorError} Stage `"subscription"` if WebSocket fails.
|
|
1195
|
+
* @throws {ComputationMonitorError} Stage `"signature-retrieval"` if lookup fails.
|
|
1196
|
+
*
|
|
1197
|
+
* @example
|
|
1198
|
+
* ```typescript
|
|
1199
|
+
* const monitor = getWebsocketComputationMonitor({
|
|
1200
|
+
* rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
1201
|
+
* rpcSubscriptionsUrl: "wss://api.mainnet-beta.solana.com",
|
|
1202
|
+
* });
|
|
1203
|
+
*
|
|
1204
|
+
* const result = await monitor.awaitComputation(computationAddress);
|
|
1205
|
+
* if (result.status === "finalized") {
|
|
1206
|
+
* console.log(`Done in ${result.elapsedMs}ms`);
|
|
1207
|
+
* } else {
|
|
1208
|
+
* console.log(`Pruned after ${result.currentSlot - result.queuedSlot} slots`);
|
|
1209
|
+
* }
|
|
1210
|
+
* ```
|
|
1211
|
+
*
|
|
1212
|
+
* @see {@link getPollingComputationMonitor} for the polling alternative
|
|
1213
|
+
* @since 2.0.0
|
|
1214
|
+
* @public
|
|
1215
|
+
*/
|
|
1216
|
+
declare function getWebsocketComputationMonitor(config: WebsocketBasedComputationMonitorConfig, deps?: WebsocketBasedComputationMonitorDeps): ComputationMonitor;
|
|
1217
|
+
/**
|
|
1218
|
+
* Creates a {@link ComputationMonitor} that uses periodic HTTP account fetching
|
|
1219
|
+
* for detection of computation finalization or pruning.
|
|
1220
|
+
*
|
|
1221
|
+
* @remarks
|
|
1222
|
+
* Each poll iteration fetches the `ComputationAccount` and the current slot
|
|
1223
|
+
* in parallel. If the account is finalized, the monitor resolves. If the slot
|
|
1224
|
+
* window is exceeded, the monitor resolves with a pruned result.
|
|
1225
|
+
*
|
|
1226
|
+
* **Transient error handling** — RPC fetch errors during polling are silently
|
|
1227
|
+
* swallowed and the poll loop continues.
|
|
1228
|
+
*
|
|
1229
|
+
* @param config - HTTP RPC endpoint URL.
|
|
1230
|
+
* @param deps - Optional dependency overrides for testing.
|
|
1231
|
+
* @returns A {@link ComputationMonitor} instance.
|
|
1232
|
+
*
|
|
1233
|
+
* @throws {ComputationMonitorError} Stage `"timeout"` if safety timeout fires.
|
|
1234
|
+
* @throws {ComputationMonitorError} Stage `"signature-retrieval"` if lookup fails.
|
|
1235
|
+
*
|
|
1236
|
+
* @example
|
|
1237
|
+
* ```typescript
|
|
1238
|
+
* const monitor = getPollingComputationMonitor({
|
|
1239
|
+
* rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
1240
|
+
* });
|
|
1241
|
+
*
|
|
1242
|
+
* const result = await monitor.awaitComputation(computationAddress, {
|
|
1243
|
+
* maxSlotWindow: 250,
|
|
1244
|
+
* pollingIntervalMs: 3_000,
|
|
1245
|
+
* retrieveCallbackSignature: true,
|
|
1246
|
+
* });
|
|
1247
|
+
*
|
|
1248
|
+
* if (result.status === "finalized") {
|
|
1249
|
+
* console.log(`Callback: ${result.callbackSignature}`);
|
|
1250
|
+
* }
|
|
1251
|
+
* ```
|
|
1252
|
+
*
|
|
1253
|
+
* @see {@link getWebsocketComputationMonitor} for the WebSocket alternative
|
|
1254
|
+
* @since 2.0.0
|
|
1255
|
+
* @public
|
|
1256
|
+
*/
|
|
1257
|
+
declare function getPollingComputationMonitor(config: PollingBasedComputationMonitorConfig, deps?: PollingBasedComputationMonitorDeps): ComputationMonitor;
|
|
1258
|
+
|
|
1259
|
+
/**
|
|
1260
|
+
* Umbra Interfaces — UTXO indexer interfaces for Merkle proofs and UTXO scanning.
|
|
1261
|
+
*
|
|
1262
|
+
* @module umbra/interfaces/indexer
|
|
1263
|
+
* @since 2.0.0
|
|
1264
|
+
*/
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Base interface for Umbra Indexer configuration.
|
|
1268
|
+
*
|
|
1269
|
+
* @remarks
|
|
1270
|
+
* Contains the essential configuration needed to connect to an Umbra indexer service.
|
|
1271
|
+
* The indexer maintains an off-chain database of protocol state for efficient querying.
|
|
1272
|
+
*
|
|
1273
|
+
* **Indexer Selection:**
|
|
1274
|
+
* Multiple indexer providers may exist. Choose one based on:
|
|
1275
|
+
* - Geographic proximity (lower latency)
|
|
1276
|
+
* - Uptime and reliability
|
|
1277
|
+
* - Query performance
|
|
1278
|
+
*
|
|
1279
|
+
* @example
|
|
1280
|
+
* ```typescript
|
|
1281
|
+
* const indexer: IUmbraIndexer = {
|
|
1282
|
+
* apiEndpoint: 'https://indexer.umbra.finance'
|
|
1283
|
+
* };
|
|
1284
|
+
* ```
|
|
1285
|
+
*
|
|
1286
|
+
* @public
|
|
1287
|
+
*/
|
|
1288
|
+
interface IUmbraIndexer {
|
|
1289
|
+
/**
|
|
1290
|
+
* The HTTP endpoint of the indexer service.
|
|
1291
|
+
*
|
|
1292
|
+
* @remarks
|
|
1293
|
+
* This should be a fully qualified URL including the protocol.
|
|
1294
|
+
* The endpoint must support CORS for browser-based clients.
|
|
1295
|
+
*
|
|
1296
|
+
* @example "https://indexer.umbra.finance"
|
|
1297
|
+
*/
|
|
1298
|
+
apiEndpoint: string;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Timestamp components returned by the indexer.
|
|
1302
|
+
*
|
|
1303
|
+
* @remarks
|
|
1304
|
+
* Represents the decomposed timestamp of when the UTXO was created.
|
|
1305
|
+
* Used for transaction viewing key derivation.
|
|
1306
|
+
*
|
|
1307
|
+
* @see {@link H1Components}
|
|
1308
|
+
* @public
|
|
1309
|
+
*/
|
|
1310
|
+
interface TimestampComponents {
|
|
1311
|
+
/** Year (e.g., 2024) */
|
|
1312
|
+
year: Year;
|
|
1313
|
+
/** Month (1-12) */
|
|
1314
|
+
month: Month;
|
|
1315
|
+
/** Day (1-31) */
|
|
1316
|
+
day: Day;
|
|
1317
|
+
/** Hour (0-23) */
|
|
1318
|
+
hour: Hour;
|
|
1319
|
+
/** Minute (0-59) */
|
|
1320
|
+
minute: Minute;
|
|
1321
|
+
/** Second (0-59) */
|
|
1322
|
+
second: Second;
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* H1 components returned by the indexer for UTXO data reconstruction.
|
|
1326
|
+
*
|
|
1327
|
+
* @remarks
|
|
1328
|
+
* These are the public components needed to reconstruct the H1 hash
|
|
1329
|
+
* during claim operations. H1 contains metadata about the deposit
|
|
1330
|
+
* that is public knowledge.
|
|
1331
|
+
*
|
|
1332
|
+
* @see {@link UtxoDataItem}
|
|
1333
|
+
* @public
|
|
1334
|
+
*/
|
|
1335
|
+
interface H1Components {
|
|
1336
|
+
/** Protocol version identifier */
|
|
1337
|
+
version: U64;
|
|
1338
|
+
/** Index of the commitment in the Merkle tree */
|
|
1339
|
+
commitmentIndex: U128;
|
|
1340
|
+
/** Lower 128 bits of the sender's address */
|
|
1341
|
+
senderAddressLow: U128;
|
|
1342
|
+
/** Upper 128 bits of the sender's address */
|
|
1343
|
+
senderAddressHigh: U128;
|
|
1344
|
+
/** Fixed SOL fees paid to relayer (in lamports) */
|
|
1345
|
+
relayerFixedSolFees: U64;
|
|
1346
|
+
/** Lower 128 bits of the mint address */
|
|
1347
|
+
mintAddressLow: U128;
|
|
1348
|
+
/** Upper 128 bits of the mint address */
|
|
1349
|
+
mintAddressHigh: U128;
|
|
1350
|
+
/** Timestamp components for TVK derivation */
|
|
1351
|
+
timestamp: TimestampComponents;
|
|
1352
|
+
/** Cumulative SPL token volume in the pool at insertion time (base units) */
|
|
1353
|
+
poolVolumeSpl: U64;
|
|
1354
|
+
/** Cumulative SOL volume in the pool at insertion time (lamports) */
|
|
1355
|
+
poolVolumeSol: U64;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Data returned from the Merkle proof endpoint.
|
|
1359
|
+
*
|
|
1360
|
+
* @remarks
|
|
1361
|
+
* Contains the Merkle root, proof path (sibling hashes), and the leaf value
|
|
1362
|
+
* for a specific insertion in the Merkle tree.
|
|
1363
|
+
*
|
|
1364
|
+
* **Hash Encoding:**
|
|
1365
|
+
* All hashes are little-endian byte arrays (U256LeBytes).
|
|
1366
|
+
* The API returns hex strings with `0x` prefix which are converted.
|
|
1367
|
+
*
|
|
1368
|
+
* @see {@link MerkleProofFetcherFunction}
|
|
1369
|
+
* @public
|
|
1370
|
+
*/
|
|
1371
|
+
interface MerkleProofData {
|
|
1372
|
+
/** The Merkle root at the time of the query */
|
|
1373
|
+
merkleRoot: U256LeBytes;
|
|
1374
|
+
/** The tree index containing this leaf */
|
|
1375
|
+
treeIndex: U32;
|
|
1376
|
+
/** The insertion index within the tree */
|
|
1377
|
+
insertionIndex: U32;
|
|
1378
|
+
/** Array of sibling hashes from leaf to root */
|
|
1379
|
+
merklePath: U256LeBytes[];
|
|
1380
|
+
/** The leaf node value (commitment) */
|
|
1381
|
+
leaf: U256LeBytes;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Function type for fetching Merkle proofs from the indexer.
|
|
1385
|
+
*
|
|
1386
|
+
* @remarks
|
|
1387
|
+
* Fetches Merkle proofs for one or more leaves in a specific tree.
|
|
1388
|
+
* The returned Map is keyed by insertion index for easy lookup.
|
|
1389
|
+
*
|
|
1390
|
+
* @param treeIndex - The Merkle tree index to query
|
|
1391
|
+
* @param insertionIndices - Array of insertion indices to fetch proofs for
|
|
1392
|
+
* @returns A Map from insertion index to its Merkle proof data
|
|
1393
|
+
*
|
|
1394
|
+
* @example
|
|
1395
|
+
* ```typescript
|
|
1396
|
+
* const proofs = await fetchMerkleProof(0, [42, 43, 44]);
|
|
1397
|
+
* const proof = proofs.get(42);
|
|
1398
|
+
* if (proof) {
|
|
1399
|
+
* console.log('Root:', proof.merkleRoot);
|
|
1400
|
+
* console.log('Path length:', proof.merklePath.length);
|
|
1401
|
+
* }
|
|
1402
|
+
* ```
|
|
1403
|
+
*
|
|
1404
|
+
* @see {@link MerkleProofData}
|
|
1405
|
+
* @public
|
|
1406
|
+
*/
|
|
1407
|
+
type MerkleProofFetcherFunction = (treeIndex: U32, insertionIndices: readonly U32[]) => Promise<Map<U32, MerkleProofData>>;
|
|
1408
|
+
/**
|
|
1409
|
+
* Arguments for creating a Merkle proof fetcher function.
|
|
1410
|
+
*
|
|
1411
|
+
* @example
|
|
1412
|
+
* ```typescript
|
|
1413
|
+
* const args: GetMerkleProofFetcherArgs = {
|
|
1414
|
+
* apiEndpoint: 'https://indexer.umbra.finance'
|
|
1415
|
+
* };
|
|
1416
|
+
* ```
|
|
1417
|
+
*
|
|
1418
|
+
* @see {@link MerkleProofFetcherFunction}
|
|
1419
|
+
* @public
|
|
1420
|
+
*/
|
|
1421
|
+
interface GetMerkleProofFetcherArgs {
|
|
1422
|
+
/** The HTTP endpoint of the indexer service */
|
|
1423
|
+
apiEndpoint: string;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Optional dependencies for the Merkle proof fetcher factory.
|
|
1427
|
+
*
|
|
1428
|
+
* @remarks
|
|
1429
|
+
* Allows injection of a custom fetch implementation for testing
|
|
1430
|
+
* or environments without global fetch.
|
|
1431
|
+
*
|
|
1432
|
+
* @see {@link MerkleProofFetcherFunction}
|
|
1433
|
+
* @public
|
|
1434
|
+
*/
|
|
1435
|
+
interface GetMerkleProofFetcherDeps {
|
|
1436
|
+
/**
|
|
1437
|
+
* Custom fetch implementation.
|
|
1438
|
+
* @default globalThis.fetch
|
|
1439
|
+
*/
|
|
1440
|
+
fetch?: typeof globalThis.fetch;
|
|
1441
|
+
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Individual UTXO data item from the indexer API.
|
|
1444
|
+
*
|
|
1445
|
+
* @remarks
|
|
1446
|
+
* Contains all the data needed to reconstruct and claim a UTXO,
|
|
1447
|
+
* including H1/H2 hashes, encrypted data, and metadata.
|
|
1448
|
+
*
|
|
1449
|
+
* **Absolute Index Calculation:**
|
|
1450
|
+
* ```
|
|
1451
|
+
* absoluteIndex = treeIndex * 1,048,576 + insertionIndex
|
|
1452
|
+
* ```
|
|
1453
|
+
*
|
|
1454
|
+
* @see {@link UtxoDataFetcherFunction}
|
|
1455
|
+
* @see {@link H1Components}
|
|
1456
|
+
* @public
|
|
1457
|
+
*/
|
|
1458
|
+
interface UtxoDataItem {
|
|
1459
|
+
/** Absolute index in the global UTXO sequence */
|
|
1460
|
+
absoluteIndex: bigint;
|
|
1461
|
+
/** Tree index containing this UTXO */
|
|
1462
|
+
treeIndex: U32;
|
|
1463
|
+
/** Insertion index within the tree */
|
|
1464
|
+
insertionIndex: U32;
|
|
1465
|
+
/** Final commitment h(h1_hash, h2_hash) */
|
|
1466
|
+
finalCommitment: U256LeBytes;
|
|
1467
|
+
/** H1 components for claim proof generation */
|
|
1468
|
+
h1Components: H1Components;
|
|
1469
|
+
/** H1 hash value */
|
|
1470
|
+
h1Hash: U256LeBytes;
|
|
1471
|
+
/** H2 hash value */
|
|
1472
|
+
h2Hash: U256LeBytes;
|
|
1473
|
+
/** AES encrypted UTXO data (amount, destination, gen index, domain separator) */
|
|
1474
|
+
aesEncryptedData: AesCiphertextWithMetadata;
|
|
1475
|
+
/** Depositor's X25519 public key for decryption */
|
|
1476
|
+
depositorX25519PublicKey: X25519PublicKey;
|
|
1477
|
+
/** Block timestamp (Unix epoch seconds) */
|
|
1478
|
+
timestamp: U64;
|
|
1479
|
+
/** Solana slot number */
|
|
1480
|
+
slot: U64;
|
|
1481
|
+
/** Event type: 'deposit' or 'callback' */
|
|
1482
|
+
eventType: "deposit" | "callback";
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Paginated result from the UTXO data endpoint.
|
|
1486
|
+
*
|
|
1487
|
+
* @remarks
|
|
1488
|
+
* Contains the fetched UTXO items along with pagination metadata
|
|
1489
|
+
* for iterating through large result sets.
|
|
1490
|
+
*
|
|
1491
|
+
* @see {@link UtxoDataFetcherFunction}
|
|
1492
|
+
* @see {@link UtxoDataItem}
|
|
1493
|
+
* @public
|
|
1494
|
+
*/
|
|
1495
|
+
interface UtxoFetchResult {
|
|
1496
|
+
/** Map of insertion index to UTXO data */
|
|
1497
|
+
items: Map<U32, UtxoDataItem>;
|
|
1498
|
+
/** Whether there are more results available */
|
|
1499
|
+
hasMore: boolean;
|
|
1500
|
+
/** Next cursor to continue pagination (use as startIndex for next request) */
|
|
1501
|
+
nextCursor: bigint | undefined;
|
|
1502
|
+
/** Total count of records in the requested range */
|
|
1503
|
+
totalCount: U64;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Function type for fetching UTXO data from the indexer.
|
|
1507
|
+
*
|
|
1508
|
+
* @remarks
|
|
1509
|
+
* Fetches UTXO data by absolute index range with pagination support.
|
|
1510
|
+
*
|
|
1511
|
+
* **Pagination:**
|
|
1512
|
+
* - Use `startIndex` and optional `endIndex` to define the range
|
|
1513
|
+
* - Use `limit` to control page size (default: 1000, max: 5000)
|
|
1514
|
+
* - Use `nextCursor` from result as `startIndex` for next page
|
|
1515
|
+
*
|
|
1516
|
+
* @param startIndex - Starting absolute index (inclusive)
|
|
1517
|
+
* @param endIndex - Optional ending absolute index (inclusive)
|
|
1518
|
+
* @param limit - Maximum number of records to return (default: 1000, max: 5000)
|
|
1519
|
+
* @returns Paginated result with UTXO items and metadata
|
|
1520
|
+
*
|
|
1521
|
+
* @example
|
|
1522
|
+
* ```typescript
|
|
1523
|
+
* // Fetch first page
|
|
1524
|
+
* let result = await fetchUtxoData(0n, undefined, 1000);
|
|
1525
|
+
* console.log(`Fetched ${result.items.size} UTXOs`);
|
|
1526
|
+
*
|
|
1527
|
+
* // Continue pagination
|
|
1528
|
+
* while (result.hasMore && result.nextCursor !== undefined) {
|
|
1529
|
+
* result = await fetchUtxoData(result.nextCursor, undefined, 1000);
|
|
1530
|
+
* console.log(`Fetched ${result.items.size} more UTXOs`);
|
|
1531
|
+
* }
|
|
1532
|
+
* ```
|
|
1533
|
+
*
|
|
1534
|
+
* @see {@link UtxoFetchResult}
|
|
1535
|
+
* @public
|
|
1536
|
+
*/
|
|
1537
|
+
type UtxoDataFetcherFunction = (startIndex: bigint, endIndex?: bigint, limit?: U32) => Promise<UtxoFetchResult>;
|
|
1538
|
+
/**
|
|
1539
|
+
* Arguments for creating a UTXO data fetcher function.
|
|
1540
|
+
*
|
|
1541
|
+
* @example
|
|
1542
|
+
* ```typescript
|
|
1543
|
+
* const args: GetUtxoDataFetcherArgs = {
|
|
1544
|
+
* apiEndpoint: 'https://indexer.umbra.finance'
|
|
1545
|
+
* };
|
|
1546
|
+
* ```
|
|
1547
|
+
*
|
|
1548
|
+
* @see {@link UtxoDataFetcherFunction}
|
|
1549
|
+
* @public
|
|
1550
|
+
*/
|
|
1551
|
+
interface GetUtxoDataFetcherArgs {
|
|
1552
|
+
/** The HTTP endpoint of the indexer service */
|
|
1553
|
+
apiEndpoint: string;
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Optional dependencies for the UTXO data fetcher factory.
|
|
1557
|
+
*
|
|
1558
|
+
* @remarks
|
|
1559
|
+
* Allows injection of a custom fetch implementation for testing
|
|
1560
|
+
* or environments without global fetch.
|
|
1561
|
+
*
|
|
1562
|
+
* @see {@link UtxoDataFetcherFunction}
|
|
1563
|
+
* @public
|
|
1564
|
+
*/
|
|
1565
|
+
interface GetUtxoDataFetcherDeps {
|
|
1566
|
+
/**
|
|
1567
|
+
* Custom fetch implementation.
|
|
1568
|
+
* @default globalThis.fetch
|
|
1569
|
+
*/
|
|
1570
|
+
fetch?: typeof globalThis.fetch;
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Decrypted UTXO data from successful AES decryption.
|
|
1574
|
+
*
|
|
1575
|
+
* @remarks
|
|
1576
|
+
* Contains the plaintext values extracted from the AES-encrypted UTXO data,
|
|
1577
|
+
* along with the original UTXO metadata and the determined unlocker type.
|
|
1578
|
+
*
|
|
1579
|
+
* **AES Plaintext Structure (68 bytes):**
|
|
1580
|
+
* | Offset | Size | Field |
|
|
1581
|
+
* |--------|------|-------|
|
|
1582
|
+
* | 0 | 8 | Amount (LE) |
|
|
1583
|
+
* | 8 | 32 | Destination address |
|
|
1584
|
+
* | 40 | 16 | Modified generation index (LE) |
|
|
1585
|
+
* | 56 | 12 | Domain separator |
|
|
1586
|
+
*
|
|
1587
|
+
* @see {@link ClaimableUtxoData}
|
|
1588
|
+
* @public
|
|
1589
|
+
*/
|
|
1590
|
+
interface DecryptedUtxoData {
|
|
1591
|
+
/** Decrypted UTXO amount */
|
|
1592
|
+
amount: U64;
|
|
1593
|
+
/** Decrypted destination address */
|
|
1594
|
+
destinationAddress: Address;
|
|
1595
|
+
/** Modified generation index from decrypted AES data */
|
|
1596
|
+
modifiedGenerationIndex: U128LeBytes;
|
|
1597
|
+
/** Tree index containing this UTXO */
|
|
1598
|
+
treeIndex: U32;
|
|
1599
|
+
/** Insertion index within the tree */
|
|
1600
|
+
insertionIndex: U32;
|
|
1601
|
+
/** H1 components for claim proof generation */
|
|
1602
|
+
h1Components: H1Components;
|
|
1603
|
+
/** H1 hash value */
|
|
1604
|
+
h1Hash: U256LeBytes;
|
|
1605
|
+
/** H2 hash value */
|
|
1606
|
+
h2Hash: U256LeBytes;
|
|
1607
|
+
/**
|
|
1608
|
+
* The type of unlocker determined by the domain separator.
|
|
1609
|
+
* - 'self-burnable': Encrypted-balance deposit, self-claimable (you deposited it, you can burn it)
|
|
1610
|
+
* - 'received': Encrypted-balance deposit, receiver-claimable (someone sent it to you)
|
|
1611
|
+
* - 'public-self-burnable': Public-balance deposit, self-claimable (you deposited it via public ATA)
|
|
1612
|
+
* - 'public-received': Public-balance deposit, receiver-claimable (someone sent it to you via public ATA)
|
|
1613
|
+
*/
|
|
1614
|
+
unlockerType: "self-burnable" | "received" | "public-self-burnable" | "public-received";
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Full claim-ready UTXO data with Merkle proof.
|
|
1618
|
+
*
|
|
1619
|
+
* @remarks
|
|
1620
|
+
* Contains all the data needed to submit a claim transaction,
|
|
1621
|
+
* including the Merkle proof and decrypted values.
|
|
1622
|
+
* This structure is designed to be passed directly to claim functions.
|
|
1623
|
+
*
|
|
1624
|
+
* @see {@link ClaimableUtxoResult}
|
|
1625
|
+
* @see {@link ClaimableUtxoScannerFunction}
|
|
1626
|
+
* @public
|
|
1627
|
+
*/
|
|
1628
|
+
interface ClaimableUtxoData {
|
|
1629
|
+
/** Current Merkle root of the mixer tree */
|
|
1630
|
+
merkleRoot: U256LeBytes;
|
|
1631
|
+
/** Array of sibling hashes from leaf to root */
|
|
1632
|
+
merklePath: U256LeBytes[];
|
|
1633
|
+
/** Leaf index in the Merkle tree */
|
|
1634
|
+
leafIndex: U128;
|
|
1635
|
+
/** Decrypted UTXO amount */
|
|
1636
|
+
amount: U64;
|
|
1637
|
+
/** Decrypted destination address */
|
|
1638
|
+
destinationAddress: Address;
|
|
1639
|
+
/** Modified generation index from decrypted AES data */
|
|
1640
|
+
depositModifiedGenerationIndex: U128;
|
|
1641
|
+
/** Protocol version identifier */
|
|
1642
|
+
version: U64;
|
|
1643
|
+
/** Index of the commitment in the Merkle tree */
|
|
1644
|
+
commitmentIndex: U128;
|
|
1645
|
+
/** Lower 128 bits of the sender's address */
|
|
1646
|
+
senderAddressLow: U128;
|
|
1647
|
+
/** Upper 128 bits of the sender's address */
|
|
1648
|
+
senderAddressHigh: U128;
|
|
1649
|
+
/** Fixed SOL fees paid to relayer (in lamports) */
|
|
1650
|
+
relayerFixedSolFees: U64;
|
|
1651
|
+
/** Lower 128 bits of the mint address */
|
|
1652
|
+
mintAddressLow: U128;
|
|
1653
|
+
/** Upper 128 bits of the mint address */
|
|
1654
|
+
mintAddressHigh: U128;
|
|
1655
|
+
/** Timestamp components for TVK derivation */
|
|
1656
|
+
timestamp: TimestampComponents;
|
|
1657
|
+
/** Cumulative SPL token volume in the pool at insertion time (base units) */
|
|
1658
|
+
poolVolumeSpl: U64;
|
|
1659
|
+
/** Cumulative SOL volume in the pool at insertion time (lamports) */
|
|
1660
|
+
poolVolumeSol: U64;
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Result from fetching claimable UTXOs.
|
|
1664
|
+
*
|
|
1665
|
+
* @remarks
|
|
1666
|
+
* Groups claimable UTXOs by unlocker type for easy processing.
|
|
1667
|
+
* Each array contains claim-ready data that can be passed directly
|
|
1668
|
+
* to the appropriate claim function.
|
|
1669
|
+
*
|
|
1670
|
+
* @example
|
|
1671
|
+
* ```typescript
|
|
1672
|
+
* const result: ClaimableUtxoResult = await fetchClaimableUtxos({...});
|
|
1673
|
+
* console.log(`Self-burnable: ${result.selfBurnable.length}`);
|
|
1674
|
+
* console.log(`Received: ${result.received.length}`);
|
|
1675
|
+
* ```
|
|
1676
|
+
*
|
|
1677
|
+
* @see {@link ClaimableUtxoScannerFunction}
|
|
1678
|
+
* @see {@link ClaimableUtxoData}
|
|
1679
|
+
* @public
|
|
1680
|
+
*/
|
|
1681
|
+
interface ClaimableUtxoResult {
|
|
1682
|
+
/**
|
|
1683
|
+
* Encrypted-balance deposit UTXOs you can burn yourself (you created them).
|
|
1684
|
+
* Pass to `SelfClaimableUtxoToEncryptedBalanceClaimerFunction` or
|
|
1685
|
+
* `SelfClaimableUtxoToPublicBalanceClaimerFunction`.
|
|
1686
|
+
*/
|
|
1687
|
+
selfBurnable: ClaimableUtxoData[];
|
|
1688
|
+
/**
|
|
1689
|
+
* Encrypted-balance deposit UTXOs sent to you by others.
|
|
1690
|
+
* Pass to `ReceiverClaimableUtxoToEncryptedBalanceClaimerFunction`.
|
|
1691
|
+
*/
|
|
1692
|
+
received: ClaimableUtxoData[];
|
|
1693
|
+
/**
|
|
1694
|
+
* Public-balance deposit UTXOs you can burn yourself (you created them via public ATA).
|
|
1695
|
+
* Pass to `SelfClaimableUtxoToEncryptedBalanceClaimerFunction` or
|
|
1696
|
+
* `SelfClaimableUtxoToPublicBalanceClaimerFunction`.
|
|
1697
|
+
*/
|
|
1698
|
+
publicSelfBurnable: ClaimableUtxoData[];
|
|
1699
|
+
/**
|
|
1700
|
+
* Public-balance deposit UTXOs sent to you by others via public ATA.
|
|
1701
|
+
* Pass to `ReceiverClaimableUtxoToEncryptedBalanceClaimerFunction`.
|
|
1702
|
+
*/
|
|
1703
|
+
publicReceived: ClaimableUtxoData[];
|
|
1704
|
+
}
|
|
1705
|
+
/**
|
|
1706
|
+
* A decrypted UTXO as returned by the scanner, before merkle proofs are attached.
|
|
1707
|
+
*
|
|
1708
|
+
* @remarks
|
|
1709
|
+
* This is the output of the scan phase. Merkle proofs are fetched later at
|
|
1710
|
+
* claim time, per-batch, via the batch proof endpoint to guarantee a consistent
|
|
1711
|
+
* root across all UTXOs in a ZK proof batch.
|
|
1712
|
+
*
|
|
1713
|
+
* @see {@link ScannedUtxoResult}
|
|
1714
|
+
* @see {@link ClaimableUtxoData} — the enriched type after proofs are attached
|
|
1715
|
+
* @public
|
|
1716
|
+
*/
|
|
1717
|
+
type ScannedUtxoData = DecryptedUtxoData;
|
|
1718
|
+
/**
|
|
1719
|
+
* Result from scanning UTXOs (no merkle proofs attached).
|
|
1720
|
+
*
|
|
1721
|
+
* @remarks
|
|
1722
|
+
* Groups scanned UTXOs by unlocker type. Merkle proofs are fetched at claim
|
|
1723
|
+
* time, not scan time, so these entries do not yet contain proof data.
|
|
1724
|
+
*
|
|
1725
|
+
* @see {@link ScannedUtxoData}
|
|
1726
|
+
* @public
|
|
1727
|
+
*/
|
|
1728
|
+
interface ScannedUtxoResult {
|
|
1729
|
+
/** Encrypted-balance deposit UTXOs you can burn yourself. */
|
|
1730
|
+
selfBurnable: ScannedUtxoData[];
|
|
1731
|
+
/** Encrypted-balance deposit UTXOs sent to you by others. */
|
|
1732
|
+
received: ScannedUtxoData[];
|
|
1733
|
+
/** Public-balance deposit UTXOs you can burn yourself. */
|
|
1734
|
+
publicSelfBurnable: ScannedUtxoData[];
|
|
1735
|
+
/** Public-balance deposit UTXOs sent to you by others via public ATA. */
|
|
1736
|
+
publicReceived: ScannedUtxoData[];
|
|
1737
|
+
}
|
|
1738
|
+
/**
|
|
1739
|
+
* Result of a batch merkle proof fetch from the indexer.
|
|
1740
|
+
*
|
|
1741
|
+
* @remarks
|
|
1742
|
+
* All proofs share a single `root` because they were generated under a single
|
|
1743
|
+
* tree read lock. This guarantees consistency for ZK batch claims.
|
|
1744
|
+
*
|
|
1745
|
+
* @public
|
|
1746
|
+
*/
|
|
1747
|
+
interface BatchMerkleProofResult {
|
|
1748
|
+
/** The shared merkle root for all proofs in this batch. */
|
|
1749
|
+
root: U256LeBytes;
|
|
1750
|
+
/** Per-leaf proof data, keyed by insertion index. */
|
|
1751
|
+
proofs: Map<U32, {
|
|
1752
|
+
merklePath: U256LeBytes[];
|
|
1753
|
+
leaf: U256LeBytes;
|
|
1754
|
+
}>;
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Function type for fetching batch merkle proofs with a consistent root.
|
|
1758
|
+
*
|
|
1759
|
+
* @remarks
|
|
1760
|
+
* Used at claim time (not scan time) to fetch proofs for all UTXOs in a
|
|
1761
|
+
* single ZK proof batch. The indexer guarantees all returned proofs share
|
|
1762
|
+
* the same merkle root by holding a single read lock.
|
|
1763
|
+
*
|
|
1764
|
+
* @see {@link BatchMerkleProofResult}
|
|
1765
|
+
* @public
|
|
1766
|
+
*/
|
|
1767
|
+
type BatchMerkleProofFetcherFunction = (treeIndex: U32, insertionIndices: readonly U32[]) => Promise<BatchMerkleProofResult>;
|
|
1768
|
+
/**
|
|
1769
|
+
* Function type for fetching claimable UTXOs.
|
|
1770
|
+
*
|
|
1771
|
+
* @remarks
|
|
1772
|
+
* Fetches, decrypts, and categorizes all UTXOs that belong to the client
|
|
1773
|
+
* within the specified tree and index range.
|
|
1774
|
+
*
|
|
1775
|
+
* **Workflow:**
|
|
1776
|
+
* 1. Fetch UTXO data from indexer (paginated)
|
|
1777
|
+
* 2. For each UTXO, try to decrypt using client's X25519 key
|
|
1778
|
+
* 3. Check domain separator to determine unlocker type
|
|
1779
|
+
* 4. Fetch Merkle proofs for successfully decrypted UTXOs
|
|
1780
|
+
* 5. Return grouped results
|
|
1781
|
+
*
|
|
1782
|
+
* @param treeIndex - The Merkle tree index to scan
|
|
1783
|
+
* @param startInsertionIndex - Starting insertion index (inclusive)
|
|
1784
|
+
* @param endInsertionIndex - Optional ending insertion index (inclusive)
|
|
1785
|
+
* @returns Promise resolving to grouped claimable UTXOs
|
|
1786
|
+
*
|
|
1787
|
+
* @example
|
|
1788
|
+
* ```typescript
|
|
1789
|
+
* const fetchClaimable = getClaimableUtxoScannerFunction({ client });
|
|
1790
|
+
*
|
|
1791
|
+
* // Scan tree 0 for claimable UTXOs
|
|
1792
|
+
* const result = await fetchClaimable(0, 0, 10000);
|
|
1793
|
+
*
|
|
1794
|
+
* console.log(`Found ${result.ephemeral.length} self-deposited UTXOs`);
|
|
1795
|
+
* console.log(`Found ${result.receiver.length} UTXOs from others`);
|
|
1796
|
+
* ```
|
|
1797
|
+
*
|
|
1798
|
+
* @see {@link ClaimableUtxoResult}
|
|
1799
|
+
* @public
|
|
1800
|
+
*/
|
|
1801
|
+
type ClaimableUtxoScannerFunction = (treeIndex: U32, startInsertionIndex: U32, endInsertionIndex?: U32) => Promise<ScannedUtxoResult>;
|
|
1802
|
+
/**
|
|
1803
|
+
* Arguments for creating a claimable UTXO fetcher function.
|
|
1804
|
+
*
|
|
1805
|
+
* @example
|
|
1806
|
+
* ```typescript
|
|
1807
|
+
* const args: GetClaimableUtxoScannerFunctionArgs = {
|
|
1808
|
+
* client: myUmbraClient
|
|
1809
|
+
* };
|
|
1810
|
+
* ```
|
|
1811
|
+
*
|
|
1812
|
+
* @see {@link ClaimableUtxoScannerFunction}
|
|
1813
|
+
* @public
|
|
1814
|
+
*/
|
|
1815
|
+
interface GetClaimableUtxoScannerFunctionArgs {
|
|
1816
|
+
/** The Umbra client providing cryptographic keys for decryption */
|
|
1817
|
+
client: IUmbraClient;
|
|
1818
|
+
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Optional dependencies for the claimable UTXO fetcher factory.
|
|
1821
|
+
*
|
|
1822
|
+
* @remarks
|
|
1823
|
+
* Allows injection of custom implementations for testing
|
|
1824
|
+
* or alternative implementations.
|
|
1825
|
+
*
|
|
1826
|
+
* @see {@link ClaimableUtxoScannerFunction}
|
|
1827
|
+
* @public
|
|
1828
|
+
*/
|
|
1829
|
+
interface GetClaimableUtxoScannerFunctionDeps {
|
|
1830
|
+
/**
|
|
1831
|
+
* Custom UTXO data fetcher.
|
|
1832
|
+
* @default Created from client's indexerApiEndpoint
|
|
1833
|
+
*/
|
|
1834
|
+
fetchUtxoData?: UtxoDataFetcherFunction;
|
|
1835
|
+
/**
|
|
1836
|
+
* Custom Merkle proof fetcher.
|
|
1837
|
+
* @default Created from client's indexerApiEndpoint
|
|
1838
|
+
*/
|
|
1839
|
+
fetchMerkleProof?: MerkleProofFetcherFunction;
|
|
1840
|
+
/**
|
|
1841
|
+
* Custom AES decryptor function.
|
|
1842
|
+
* @default getAesDecryptor()
|
|
1843
|
+
*/
|
|
1844
|
+
aesDecryptor?: AesDecryptorFunction;
|
|
1845
|
+
/**
|
|
1846
|
+
* Custom X25519 public key derivation function.
|
|
1847
|
+
* @default x25519.getPublicKey
|
|
1848
|
+
*/
|
|
1849
|
+
x25519GetPublicKey?: (privateKey: X25519PrivateKey) => X25519PublicKey;
|
|
1850
|
+
/**
|
|
1851
|
+
* Custom X25519 shared secret computation function.
|
|
1852
|
+
* @default x25519.getSharedSecret
|
|
1853
|
+
*/
|
|
1854
|
+
x25519GetSharedSecret?: (privateKey: X25519PrivateKey, publicKey: X25519PublicKey) => Uint8Array;
|
|
1855
|
+
/**
|
|
1856
|
+
* Custom fetch implementation for indexer API calls.
|
|
1857
|
+
* @default globalThis.fetch
|
|
1858
|
+
*/
|
|
1859
|
+
fetch?: typeof globalThis.fetch;
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
/**
|
|
1863
|
+
* Umbra Interfaces — Client and signer interfaces — the core SDK configuration object.
|
|
1864
|
+
*
|
|
1865
|
+
* @module umbra/interfaces/client
|
|
1866
|
+
* @since 2.0.0
|
|
1867
|
+
*/
|
|
1868
|
+
|
|
1869
|
+
/**
|
|
1870
|
+
* A transaction with blockhash lifetime constraint, ready for signing.
|
|
1871
|
+
*
|
|
1872
|
+
* This is the intersection of a base `Transaction` (containing `messageBytes`
|
|
1873
|
+
* and `signatures`) with `TransactionWithBlockhashLifetime` (containing the
|
|
1874
|
+
* `lifetimeConstraint` with blockhash and lastValidBlockHeight).
|
|
1875
|
+
*
|
|
1876
|
+
* @remarks
|
|
1877
|
+
* All Umbra transaction signing flows expect this type. Construct it by
|
|
1878
|
+
* fetching a recent blockhash and attaching it to a compiled transaction
|
|
1879
|
+
* via the `@solana/kit` utilities before passing to `IUmbraSigner.signTransaction`.
|
|
1880
|
+
*
|
|
1881
|
+
* @example
|
|
1882
|
+
* ```typescript
|
|
1883
|
+
* import { pipe } from "@solana/functional";
|
|
1884
|
+
* import { setTransactionMessageLifetimeUsingBlockhash } from "@solana/kit";
|
|
1885
|
+
*
|
|
1886
|
+
* const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
|
|
1887
|
+
* const signable: SignableTransaction = pipe(
|
|
1888
|
+
* txMessage,
|
|
1889
|
+
* (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
|
|
1890
|
+
* (tx) => compileTransaction(tx),
|
|
1891
|
+
* );
|
|
1892
|
+
* ```
|
|
1893
|
+
*
|
|
1894
|
+
* @see {@link IUmbraSigner}
|
|
1895
|
+
* @public
|
|
1896
|
+
*/
|
|
1897
|
+
type SignableTransaction = Transaction & TransactionWithBlockhashLifetime;
|
|
1898
|
+
/**
|
|
1899
|
+
* Result of signing an arbitrary message.
|
|
1900
|
+
*
|
|
1901
|
+
* Contains both the original message and its cryptographic signature,
|
|
1902
|
+
* allowing verification without needing to reconstruct the message.
|
|
1903
|
+
*
|
|
1904
|
+
* @remarks
|
|
1905
|
+
* Returned by {@link IUmbraSigner.signMessage}. All three fields are required
|
|
1906
|
+
* for off-chain signature verification.
|
|
1907
|
+
*
|
|
1908
|
+
* @example
|
|
1909
|
+
* ```typescript
|
|
1910
|
+
* const signed: SignedMessage = await signer.signMessage(
|
|
1911
|
+
* new TextEncoder().encode("Hello, Umbra!")
|
|
1912
|
+
* );
|
|
1913
|
+
* console.log(signed.signer); // base58 address
|
|
1914
|
+
* console.log(signed.signature); // 64-byte Ed25519 signature
|
|
1915
|
+
* ```
|
|
1916
|
+
*
|
|
1917
|
+
* @sealed
|
|
1918
|
+
* @see {@link IUmbraSigner}
|
|
1919
|
+
* @public
|
|
1920
|
+
*/
|
|
1921
|
+
interface SignedMessage {
|
|
1922
|
+
/**
|
|
1923
|
+
* The original message bytes that were signed.
|
|
1924
|
+
*/
|
|
1925
|
+
readonly message: Uint8Array;
|
|
1926
|
+
/**
|
|
1927
|
+
* The Ed25519 signature over the message.
|
|
1928
|
+
*
|
|
1929
|
+
* This is a 64-byte signature that can be verified using the
|
|
1930
|
+
* signer's public key (address).
|
|
1931
|
+
*/
|
|
1932
|
+
readonly signature: SignatureBytes;
|
|
1933
|
+
/**
|
|
1934
|
+
* The address of the signer who produced this signature.
|
|
1935
|
+
*
|
|
1936
|
+
* This can be used to verify the signature against the expected
|
|
1937
|
+
* signer's public key.
|
|
1938
|
+
*/
|
|
1939
|
+
readonly signer: Address;
|
|
1940
|
+
}
|
|
1941
|
+
/**
|
|
1942
|
+
* Interface for signing Solana transactions and messages in the Umbra protocol.
|
|
1943
|
+
*
|
|
1944
|
+
* This interface abstracts the signing implementation, allowing different
|
|
1945
|
+
* backends such as:
|
|
1946
|
+
* - In-memory key pairs (for testing or non-sensitive operations)
|
|
1947
|
+
* - Hardware wallets (Ledger, Trezor)
|
|
1948
|
+
* - Mobile secure enclaves
|
|
1949
|
+
* - Browser wallet extensions
|
|
1950
|
+
* - Multi-party computation (MPC) signers
|
|
1951
|
+
*
|
|
1952
|
+
* ## Design Principles
|
|
1953
|
+
*
|
|
1954
|
+
* 1. **Partial Signing**: Methods return partially signed transactions,
|
|
1955
|
+
* allowing for multi-signature workflows where multiple parties must sign.
|
|
1956
|
+
*
|
|
1957
|
+
* 2. **Signature Only**: The signer only signs transactions; it does not
|
|
1958
|
+
* submit them to the network. This separation allows for:
|
|
1959
|
+
* - Transaction inspection before submission
|
|
1960
|
+
* - Batching multiple transactions
|
|
1961
|
+
* - Custom submission strategies (retry logic, priority fees, etc.)
|
|
1962
|
+
*
|
|
1963
|
+
* 3. **Lifetime Preservation**: Transactions already contain their blockhash
|
|
1964
|
+
* and lifetime constraints. The signer preserves this information.
|
|
1965
|
+
*
|
|
1966
|
+
* @example
|
|
1967
|
+
* ```typescript
|
|
1968
|
+
* import { IUmbraSigner, SignableTransaction } from '@umbra-privacy/sdk';
|
|
1969
|
+
*
|
|
1970
|
+
* // Example: Using a signer to sign a transaction
|
|
1971
|
+
* async function signAndInspect(
|
|
1972
|
+
* signer: IUmbraSigner,
|
|
1973
|
+
* transaction: SignableTransaction
|
|
1974
|
+
* ) {
|
|
1975
|
+
* // Get the signer's address
|
|
1976
|
+
* console.log(`Signing with: ${signer.address}`);
|
|
1977
|
+
*
|
|
1978
|
+
* // Sign the transaction
|
|
1979
|
+
* const signedTx = await signer.signTransaction(transaction);
|
|
1980
|
+
*
|
|
1981
|
+
* // Transaction still has its lifetime info
|
|
1982
|
+
* console.log(`Valid until block: ${signedTx.lifetimeConstraint.lastValidBlockHeight}`);
|
|
1983
|
+
*
|
|
1984
|
+
* return signedTx;
|
|
1985
|
+
* }
|
|
1986
|
+
* ```
|
|
1987
|
+
*
|
|
1988
|
+
* @example
|
|
1989
|
+
* ```typescript
|
|
1990
|
+
* // Example: Batch signing multiple transactions
|
|
1991
|
+
* async function batchSign(
|
|
1992
|
+
* signer: IUmbraSigner,
|
|
1993
|
+
* transactions: SignableTransaction[]
|
|
1994
|
+
* ) {
|
|
1995
|
+
* // Sign all transactions in one call
|
|
1996
|
+
* const signedTransactions = await signer.signTransactions(transactions);
|
|
1997
|
+
*
|
|
1998
|
+
* // Each transaction is signed independently
|
|
1999
|
+
* for (const tx of signedTransactions) {
|
|
2000
|
+
* console.log(`Blockhash: ${tx.lifetimeConstraint.blockhash}`);
|
|
2001
|
+
* }
|
|
2002
|
+
*
|
|
2003
|
+
* return signedTransactions;
|
|
2004
|
+
* }
|
|
2005
|
+
* ```
|
|
2006
|
+
*
|
|
2007
|
+
* @example
|
|
2008
|
+
* ```typescript
|
|
2009
|
+
* // Example: Signing an arbitrary message for off-chain verification
|
|
2010
|
+
* async function createSignedAttestation(
|
|
2011
|
+
* signer: IUmbraSigner,
|
|
2012
|
+
* data: string
|
|
2013
|
+
* ) {
|
|
2014
|
+
* const message = new TextEncoder().encode(data);
|
|
2015
|
+
* const signedMessage = await signer.signMessage(message);
|
|
2016
|
+
*
|
|
2017
|
+
* return {
|
|
2018
|
+
* data,
|
|
2019
|
+
* signature: signedMessage.signature,
|
|
2020
|
+
* signer: signedMessage.signer,
|
|
2021
|
+
* };
|
|
2022
|
+
* }
|
|
2023
|
+
* ```
|
|
2024
|
+
*
|
|
2025
|
+
* @public
|
|
2026
|
+
*/
|
|
2027
|
+
interface IUmbraSigner {
|
|
2028
|
+
/**
|
|
2029
|
+
* The public address of this signer.
|
|
2030
|
+
*
|
|
2031
|
+
* This is the base58-encoded Ed25519 public key that corresponds to
|
|
2032
|
+
* the private key used for signing. It uniquely identifies this signer
|
|
2033
|
+
* on the Solana network.
|
|
2034
|
+
*
|
|
2035
|
+
* @remarks
|
|
2036
|
+
* The address is derived from the public key and serves as:
|
|
2037
|
+
* - The account owner for token accounts
|
|
2038
|
+
* - The fee payer identifier in transactions
|
|
2039
|
+
* - The key to look up signatures in signed transactions
|
|
2040
|
+
*
|
|
2041
|
+
* @example
|
|
2042
|
+
* ```typescript
|
|
2043
|
+
* const signer: IUmbraSigner = getUmbraSigner(config);
|
|
2044
|
+
* console.log(`Signer address: ${signer.address}`);
|
|
2045
|
+
* // Output: "8xH3kJLmV9..."
|
|
2046
|
+
* ```
|
|
2047
|
+
*/
|
|
2048
|
+
readonly address: Address;
|
|
2049
|
+
/**
|
|
2050
|
+
* Signs a single transaction.
|
|
2051
|
+
*
|
|
2052
|
+
* Takes a compiled transaction with its blockhash lifetime already set
|
|
2053
|
+
* and returns the same transaction with the signer's signature added.
|
|
2054
|
+
*
|
|
2055
|
+
* @param transaction - The transaction to sign. Must include:
|
|
2056
|
+
* - `messageBytes`: The serialized transaction message
|
|
2057
|
+
* - `signatures`: Existing signatures map (may be empty)
|
|
2058
|
+
* - `lifetimeConstraint`: Blockhash and last valid block height
|
|
2059
|
+
*
|
|
2060
|
+
* @returns A promise that resolves to the signed transaction with:
|
|
2061
|
+
* - The signer's signature added to the `signatures` map
|
|
2062
|
+
* - All other fields preserved unchanged
|
|
2063
|
+
*
|
|
2064
|
+
* @remarks
|
|
2065
|
+
* ## Partial Signing
|
|
2066
|
+
*
|
|
2067
|
+
* This method performs partial signing, meaning:
|
|
2068
|
+
* - Only adds a signature for this signer's address
|
|
2069
|
+
* - Does not validate that all required signatures are present
|
|
2070
|
+
* - The returned transaction may need additional signatures
|
|
2071
|
+
*
|
|
2072
|
+
* ## Error Conditions
|
|
2073
|
+
*
|
|
2074
|
+
* The method may throw if:
|
|
2075
|
+
* - The signer's address is not in the transaction's account list
|
|
2076
|
+
* - The underlying signing operation fails (hardware wallet disconnected, etc.)
|
|
2077
|
+
* - The user rejects the signing request (for interactive signers)
|
|
2078
|
+
*
|
|
2079
|
+
* @example
|
|
2080
|
+
* ```typescript
|
|
2081
|
+
* const transaction: SignableTransaction = buildShieldTransaction(...);
|
|
2082
|
+
*
|
|
2083
|
+
* // Sign the transaction
|
|
2084
|
+
* const signedTx = await signer.signTransaction(transaction);
|
|
2085
|
+
*
|
|
2086
|
+
* console.log('Transaction signed successfully');
|
|
2087
|
+
* ```
|
|
2088
|
+
*/
|
|
2089
|
+
signTransaction: (transaction: SignableTransaction) => Promise<SignedTransaction>;
|
|
2090
|
+
/**
|
|
2091
|
+
* Signs multiple transactions in a batch.
|
|
2092
|
+
*
|
|
2093
|
+
* This method signs an array of transactions, returning them in the
|
|
2094
|
+
* same order with signatures added. Each transaction is signed
|
|
2095
|
+
* independently with its own blockhash lifetime preserved.
|
|
2096
|
+
*
|
|
2097
|
+
* @param transactions - Array of transactions to sign. Each must include
|
|
2098
|
+
* the same fields as required by `signTransaction`.
|
|
2099
|
+
*
|
|
2100
|
+
* @returns A promise that resolves to an array of signed transactions,
|
|
2101
|
+
* in the same order as the input array.
|
|
2102
|
+
*
|
|
2103
|
+
* @remarks
|
|
2104
|
+
* ## Batch Efficiency
|
|
2105
|
+
*
|
|
2106
|
+
* Implementations may optimize batch signing by:
|
|
2107
|
+
* - Reducing round-trips to hardware wallets
|
|
2108
|
+
* - Batching user approval prompts
|
|
2109
|
+
* - Parallelizing cryptographic operations
|
|
2110
|
+
*
|
|
2111
|
+
* ## Atomicity
|
|
2112
|
+
*
|
|
2113
|
+
* Batch signing is **not atomic**. If signing fails partway through:
|
|
2114
|
+
* - Some transactions may be signed, others not
|
|
2115
|
+
* - The method will throw, but partial results may not be recoverable
|
|
2116
|
+
* - Callers should handle failures by re-signing the entire batch
|
|
2117
|
+
*
|
|
2118
|
+
* ## Order Preservation
|
|
2119
|
+
*
|
|
2120
|
+
* The returned array maintains the same order as the input:
|
|
2121
|
+
* - `result[i]` is the signed version of `transactions[i]`
|
|
2122
|
+
* - Each transaction retains its original blockhash lifetime
|
|
2123
|
+
*
|
|
2124
|
+
* @example
|
|
2125
|
+
* ```typescript
|
|
2126
|
+
* const transactions: SignableTransaction[] = [
|
|
2127
|
+
* buildShieldTransaction(mint1, amount1),
|
|
2128
|
+
* buildShieldTransaction(mint2, amount2),
|
|
2129
|
+
* buildShieldTransaction(mint3, amount3),
|
|
2130
|
+
* ];
|
|
2131
|
+
*
|
|
2132
|
+
* // Sign all transactions at once
|
|
2133
|
+
* const signedTransactions = await signer.signTransactions(transactions);
|
|
2134
|
+
*
|
|
2135
|
+
* // Submit each signed transaction
|
|
2136
|
+
* for (const tx of signedTransactions) {
|
|
2137
|
+
* await submitTransaction(tx);
|
|
2138
|
+
* }
|
|
2139
|
+
* ```
|
|
2140
|
+
*/
|
|
2141
|
+
signTransactions: (transactions: readonly SignableTransaction[]) => Promise<SignedTransaction[]>;
|
|
2142
|
+
/**
|
|
2143
|
+
* Signs an arbitrary message.
|
|
2144
|
+
*
|
|
2145
|
+
* This method signs a raw byte array using the signer's private key,
|
|
2146
|
+
* producing an Ed25519 signature. This is useful for:
|
|
2147
|
+
* - Off-chain message authentication
|
|
2148
|
+
* - Proving ownership of an address
|
|
2149
|
+
* - Creating verifiable attestations
|
|
2150
|
+
*
|
|
2151
|
+
* @param message - The raw bytes to sign. Can be any data, but common
|
|
2152
|
+
* patterns include:
|
|
2153
|
+
* - UTF-8 encoded strings
|
|
2154
|
+
* - Structured data (JSON, protobuf, etc.)
|
|
2155
|
+
* - Hash digests
|
|
2156
|
+
*
|
|
2157
|
+
* @returns A promise that resolves to a signed message containing:
|
|
2158
|
+
* - The original message bytes
|
|
2159
|
+
* - The 64-byte Ed25519 signature
|
|
2160
|
+
* - The signer's address for verification
|
|
2161
|
+
*
|
|
2162
|
+
* @remarks
|
|
2163
|
+
* ## Message Prefixing
|
|
2164
|
+
*
|
|
2165
|
+
* Some signer implementations may prefix the message with a domain
|
|
2166
|
+
* separator (e.g., "\x19Solana Signed Message:\n") to prevent
|
|
2167
|
+
* signature reuse attacks. Verifiers must use the same prefix.
|
|
2168
|
+
*
|
|
2169
|
+
* ## Security Considerations
|
|
2170
|
+
*
|
|
2171
|
+
* - Never sign messages you don't understand
|
|
2172
|
+
* - Be cautious of messages that look like transaction data
|
|
2173
|
+
* - Consider using structured message formats with clear semantics
|
|
2174
|
+
*
|
|
2175
|
+
* @example
|
|
2176
|
+
* ```typescript
|
|
2177
|
+
* // Sign a simple text message
|
|
2178
|
+
* const message = new TextEncoder().encode('Hello, Umbra!');
|
|
2179
|
+
* const signedMessage = await signer.signMessage(message);
|
|
2180
|
+
*
|
|
2181
|
+
* // The signature can be verified off-chain
|
|
2182
|
+
* const isValid = await verifySignature(
|
|
2183
|
+
* signedMessage.message,
|
|
2184
|
+
* signedMessage.signature,
|
|
2185
|
+
* signedMessage.signer
|
|
2186
|
+
* );
|
|
2187
|
+
* ```
|
|
2188
|
+
*
|
|
2189
|
+
* @example
|
|
2190
|
+
* ```typescript
|
|
2191
|
+
* // Sign structured data
|
|
2192
|
+
* const attestation = {
|
|
2193
|
+
* action: 'verify_ownership',
|
|
2194
|
+
* address: signer.address,
|
|
2195
|
+
* timestamp: Date.now(),
|
|
2196
|
+
* };
|
|
2197
|
+
*
|
|
2198
|
+
* const message = new TextEncoder().encode(JSON.stringify(attestation));
|
|
2199
|
+
* const signedAttestation = await signer.signMessage(message);
|
|
2200
|
+
* ```
|
|
2201
|
+
*/
|
|
2202
|
+
signMessage: (message: Uint8Array) => Promise<SignedMessage>;
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Function type for retrieving the master seed from secure storage.
|
|
2206
|
+
*
|
|
2207
|
+
* This function type is used to abstract the retrieval of the master seed,
|
|
2208
|
+
* which is the root cryptographic secret used to derive all keys in the
|
|
2209
|
+
* Umbra protocol (viewing keys, spending keys, encryption keys, etc.).
|
|
2210
|
+
*
|
|
2211
|
+
* @returns A `Promise` that resolves to the `MasterSeed` - a 64-byte
|
|
2212
|
+
* Keccak-512 hash that serves as the root of the key hierarchy.
|
|
2213
|
+
*
|
|
2214
|
+
* @remarks
|
|
2215
|
+
* ## Why Use a Function?
|
|
2216
|
+
*
|
|
2217
|
+
* The master seed is stored in the device's secure storage (e.g., iOS Keychain,
|
|
2218
|
+
* Android Keystore, or platform-specific secure enclaves) rather than being
|
|
2219
|
+
* held in memory. Using a function to retrieve it provides several benefits:
|
|
2220
|
+
*
|
|
2221
|
+
* - **Lazy Loading**: The seed is only loaded into memory when needed
|
|
2222
|
+
* - **Security**: Minimizes the time sensitive data spends in application memory
|
|
2223
|
+
* - **Abstraction**: Allows different implementations for different platforms
|
|
2224
|
+
* (mobile secure storage, hardware wallets, browser extensions, etc.)
|
|
2225
|
+
* - **Async Support**: Secure storage APIs are typically asynchronous
|
|
2226
|
+
*
|
|
2227
|
+
* ## Security Considerations
|
|
2228
|
+
*
|
|
2229
|
+
* - The master seed should NEVER be logged, transmitted, or stored in plain text
|
|
2230
|
+
* - Implementations should clear the seed from memory after use when possible
|
|
2231
|
+
* - Consider using secure memory handling techniques on supported platforms
|
|
2232
|
+
* - The seed should only be accessed when cryptographic operations are required
|
|
2233
|
+
*
|
|
2234
|
+
* @example
|
|
2235
|
+
* ```typescript
|
|
2236
|
+
* import { GetMasterSeedFunction } from '@umbra-privacy/sdk';
|
|
2237
|
+
* import * as SecureStore from 'expo-secure-store';
|
|
2238
|
+
*
|
|
2239
|
+
* // Example implementation using Expo SecureStore
|
|
2240
|
+
* const getMasterSeed: GetMasterSeedFunction = async () => {
|
|
2241
|
+
* const seedHex = await SecureStore.getItemAsync('umbra_master_seed');
|
|
2242
|
+
* if (!seedHex) {
|
|
2243
|
+
* throw new Error('Master seed not found in secure storage');
|
|
2244
|
+
* }
|
|
2245
|
+
* return hexToBytes(seedHex) as MasterSeed;
|
|
2246
|
+
* };
|
|
2247
|
+
* ```
|
|
2248
|
+
*
|
|
2249
|
+
* @see {@link IUmbraClient}
|
|
2250
|
+
* @public
|
|
2251
|
+
*/
|
|
2252
|
+
type GetMasterSeedFunction = () => Promise<MasterSeed>;
|
|
2253
|
+
/**
|
|
2254
|
+
* Configuration interface for the Umbra client.
|
|
2255
|
+
*
|
|
2256
|
+
* This interface defines the required configuration for initializing an Umbra
|
|
2257
|
+
* client instance. It includes network endpoints, the master seed retrieval
|
|
2258
|
+
* function, and other essential settings for interacting with the Umbra
|
|
2259
|
+
* privacy protocol on Solana.
|
|
2260
|
+
*
|
|
2261
|
+
* @remarks
|
|
2262
|
+
* ## Master Seed Function
|
|
2263
|
+
*
|
|
2264
|
+
* The `getMasterSeed` property is a function rather than a direct value because
|
|
2265
|
+
* the master seed is typically stored in the device's secure storage system:
|
|
2266
|
+
*
|
|
2267
|
+
* - **iOS**: Keychain Services
|
|
2268
|
+
* - **Android**: Android Keystore
|
|
2269
|
+
* - **Desktop**: OS-specific credential managers
|
|
2270
|
+
* - **Browser**: Web Crypto API or extension-based secure storage
|
|
2271
|
+
*
|
|
2272
|
+
* By using a function, we ensure:
|
|
2273
|
+
* 1. The seed is retrieved on-demand, not held in memory unnecessarily
|
|
2274
|
+
* 2. Each retrieval can include biometric/PIN verification if configured
|
|
2275
|
+
* 3. The implementation can vary across platforms without changing the interface
|
|
2276
|
+
*
|
|
2277
|
+
* ## Network Configuration
|
|
2278
|
+
*
|
|
2279
|
+
* The client requires both HTTP and WebSocket endpoints:
|
|
2280
|
+
* - `rpcUrl`: Used for sending transactions and querying account state
|
|
2281
|
+
* - `rpcSubscriptionsUrl`: Used for real-time account change notifications
|
|
2282
|
+
*
|
|
2283
|
+
* @example
|
|
2284
|
+
* ```typescript
|
|
2285
|
+
* import { IUmbraClient } from '@umbra-privacy/sdk';
|
|
2286
|
+
* import * as SecureStore from 'expo-secure-store';
|
|
2287
|
+
*
|
|
2288
|
+
* const config: IUmbraClient = {
|
|
2289
|
+
* // Solana RPC endpoint for transactions and queries
|
|
2290
|
+
* rpcUrl: 'https://api.mainnet-beta.solana.com',
|
|
2291
|
+
*
|
|
2292
|
+
* // WebSocket endpoint for real-time subscriptions
|
|
2293
|
+
* rpcSubscriptionsUrl: 'wss://api.mainnet-beta.solana.com',
|
|
2294
|
+
*
|
|
2295
|
+
* // Function to retrieve master seed from secure storage
|
|
2296
|
+
* // This is called on-demand when cryptographic operations are needed
|
|
2297
|
+
* getMasterSeed: async () => {
|
|
2298
|
+
* const seedHex = await SecureStore.getItemAsync('umbra_master_seed');
|
|
2299
|
+
* if (!seedHex) {
|
|
2300
|
+
* throw new Error('Master seed not found');
|
|
2301
|
+
* }
|
|
2302
|
+
* return hexToBytes(seedHex) as MasterSeed;
|
|
2303
|
+
* },
|
|
2304
|
+
*
|
|
2305
|
+
* // Optional: Commitment level for transactions
|
|
2306
|
+
* commitment: 'confirmed',
|
|
2307
|
+
* };
|
|
2308
|
+
*
|
|
2309
|
+
* const client = createUmbraClient(config);
|
|
2310
|
+
* ```
|
|
2311
|
+
*
|
|
2312
|
+
* @example
|
|
2313
|
+
* ```typescript
|
|
2314
|
+
* // React Native with biometric protection
|
|
2315
|
+
* import * as LocalAuthentication from 'expo-local-authentication';
|
|
2316
|
+
*
|
|
2317
|
+
* const secureConfig: IUmbraClient = {
|
|
2318
|
+
* rpcUrl: process.env.SOLANA_RPC_URL,
|
|
2319
|
+
* rpcSubscriptionsUrl: process.env.SOLANA_WS_URL,
|
|
2320
|
+
* getMasterSeed: async () => {
|
|
2321
|
+
* // Require biometric authentication before accessing seed
|
|
2322
|
+
* const authResult = await LocalAuthentication.authenticateAsync({
|
|
2323
|
+
* promptMessage: 'Authenticate to access your private keys',
|
|
2324
|
+
* });
|
|
2325
|
+
*
|
|
2326
|
+
* if (!authResult.success) {
|
|
2327
|
+
* throw new Error('Biometric authentication failed');
|
|
2328
|
+
* }
|
|
2329
|
+
*
|
|
2330
|
+
* const seedHex = await SecureStore.getItemAsync('umbra_master_seed');
|
|
2331
|
+
* return hexToBytes(seedHex) as MasterSeed;
|
|
2332
|
+
* },
|
|
2333
|
+
* };
|
|
2334
|
+
* ```
|
|
2335
|
+
*
|
|
2336
|
+
* @public
|
|
2337
|
+
*/
|
|
2338
|
+
interface IUmbraClient {
|
|
2339
|
+
/**
|
|
2340
|
+
* The signer used for signing transactions and messages.
|
|
2341
|
+
*
|
|
2342
|
+
* This signer is used for:
|
|
2343
|
+
* - Signing transactions during registration, shielding, unshielding
|
|
2344
|
+
* - Paying transaction fees (as the fee payer)
|
|
2345
|
+
* - Identifying the user's address for PDA derivation
|
|
2346
|
+
* - Generating the master seed (via signature-based derivation)
|
|
2347
|
+
*
|
|
2348
|
+
* The signer is stored when the client is created via `getUmbraClient`
|
|
2349
|
+
* and reused for all subsequent operations.
|
|
2350
|
+
*/
|
|
2351
|
+
readonly signer: IUmbraSigner;
|
|
2352
|
+
/**
|
|
2353
|
+
* Network environment for the client.
|
|
2354
|
+
*
|
|
2355
|
+
* @remarks
|
|
2356
|
+
* - `'mainnet'`: Production network
|
|
2357
|
+
* - `'devnet'`: Development/testing network
|
|
2358
|
+
* - `'localnet'`: Local validator for development
|
|
2359
|
+
*/
|
|
2360
|
+
readonly network: Network;
|
|
2361
|
+
/**
|
|
2362
|
+
* Network-specific configuration for the protocol deployment.
|
|
2363
|
+
*
|
|
2364
|
+
* Contains the program ID, MXE account address, MXE X25519 public key,
|
|
2365
|
+
* Arcium program address, and cluster offset for the current network.
|
|
2366
|
+
*
|
|
2367
|
+
* @remarks
|
|
2368
|
+
* Populated at client construction time from the SDK's per-network
|
|
2369
|
+
* config (see `packages/sdk/src/constants/networks.ts`). If the
|
|
2370
|
+
* network has not been configured, client construction will throw.
|
|
2371
|
+
*/
|
|
2372
|
+
readonly networkConfig: NetworkConfig;
|
|
2373
|
+
/**
|
|
2374
|
+
* Version specifications for protocol, algorithm, and scheme.
|
|
2375
|
+
*
|
|
2376
|
+
* These version specifiers are used during key derivation to ensure
|
|
2377
|
+
* keys are derived with the correct versioning context.
|
|
2378
|
+
*
|
|
2379
|
+
* @remarks
|
|
2380
|
+
* Each specifier is a function that returns a version object with:
|
|
2381
|
+
* - `name`: The version identifier (e.g., "umbra", "kmac", "default")
|
|
2382
|
+
* - `version`: Semver string (e.g., "1.0.0")
|
|
2383
|
+
*/
|
|
2384
|
+
readonly versions: {
|
|
2385
|
+
readonly protocol: ProtocolVersionSpecifierFunction;
|
|
2386
|
+
readonly algorithm: AlgorithmVersionSpecifierFunction;
|
|
2387
|
+
readonly scheme: SchemeVersionSpecifierFunction;
|
|
2388
|
+
readonly network: NetworkSpecifierFunction;
|
|
2389
|
+
};
|
|
2390
|
+
/**
|
|
2391
|
+
* U512 offsets for key derivation.
|
|
2392
|
+
*
|
|
2393
|
+
* Each offset is used in the domain separator during key derivation
|
|
2394
|
+
* to create distinct keys for different purposes.
|
|
2395
|
+
*
|
|
2396
|
+
* @remarks
|
|
2397
|
+
* - masterViewingKey: Offset for master viewing key derivation
|
|
2398
|
+
* - poseidonPrivateKey: Offset for Poseidon private key derivation
|
|
2399
|
+
* - x25519UserAccountPrivateKey: Offset for user account X25519 keypair (token registration)
|
|
2400
|
+
* - x25519MasterViewingKeyEncryptingPrivateKey: Offset for MVK-encrypting X25519 keypair (anonymous registration)
|
|
2401
|
+
* - mintX25519PrivateKey: Offset for per-mint X25519 keypair (reencrypt_shared operations)
|
|
2402
|
+
* - rescueCommitmentBlindingFactor: Offset for Rescue commitment blinding
|
|
2403
|
+
* - randomCommitmentFactor: Offset for random commitment factor
|
|
2404
|
+
*
|
|
2405
|
+
* Time-based keys (yearly/monthly/daily) and mint viewing keys do not
|
|
2406
|
+
* use offsets as they have parameters that provide sufficient separation.
|
|
2407
|
+
*/
|
|
2408
|
+
readonly offsets: {
|
|
2409
|
+
readonly masterViewingKey: U512;
|
|
2410
|
+
readonly poseidonPrivateKey: U512;
|
|
2411
|
+
readonly x25519UserAccountPrivateKey: U512;
|
|
2412
|
+
readonly x25519MasterViewingKeyEncryptingPrivateKey: U512;
|
|
2413
|
+
readonly mintX25519PrivateKey: U512;
|
|
2414
|
+
readonly rescueCommitmentBlindingFactor: U512;
|
|
2415
|
+
readonly randomCommitmentFactor: U512;
|
|
2416
|
+
};
|
|
2417
|
+
/**
|
|
2418
|
+
* Pre-constructed Solana infrastructure dependencies.
|
|
2419
|
+
*
|
|
2420
|
+
* These dependencies are constructed by the client factory from URLs
|
|
2421
|
+
* or custom implementations provided in the factory options.
|
|
2422
|
+
*/
|
|
2423
|
+
readonly accountInfoProvider: AccountInfoProviderFunction;
|
|
2424
|
+
readonly blockhashProvider: GetLatestBlockhash;
|
|
2425
|
+
readonly transactionForwarder: TransactionForwarder;
|
|
2426
|
+
readonly epochInfoProvider: GetEpochInfo;
|
|
2427
|
+
/**
|
|
2428
|
+
* Optional computation monitor for tracking Arcium MPC computation status.
|
|
2429
|
+
*
|
|
2430
|
+
* @remarks
|
|
2431
|
+
* When provided, enables monitoring of ComputationAccount status changes
|
|
2432
|
+
* (Queued → Finalized) after queue_computation transactions. Callers invoke
|
|
2433
|
+
* `computationMonitor.awaitComputation(address)` to wait for finalization.
|
|
2434
|
+
*
|
|
2435
|
+
* If not provided, callers must implement their own monitoring logic or
|
|
2436
|
+
* use fire-and-forget behavior (send the queue transaction and don't wait
|
|
2437
|
+
* for the callback).
|
|
2438
|
+
*
|
|
2439
|
+
* Constructed by the client factory from `rpcUrl` and `rpcSubscriptionsUrl`
|
|
2440
|
+
* using the WebSocket-based strategy by default. Can be overridden in
|
|
2441
|
+
* `GetUmbraClientDeps`.
|
|
2442
|
+
*/
|
|
2443
|
+
readonly computationMonitor?: ComputationMonitor;
|
|
2444
|
+
/**
|
|
2445
|
+
* Optional indexer functions for UTXO discovery.
|
|
2446
|
+
*
|
|
2447
|
+
* Constructed by the client factory from indexerApiEndpoint URL
|
|
2448
|
+
* or custom implementations provided in the factory options.
|
|
2449
|
+
*
|
|
2450
|
+
* Required for fetching claimable UTXOs. The indexer maintains an off-chain
|
|
2451
|
+
* database of protocol state for efficient querying of UTXOs, Merkle proofs,
|
|
2452
|
+
* and nullifier status.
|
|
2453
|
+
*
|
|
2454
|
+
* @remarks
|
|
2455
|
+
* If not provided, UTXO discovery operations will not be available.
|
|
2456
|
+
*/
|
|
2457
|
+
readonly fetchMerkleProof?: MerkleProofFetcherFunction;
|
|
2458
|
+
readonly fetchBatchMerkleProof?: BatchMerkleProofFetcherFunction;
|
|
2459
|
+
readonly fetchUtxoData?: UtxoDataFetcherFunction;
|
|
2460
|
+
/**
|
|
2461
|
+
* Master seed storage operations.
|
|
2462
|
+
*
|
|
2463
|
+
* The master seed is the root of all key derivation. These functions
|
|
2464
|
+
* control how the seed is loaded, stored, and generated.
|
|
2465
|
+
*
|
|
2466
|
+
* @remarks
|
|
2467
|
+
* - `load`: Attempts to load existing seed from storage
|
|
2468
|
+
* - `store`: Persists the seed to storage
|
|
2469
|
+
* - `generate`: Generates a new seed via signature-based derivation
|
|
2470
|
+
* - `getMasterSeed`: Convenience method combining load and generate
|
|
2471
|
+
*/
|
|
2472
|
+
readonly masterSeed: {
|
|
2473
|
+
readonly load: MasterSeedLoaderFunction;
|
|
2474
|
+
readonly store: MasterSeedStorerFunction;
|
|
2475
|
+
readonly generate: MasterSeedGeneratorFunction;
|
|
2476
|
+
readonly getMasterSeed: () => Promise<MasterSeed>;
|
|
2477
|
+
};
|
|
2478
|
+
/**
|
|
2479
|
+
* Master viewing key storage operations.
|
|
2480
|
+
*
|
|
2481
|
+
* @remarks
|
|
2482
|
+
* The master viewing key is derived from the master seed and is used
|
|
2483
|
+
* to derive all other viewing keys in the hierarchy.
|
|
2484
|
+
*/
|
|
2485
|
+
readonly masterViewingKey: {
|
|
2486
|
+
readonly load: LoaderFunction<MasterViewingKey>;
|
|
2487
|
+
readonly store: StorerFunction<MasterViewingKey>;
|
|
2488
|
+
readonly generate: GeneratorFunction<MasterViewingKey>;
|
|
2489
|
+
};
|
|
2490
|
+
/**
|
|
2491
|
+
* Poseidon private key storage operations.
|
|
2492
|
+
*
|
|
2493
|
+
* @remarks
|
|
2494
|
+
* Used for Poseidon hash-based cryptographic operations.
|
|
2495
|
+
*/
|
|
2496
|
+
readonly poseidonPrivateKey: {
|
|
2497
|
+
readonly load: LoaderFunction<PoseidonKey>;
|
|
2498
|
+
readonly store: StorerFunction<PoseidonKey>;
|
|
2499
|
+
readonly generate: GeneratorFunction<PoseidonKey>;
|
|
2500
|
+
};
|
|
2501
|
+
/**
|
|
2502
|
+
* X25519 private key storage operations.
|
|
2503
|
+
*
|
|
2504
|
+
* @remarks
|
|
2505
|
+
* Used for elliptic curve Diffie-Hellman key exchange.
|
|
2506
|
+
*/
|
|
2507
|
+
readonly x25519PrivateKey: {
|
|
2508
|
+
readonly load: LoaderFunction<X25519PrivateKey>;
|
|
2509
|
+
readonly store: StorerFunction<X25519PrivateKey>;
|
|
2510
|
+
readonly generate: GeneratorFunction<X25519PrivateKey>;
|
|
2511
|
+
};
|
|
2512
|
+
/**
|
|
2513
|
+
* Yearly viewing key storage operations.
|
|
2514
|
+
*
|
|
2515
|
+
* @remarks
|
|
2516
|
+
* Time-based viewing keys are parameterized by year.
|
|
2517
|
+
*/
|
|
2518
|
+
readonly yearlyViewingKey: {
|
|
2519
|
+
readonly load: ParameterizedLoaderFunction<YearlyViewingKey, {
|
|
2520
|
+
year: Year;
|
|
2521
|
+
}>;
|
|
2522
|
+
readonly store: ParameterizedStorerFunction<YearlyViewingKey, {
|
|
2523
|
+
year: Year;
|
|
2524
|
+
}>;
|
|
2525
|
+
readonly generate: ParameterizedGeneratorFunction<YearlyViewingKey, {
|
|
2526
|
+
year: Year;
|
|
2527
|
+
}>;
|
|
2528
|
+
};
|
|
2529
|
+
/**
|
|
2530
|
+
* Monthly viewing key storage operations.
|
|
2531
|
+
*
|
|
2532
|
+
* @remarks
|
|
2533
|
+
* Time-based viewing keys are parameterized by year and month.
|
|
2534
|
+
*/
|
|
2535
|
+
readonly monthlyViewingKey: {
|
|
2536
|
+
readonly load: ParameterizedLoaderFunction<MonthlyViewingKey, {
|
|
2537
|
+
year: Year;
|
|
2538
|
+
month: Month;
|
|
2539
|
+
}>;
|
|
2540
|
+
readonly store: ParameterizedStorerFunction<MonthlyViewingKey, {
|
|
2541
|
+
year: Year;
|
|
2542
|
+
month: Month;
|
|
2543
|
+
}>;
|
|
2544
|
+
readonly generate: ParameterizedGeneratorFunction<MonthlyViewingKey, {
|
|
2545
|
+
year: Year;
|
|
2546
|
+
month: Month;
|
|
2547
|
+
}>;
|
|
2548
|
+
};
|
|
2549
|
+
/**
|
|
2550
|
+
* Daily viewing key storage operations.
|
|
2551
|
+
*
|
|
2552
|
+
* @remarks
|
|
2553
|
+
* Time-based viewing keys are parameterized by year, month, and day.
|
|
2554
|
+
*/
|
|
2555
|
+
readonly dailyViewingKey: {
|
|
2556
|
+
readonly load: ParameterizedLoaderFunction<DailyViewingKey, {
|
|
2557
|
+
year: Year;
|
|
2558
|
+
month: Month;
|
|
2559
|
+
day: Day;
|
|
2560
|
+
}>;
|
|
2561
|
+
readonly store: ParameterizedStorerFunction<DailyViewingKey, {
|
|
2562
|
+
year: Year;
|
|
2563
|
+
month: Month;
|
|
2564
|
+
day: Day;
|
|
2565
|
+
}>;
|
|
2566
|
+
readonly generate: ParameterizedGeneratorFunction<DailyViewingKey, {
|
|
2567
|
+
year: Year;
|
|
2568
|
+
month: Month;
|
|
2569
|
+
day: Day;
|
|
2570
|
+
}>;
|
|
2571
|
+
};
|
|
2572
|
+
/**
|
|
2573
|
+
* Mint viewing key storage operations.
|
|
2574
|
+
*
|
|
2575
|
+
* @remarks
|
|
2576
|
+
* Mint-specific viewing keys are parameterized by mint address.
|
|
2577
|
+
*/
|
|
2578
|
+
readonly mintViewingKey: {
|
|
2579
|
+
readonly load: ParameterizedLoaderFunction<MintViewingKey, {
|
|
2580
|
+
mint: Address;
|
|
2581
|
+
}>;
|
|
2582
|
+
readonly store: ParameterizedStorerFunction<MintViewingKey, {
|
|
2583
|
+
mint: Address;
|
|
2584
|
+
}>;
|
|
2585
|
+
readonly generate: ParameterizedGeneratorFunction<MintViewingKey, {
|
|
2586
|
+
mint: Address;
|
|
2587
|
+
}>;
|
|
2588
|
+
};
|
|
2589
|
+
/**
|
|
2590
|
+
* Rescue commitment blinding factor storage operations.
|
|
2591
|
+
*
|
|
2592
|
+
* @remarks
|
|
2593
|
+
* Used for blinding Rescue-based commitments.
|
|
2594
|
+
*/
|
|
2595
|
+
readonly rescueCommitmentBlindingFactor: {
|
|
2596
|
+
readonly load: LoaderFunction<Bn254FieldElement>;
|
|
2597
|
+
readonly store: StorerFunction<Bn254FieldElement>;
|
|
2598
|
+
readonly generate: GeneratorFunction<Bn254FieldElement>;
|
|
2599
|
+
};
|
|
2600
|
+
/**
|
|
2601
|
+
* Random commitment factor storage operations.
|
|
2602
|
+
*
|
|
2603
|
+
* @remarks
|
|
2604
|
+
* Used for randomness in commitment schemes.
|
|
2605
|
+
*/
|
|
2606
|
+
readonly randomCommitmentFactor: {
|
|
2607
|
+
readonly load: LoaderFunction<Curve25519FieldElement>;
|
|
2608
|
+
readonly store: StorerFunction<Curve25519FieldElement>;
|
|
2609
|
+
readonly generate: GeneratorFunction<Curve25519FieldElement>;
|
|
2610
|
+
};
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
export { type MerkleProofData as $, type AccountInfoProviderFunction as A, type BatchMerkleProofFetcherFunction as B, type CreateSolanaRpcFunction as C, DEFAULT_MAX_SLOT_WINDOW as D, type PollingBasedComputationMonitorDeps as E, type PollingComputationMonitorOptions as F, type GetLatestBlockhash as G, type WebsocketBasedComputationMonitorDeps as H, type IUmbraSigner as I, getPollingComputationMonitor as J, getWebsocketComputationMonitor as K, type GetMasterSeedFunction as L, type MerkleProofFetcherFunction as M, type ClaimableUtxoResult as N, type DecryptedUtxoData as O, type PollingBasedComputationMonitorConfig as P, type EpochInfoResult as Q, type FireAndForget as R, type SendAndConfirmTransactionFactoryFunction as S, type TransactionForwarder as T, type UtxoDataFetcherFunction as U, type ForwardInParallel as V, type WebsocketBasedComputationMonitorConfig as W, type ForwardSequentially as X, type H1Components as Y, type IUmbraIndexer as Z, type LatestBlockhashResult as _, type GetEpochInfo as a, type ScannedUtxoResult as a0, type SignableTransaction as a1, type SignedMessage as a2, type TimestampComponents as a3, type UtxoDataItem as a4, type UtxoFetchResult as a5, type CreateSolanaRpcSubscriptionsFunction as b, type ComputationMonitor as c, type IUmbraClient as d, type GetMerkleProofFetcherArgs as e, type GetMerkleProofFetcherDeps as f, type GetUtxoDataFetcherArgs as g, type GetUtxoDataFetcherDeps as h, type ScannedUtxoData as i, type BatchMerkleProofResult as j, type ClaimableUtxoData as k, type GetClaimableUtxoScannerFunctionArgs as l, type GetClaimableUtxoScannerFunctionDeps as m, type ClaimableUtxoScannerFunction as n, type AesDecryptorFunction as o, type AesEncryptorFunction as p, type ComputationFinalizedResult as q, type ComputationFinalizedResultWithSignature as r, type ComputationMonitorOptions as s, type ComputationMonitorProgressEvent as t, type ComputationMonitorResult as u, type ComputationMonitorResultWithSignature as v, type ComputationPrunedResult as w, DEFAULT_POLLING_INTERVAL_MS as x, DEFAULT_SAFETY_TIMEOUT_MS as y, DEFAULT_SIGNATURE_RETRIEVAL_LIMIT as z };
|