@umbra-privacy/sdk 1.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/LICENSE +21 -0
- package/README.md +122 -0
- package/dist/addresses-Brzgurv_.d.ts +145 -0
- package/dist/addresses-D_0YAS6B.d.cts +145 -0
- package/dist/chunk-2Q75CQQJ.js +12 -0
- package/dist/chunk-2Q75CQQJ.js.map +1 -0
- package/dist/chunk-7QVYU63E.js +6 -0
- package/dist/chunk-7QVYU63E.js.map +1 -0
- package/dist/chunk-BM7N6N7E.js +1883 -0
- package/dist/chunk-BM7N6N7E.js.map +1 -0
- package/dist/chunk-GXKSUB2U.cjs +4416 -0
- package/dist/chunk-GXKSUB2U.cjs.map +1 -0
- package/dist/chunk-HOEXDXRC.cjs +792 -0
- package/dist/chunk-HOEXDXRC.cjs.map +1 -0
- package/dist/chunk-MDFSBU5W.cjs +2033 -0
- package/dist/chunk-MDFSBU5W.cjs.map +1 -0
- package/dist/chunk-MQY7HDIA.js +600 -0
- package/dist/chunk-MQY7HDIA.js.map +1 -0
- package/dist/chunk-MVKTV3FT.cjs +20 -0
- package/dist/chunk-MVKTV3FT.cjs.map +1 -0
- package/dist/chunk-PG2J6V6Y.js +4094 -0
- package/dist/chunk-PG2J6V6Y.js.map +1 -0
- package/dist/chunk-PK6SKIKE.cjs +8 -0
- package/dist/chunk-PK6SKIKE.cjs.map +1 -0
- package/dist/chunk-VEGLTTYQ.cjs +621 -0
- package/dist/chunk-VEGLTTYQ.cjs.map +1 -0
- package/dist/chunk-WVHQ46DD.js +758 -0
- package/dist/chunk-WVHQ46DD.js.map +1 -0
- package/dist/constants/index.cjs +316 -0
- package/dist/constants/index.cjs.map +1 -0
- package/dist/constants/index.d.cts +739 -0
- package/dist/constants/index.d.ts +739 -0
- package/dist/constants/index.js +193 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/cryptography-BTGC72u-.d.cts +4809 -0
- package/dist/cryptography-BTGC72u-.d.ts +4809 -0
- package/dist/errors/index.cjs +141 -0
- package/dist/errors/index.cjs.map +1 -0
- package/dist/errors/index.d.cts +1415 -0
- package/dist/errors/index.d.ts +1415 -0
- package/dist/errors/index.js +4 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index-B9pDY73x.d.ts +12933 -0
- package/dist/index-CLj_zWSD.d.ts +235 -0
- package/dist/index-CX6_pIRS.d.cts +235 -0
- package/dist/index-D33yo0qB.d.cts +12933 -0
- package/dist/index.cjs +22464 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +11694 -0
- package/dist/index.d.ts +11694 -0
- package/dist/index.js +22314 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/index.cjs +4 -0
- package/dist/interfaces/index.cjs.map +1 -0
- package/dist/interfaces/index.d.cts +8 -0
- package/dist/interfaces/index.d.ts +8 -0
- package/dist/interfaces/index.js +3 -0
- package/dist/interfaces/index.js.map +1 -0
- package/dist/networks-C-orpSFW.d.ts +65 -0
- package/dist/networks-FxYERGD1.d.cts +65 -0
- package/dist/types/index.cjs +605 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +1853 -0
- package/dist/types/index.d.ts +1853 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types-BBuELtY8.d.cts +495 -0
- package/dist/types-n-sHFcgr.d.ts +495 -0
- package/dist/utils/index.cjs +1295 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +9559 -0
- package/dist/utils/index.d.ts +9559 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/versions-D9PqsEvj.d.cts +173 -0
- package/dist/versions-D9PqsEvj.d.ts +173 -0
- package/package.json +151 -0
|
@@ -0,0 +1,1853 @@
|
|
|
1
|
+
import { a3 as MasterSeed, K as U512, B as BrandedType, S as SubBrandedType, a2 as SubSubBrandedType, c as Bytes, V as U8, X as X25519PublicKey$1, k as PoseidonHash, U as U128, F as U256LeBytes } from '../cryptography-BTGC72u-.cjs';
|
|
2
|
+
export { am as AES_AUTH_TAG_LENGTH, an as AES_IV_LENGTH, ao as AES_KEY_LENGTH, ap as AES_METADATA_OVERHEAD, a7 as AesCiphertextWithMetadata, a6 as AesKey, a8 as AesPlaintext, aq as BASE85_LIMB_MAX, ar as BN254_FIELD_PRIME, a1 as Base85Limb, a0 as Base85LimbTuple, a as BeBytes, b as Bn254FieldElement, as as CURVE25519_FIELD_PRIME, at as CryptographyAssertionError, C as Curve25519FieldElement, ad as DailyViewingKey, au as ExtractBrand, av as GROTH16_G1_BYTE_LENGTH, aw as GROTH16_G2_BYTE_LENGTH, ax as GenerationSeed, ay as Groth16Proof, aj as Groth16ProofA, ak as Groth16ProofB, al as Groth16ProofC, ae as HourlyViewingKey, I as I1024, az as I1024_MAX, aA as I1024_MIN, d as I128, aB as I128_MAX, aC as I128_MIN, e as I16, aD as I16_MAX, aE as I16_MIN, f as I256, aF as I256_MAX, aG as I256_MIN, g as I32, aH as I32_MAX, aI as I32_MIN, h as I512, aJ as I512_MAX, aK as I512_MIN, i as I64, aL as I64_MAX, aM as I64_MIN, j as I8, aN as I8_MAX, aO as I8_MIN, aP as Keccak256Hash, aQ as Keccak512Hash, L as LeBytes, ab as MasterViewingKey, aR as MathematicsAssertionError, af as MintViewingKey, ag as MinuteViewingKey, ah as MonthlyViewingKey, a4 as Nonce, aS as OPTIONAL_DATA_BYTE_LENGTH, a5 as OptionalData32, P as PoseidonCiphertext, a9 as PoseidonCounter, l as PoseidonKey, aa as PoseidonKeystream, m as PoseidonPlaintext, R as RcCiphertext, n as RcCounter, o as RcEncryptionNonce, p as RcKey, q as RcPlaintext, ac as SecondViewingKey, r as SharedSecret, s as SignedInteger, aT as SubSubSubBrandedType, aU as SubSubSubSubBrandedType, t as U1024, u as U1024BeBytes, v as U1024LeBytes, aV as U1024_BYTE_LENGTH, aW as U1024_MAX, w as U128BeBytes, x as U128LeBytes, aX as U128_BYTE_LENGTH, aY as U128_MAX, y as U16, z as U16BeBytes, A as U16LeBytes, aZ as U16_BYTE_LENGTH, a_ as U16_MAX, D as U256, E as U256BeBytes, a$ as U256_BYTE_LENGTH, b0 as U256_MAX, G as U32, H as U32BeBytes, J as U32LeBytes, b1 as U32_BYTE_LENGTH, b2 as U32_MAX, M as U512BeBytes, N as U512LeBytes, b3 as U512_BYTE_LENGTH, b4 as U512_MAX, O as U64, Q as U64BeBytes, T as U64LeBytes, b5 as U64_BYTE_LENGTH, b6 as U64_MAX, W as U8BeBytes, Y as U8LeBytes, b7 as U8_BYTE_LENGTH, b8 as U8_MAX, Z as UnsignedInteger, b9 as UnwrapBrand, _ as X25519Bytes, ba as X25519Keypair, $ as X25519PrivateKey, bb as X25519_BYTE_LENGTH, ai as YearlyViewingKey, bc as ZK_PROOF_BYTE_LENGTH, bd as ZkProofBytes, be as assertAesCiphertextWithMetadata, bf as assertAesKey, bg as assertAesPlaintext, bh as assertBase85Limb, bi as assertBeBytes, bj as assertBn254FieldElement, bk as assertBytes, bl as assertCurve25519FieldElement, bm as assertDailyViewingKey, bn as assertGenerationSeed, bo as assertGroth16ProofA, bp as assertGroth16ProofB, bq as assertGroth16ProofC, br as assertHourlyViewingKey, bs as assertI1024, bt as assertI128, bu as assertI16, bv as assertI256, bw as assertI32, bx as assertI512, by as assertI64, bz as assertI8, bA as assertKeccak256Hash, bB as assertKeccak512Hash, bC as assertLeBytes, bD as assertMasterSeed, bE as assertMasterViewingKey, bF as assertMintViewingKey, bG as assertMinuteViewingKey, bH as assertMonthlyViewingKey, bI as assertOptionalData32, bJ as assertPoseidonCiphertext, bK as assertPoseidonCounter, bL as assertPoseidonHash, bM as assertPoseidonKey, bN as assertPoseidonKeystream, bO as assertPoseidonPlaintext, bP as assertRcCiphertext, bQ as assertRcCounter, bR as assertRcEncryptionNonce, bS as assertRcKey, bT as assertRcPlaintext, bU as assertSecondViewingKey, bV as assertSharedSecret, bW as assertSignedInteger, bX as assertU1024, bY as assertU1024BeBytes, bZ as assertU1024LeBytes, b_ as assertU128, b$ as assertU128BeBytes, c0 as assertU128LeBytes, c1 as assertU16, c2 as assertU16BeBytes, c3 as assertU16LeBytes, c4 as assertU256, c5 as assertU256BeBytes, c6 as assertU256LeBytes, c7 as assertU32, c8 as assertU32BeBytes, c9 as assertU32LeBytes, ca as assertU512, cb as assertU512BeBytes, cc as assertU512LeBytes, cd as assertU64, ce as assertU64BeBytes, cf as assertU64LeBytes, cg as assertU8, ch as assertU8BeBytes, ci as assertU8LeBytes, cj as assertUnsignedInteger, ck as assertX25519Bytes, cl as assertX25519Keypair, cm as assertX25519PrivateKey, cn as assertX25519PublicKey, co as assertYearlyViewingKey, cp as assertZkProofBytes } from '../cryptography-BTGC72u-.cjs';
|
|
3
|
+
import { N as Network } from '../versions-D9PqsEvj.cjs';
|
|
4
|
+
import { Transaction, TransactionWithBlockhashLifetime } from '@solana/kit';
|
|
5
|
+
export { b as DAY_MAX, c as DAY_MIN, D as Day, d as HOUR_MAX, e as HOUR_MIN, H as Hour, f as MINUTE_MAX, g as MINUTE_MIN, h as MONTH_MAX, i as MONTH_MIN, a as Minute, M as Month, j as SECOND_MAX, k as SECOND_MIN, S as Second, T as TemporalAssertionError, l as TimestampComponent, m as YEAR_MAX, n as YEAR_MIN, Y as Year, o as assertDay, p as assertHour, q as assertMinute, r as assertMonth, s as assertSecond, t as assertTimestampComponent, u as assertYear } from '../types-BBuELtY8.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Storage Types for Key Management
|
|
9
|
+
*
|
|
10
|
+
* This module defines types and interfaces for loading, storing, and generating
|
|
11
|
+
* cryptographic keys and derived values. The storage system supports both persistent
|
|
12
|
+
* storage (e.g., IndexedDB, local storage) and ephemeral storage (in-memory only).
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* The storage abstraction allows callers to supply custom persistence backends for
|
|
16
|
+
* the SDK's cryptographic keys without coupling the SDK to any particular storage
|
|
17
|
+
* technology. The most security-sensitive type here is `MasterSeed`, whose loader,
|
|
18
|
+
* storer, and generator functions control the full lifecycle of the root key.
|
|
19
|
+
*
|
|
20
|
+
* All load/store operations are asynchronous to support remote key management services
|
|
21
|
+
* (e.g., cloud HSMs, encrypted databases) without blocking the main thread.
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
* @module types/storage
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Result of a load operation.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* A discriminated union that signals whether a stored value was found. When `exists` is
|
|
32
|
+
* `true`, the `seed` field contains the loaded value. When `exists` is `false`, the caller
|
|
33
|
+
* should generate a new value and store it.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam T - The type of value being loaded
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const result = await loadMasterSeed();
|
|
40
|
+
* if (result.exists) {
|
|
41
|
+
* // Use result.seed
|
|
42
|
+
* } else {
|
|
43
|
+
* // Generate and store a new seed
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
type LoadResult<T> = {
|
|
50
|
+
readonly exists: true;
|
|
51
|
+
readonly seed: T;
|
|
52
|
+
} | {
|
|
53
|
+
readonly exists: false;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Result of a store operation.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* A discriminated union that signals whether the store succeeded. When `success` is `false`,
|
|
60
|
+
* the `error` field contains a human-readable description of what went wrong (e.g., quota
|
|
61
|
+
* exceeded, permission denied, serialization failure).
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const result = await storeMasterSeed(seed);
|
|
66
|
+
* if (!result.success) {
|
|
67
|
+
* console.error("Failed to persist seed:", result.error);
|
|
68
|
+
* // Optionally continue with in-memory seed only
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @public
|
|
73
|
+
*/
|
|
74
|
+
type StoreResult = {
|
|
75
|
+
readonly success: true;
|
|
76
|
+
} | {
|
|
77
|
+
readonly success: false;
|
|
78
|
+
readonly error: string;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Context information for key storage operations.
|
|
82
|
+
*
|
|
83
|
+
* This context is passed to load and store functions to provide all the necessary
|
|
84
|
+
* information about the key being stored or loaded, including versioning information,
|
|
85
|
+
* network, and derivation parameters.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* The context ensures that keys are stored and retrieved with the correct version and
|
|
89
|
+
* parameter information, preventing version mismatches across SDK upgrades. The combination
|
|
90
|
+
* of `signerAddress`, `network`, `domainSeparator`, and all three version strings forms a
|
|
91
|
+
* unique storage key that can be used as a lookup identifier in any key-value store.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const context: KeyStorageContext = {
|
|
96
|
+
* signerAddress: "7xKXt...",
|
|
97
|
+
* domainSeparator: "MasterViewingKey/0",
|
|
98
|
+
* network: "mainnet-beta",
|
|
99
|
+
* protocolVersion: "1.0.0",
|
|
100
|
+
* algorithmVersion: "1.0.0",
|
|
101
|
+
* schemeVersion: "1.0.0",
|
|
102
|
+
* };
|
|
103
|
+
* const result = await loaderFn(context);
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
interface KeyStorageContext {
|
|
109
|
+
/**
|
|
110
|
+
* The signer's public key / wallet address (user identifier).
|
|
111
|
+
*
|
|
112
|
+
* @remarks
|
|
113
|
+
* Used to namespace stored keys per user so that multiple wallets can use the same
|
|
114
|
+
* storage backend without interference.
|
|
115
|
+
* @readonly
|
|
116
|
+
*/
|
|
117
|
+
readonly signerAddress: string;
|
|
118
|
+
/**
|
|
119
|
+
* Domain separator for key derivation (e.g., `"MasterViewingKey/0"`).
|
|
120
|
+
*
|
|
121
|
+
* @remarks
|
|
122
|
+
* The domain separator ensures that keys derived for different purposes are
|
|
123
|
+
* cryptographically independent even when derived from the same root material.
|
|
124
|
+
* @readonly
|
|
125
|
+
*/
|
|
126
|
+
readonly domainSeparator: string;
|
|
127
|
+
/**
|
|
128
|
+
* Network environment (e.g., `"mainnet-beta"`, `"devnet"`).
|
|
129
|
+
*
|
|
130
|
+
* @remarks
|
|
131
|
+
* Prevents keys generated on devnet from being mistakenly used on mainnet.
|
|
132
|
+
* @readonly
|
|
133
|
+
*/
|
|
134
|
+
readonly network: Network;
|
|
135
|
+
/**
|
|
136
|
+
* Protocol version string (e.g., `"1.0.0"`).
|
|
137
|
+
*
|
|
138
|
+
* @remarks
|
|
139
|
+
* Bumped when the on-chain protocol changes in a way that requires fresh keys.
|
|
140
|
+
* @readonly
|
|
141
|
+
*/
|
|
142
|
+
readonly protocolVersion: string;
|
|
143
|
+
/**
|
|
144
|
+
* Algorithm version string (e.g., `"1.0.0"`).
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* Bumped when the cryptographic algorithm (hash function, cipher) changes.
|
|
148
|
+
* @readonly
|
|
149
|
+
*/
|
|
150
|
+
readonly algorithmVersion: string;
|
|
151
|
+
/**
|
|
152
|
+
* Scheme version string (e.g., `"1.0.0"`).
|
|
153
|
+
*
|
|
154
|
+
* @remarks
|
|
155
|
+
* Bumped when the key derivation scheme (domain separators, input ordering) changes.
|
|
156
|
+
* @readonly
|
|
157
|
+
*/
|
|
158
|
+
readonly schemeVersion: string;
|
|
159
|
+
/**
|
|
160
|
+
* Optional derivation parameters (e.g., `{ year: "2024", mint: "7xKXt..." }`).
|
|
161
|
+
*
|
|
162
|
+
* @remarks
|
|
163
|
+
* Supplied for parameterized keys (yearly viewing keys, mint-specific keys) to distinguish
|
|
164
|
+
* storage entries for different time periods or token mints.
|
|
165
|
+
* @readonly
|
|
166
|
+
*/
|
|
167
|
+
readonly derivationParams?: Record<string, string>;
|
|
168
|
+
/**
|
|
169
|
+
* Optional offset used in key derivation.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* Used when a key is derived with an explicit 512-bit offset (e.g., for generation-indexed
|
|
173
|
+
* AES keys). Most keys do not use this field.
|
|
174
|
+
* @readonly
|
|
175
|
+
*/
|
|
176
|
+
readonly offset?: U512;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Loads a value from storage.
|
|
180
|
+
*
|
|
181
|
+
* @remarks
|
|
182
|
+
* Implementations should be idempotent: calling load multiple times with the same context
|
|
183
|
+
* must return the same result (assuming no concurrent stores).
|
|
184
|
+
*
|
|
185
|
+
* @typeParam T - The type of value being loaded
|
|
186
|
+
* @param context - Storage context with version and parameter information
|
|
187
|
+
* @returns A promise that resolves to a `LoadResult` indicating whether the value exists
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const loader: LoaderFunction<MasterViewingKey> = async (context) => {
|
|
192
|
+
* const raw = await db.get(buildKey(context));
|
|
193
|
+
* if (!raw) return { exists: false };
|
|
194
|
+
* return { exists: true, seed: parseMvk(raw) };
|
|
195
|
+
* };
|
|
196
|
+
* ```
|
|
197
|
+
*
|
|
198
|
+
* @public
|
|
199
|
+
*/
|
|
200
|
+
type LoaderFunction<T> = (context: KeyStorageContext) => Promise<LoadResult<T>>;
|
|
201
|
+
/**
|
|
202
|
+
* Stores a value to storage.
|
|
203
|
+
*
|
|
204
|
+
* @remarks
|
|
205
|
+
* Implementations should overwrite any previously stored value for the same context.
|
|
206
|
+
* Failures must be reported via a `StoreResult` with `success: false` rather than throwing,
|
|
207
|
+
* unless the failure is catastrophic.
|
|
208
|
+
*
|
|
209
|
+
* @typeParam T - The type of value being stored
|
|
210
|
+
* @param value - The value to store
|
|
211
|
+
* @param context - Storage context with version and parameter information
|
|
212
|
+
* @returns A promise that resolves to a `StoreResult` indicating success or describing the failure
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* const storer: StorerFunction<MasterViewingKey> = async (value, context) => {
|
|
217
|
+
* try {
|
|
218
|
+
* await db.put(buildKey(context), serializeMvk(value));
|
|
219
|
+
* return { success: true };
|
|
220
|
+
* } catch (error) {
|
|
221
|
+
* return { success: false, error: String(error) };
|
|
222
|
+
* }
|
|
223
|
+
* };
|
|
224
|
+
* ```
|
|
225
|
+
*
|
|
226
|
+
* @public
|
|
227
|
+
*/
|
|
228
|
+
type StorerFunction<T> = (value: T, context: KeyStorageContext) => Promise<StoreResult>;
|
|
229
|
+
/**
|
|
230
|
+
* Generates a new value.
|
|
231
|
+
*
|
|
232
|
+
* @remarks
|
|
233
|
+
* Called when no stored value is found. The implementation should use the context to
|
|
234
|
+
* deterministically or randomly generate a fresh value. For security-critical keys, the
|
|
235
|
+
* generator should use a cryptographically secure random source.
|
|
236
|
+
*
|
|
237
|
+
* @typeParam T - The type of value being generated
|
|
238
|
+
* @param context - Storage context with version and parameter information
|
|
239
|
+
* @returns A promise that resolves to the freshly generated value
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const generator: GeneratorFunction<MasterViewingKey> = async (context) => {
|
|
244
|
+
* const entropy = crypto.getRandomValues(new Uint8Array(32));
|
|
245
|
+
* return deriveMasterViewingKey(entropy, context.domainSeparator);
|
|
246
|
+
* };
|
|
247
|
+
* ```
|
|
248
|
+
*
|
|
249
|
+
* @public
|
|
250
|
+
*/
|
|
251
|
+
type GeneratorFunction<T> = (context: KeyStorageContext) => Promise<T>;
|
|
252
|
+
/**
|
|
253
|
+
* Loads a value that requires additional parameters (e.g., year, mint address).
|
|
254
|
+
*
|
|
255
|
+
* @remarks
|
|
256
|
+
* Similar to `LoaderFunction`, but accepts an extra `params` argument to disambiguate
|
|
257
|
+
* storage entries when the same key type can exist for multiple parameter values (e.g.,
|
|
258
|
+
* a yearly viewing key for 2023 vs. 2024).
|
|
259
|
+
*
|
|
260
|
+
* @typeParam T - The type of value being loaded
|
|
261
|
+
* @typeParam TParams - The type of extra parameters required for lookup
|
|
262
|
+
* @param params - Parameters for key derivation and lookup (e.g., `{ year: 2024n }`)
|
|
263
|
+
* @param context - Storage context with version and base parameter information
|
|
264
|
+
* @returns A promise that resolves to a `LoadResult` indicating whether the value exists
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* ```typescript
|
|
268
|
+
* const loader: ParameterizedLoaderFunction<YearlyViewingKey, { year: bigint }> =
|
|
269
|
+
* async (params, context) => {
|
|
270
|
+
* const key = buildYearKey(context, params.year);
|
|
271
|
+
* const raw = await db.get(key);
|
|
272
|
+
* if (!raw) return { exists: false };
|
|
273
|
+
* return { exists: true, seed: parseYvk(raw) };
|
|
274
|
+
* };
|
|
275
|
+
* ```
|
|
276
|
+
*
|
|
277
|
+
* @see {@link LoaderFunction}
|
|
278
|
+
* @public
|
|
279
|
+
*/
|
|
280
|
+
type ParameterizedLoaderFunction<T, TParams> = (params: TParams, context: KeyStorageContext) => Promise<LoadResult<T>>;
|
|
281
|
+
/**
|
|
282
|
+
* Stores a value that requires additional parameters.
|
|
283
|
+
*
|
|
284
|
+
* @remarks
|
|
285
|
+
* Similar to `StorerFunction`, but accepts an extra `params` argument so the implementation
|
|
286
|
+
* can construct a unique storage key for each parameter combination.
|
|
287
|
+
*
|
|
288
|
+
* @typeParam T - The type of value being stored
|
|
289
|
+
* @typeParam TParams - The type of extra parameters required for storage
|
|
290
|
+
* @param value - The value to store
|
|
291
|
+
* @param params - Parameters for key derivation and storage (e.g., `{ year: 2024n }`)
|
|
292
|
+
* @param context - Storage context with version and base parameter information
|
|
293
|
+
* @returns A promise that resolves to a `StoreResult` indicating success or describing the failure
|
|
294
|
+
*
|
|
295
|
+
* @see {@link StorerFunction}
|
|
296
|
+
* @public
|
|
297
|
+
*/
|
|
298
|
+
type ParameterizedStorerFunction<T, TParams> = (value: T, params: TParams, context: KeyStorageContext) => Promise<StoreResult>;
|
|
299
|
+
/**
|
|
300
|
+
* Generates a value that requires additional parameters.
|
|
301
|
+
*
|
|
302
|
+
* @remarks
|
|
303
|
+
* Similar to `GeneratorFunction`, but accepts `params` to derive a key that is specific to
|
|
304
|
+
* a given parameter set (e.g., deriving a yearly viewing key scoped to a particular year and mint).
|
|
305
|
+
*
|
|
306
|
+
* @typeParam T - The type of value being generated
|
|
307
|
+
* @typeParam TParams - The type of extra parameters required for derivation
|
|
308
|
+
* @param params - Parameters for key derivation (e.g., `{ year: 2024n, mint: "7xKXt..." }`)
|
|
309
|
+
* @param context - Storage context with version and base parameter information
|
|
310
|
+
* @returns A promise that resolves to the freshly generated value
|
|
311
|
+
*
|
|
312
|
+
* @see {@link GeneratorFunction}
|
|
313
|
+
* @public
|
|
314
|
+
*/
|
|
315
|
+
type ParameterizedGeneratorFunction<T, TParams> = (params: TParams, context: KeyStorageContext) => Promise<T>;
|
|
316
|
+
/**
|
|
317
|
+
* Loads the master seed from storage.
|
|
318
|
+
*
|
|
319
|
+
* @remarks
|
|
320
|
+
* The master seed is the root of all key derivation. This function should:
|
|
321
|
+
* - Check if a master seed exists in storage
|
|
322
|
+
* - Return the seed if it exists
|
|
323
|
+
* - Return `{ exists: false }` if no seed is stored (caller will then generate one)
|
|
324
|
+
*
|
|
325
|
+
* This function takes no context argument because the master seed is not scoped to any
|
|
326
|
+
* particular set of derivation parameters — it is the single universal root.
|
|
327
|
+
*
|
|
328
|
+
* @returns A promise that resolves to a `LoadResult` containing the master seed if found
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```typescript
|
|
332
|
+
* const loader: MasterSeedLoaderFunction = async () => {
|
|
333
|
+
* const encrypted = localStorage.getItem("umbraMasterSeed");
|
|
334
|
+
* if (!encrypted) return { exists: false };
|
|
335
|
+
* const seed = await decrypt(encrypted);
|
|
336
|
+
* return { exists: true, seed };
|
|
337
|
+
* };
|
|
338
|
+
* ```
|
|
339
|
+
*
|
|
340
|
+
* @see {@link MasterSeed}
|
|
341
|
+
* @see {@link MasterSeedStorerFunction}
|
|
342
|
+
* @see {@link MasterSeedGeneratorFunction}
|
|
343
|
+
* @public
|
|
344
|
+
*/
|
|
345
|
+
type MasterSeedLoaderFunction = () => Promise<LoadResult<MasterSeed>>;
|
|
346
|
+
/**
|
|
347
|
+
* Stores the master seed to storage.
|
|
348
|
+
*
|
|
349
|
+
* @remarks
|
|
350
|
+
* This function should securely persist the master seed. The default SDK implementation
|
|
351
|
+
* uses an in-memory closure-based store, but production applications should provide a
|
|
352
|
+
* custom implementation that encrypts the seed before persisting it (e.g., AES-GCM
|
|
353
|
+
* with a password-derived key, or storing in a platform secure enclave).
|
|
354
|
+
*
|
|
355
|
+
* Security guidance:
|
|
356
|
+
* - Never store the raw seed in plaintext on disk or in localStorage
|
|
357
|
+
* - Use authenticated encryption (e.g., AES-256-GCM) to protect the seed at rest
|
|
358
|
+
* - Consider using the platform keychain (Keychain on iOS/macOS, Keystore on Android)
|
|
359
|
+
*
|
|
360
|
+
* @param seed - The master seed to store (64 bytes)
|
|
361
|
+
* @returns A promise that resolves to a `StoreResult` indicating success or describing the failure
|
|
362
|
+
*
|
|
363
|
+
* @example
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const storer: MasterSeedStorerFunction = async (seed) => {
|
|
366
|
+
* try {
|
|
367
|
+
* const encrypted = await encryptWithUserPassword(seed);
|
|
368
|
+
* localStorage.setItem("umbraMasterSeed", encrypted);
|
|
369
|
+
* return { success: true };
|
|
370
|
+
* } catch (error) {
|
|
371
|
+
* return { success: false, error: String(error) };
|
|
372
|
+
* }
|
|
373
|
+
* };
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* @see {@link MasterSeed}
|
|
377
|
+
* @see {@link MasterSeedLoaderFunction}
|
|
378
|
+
* @public
|
|
379
|
+
*/
|
|
380
|
+
type MasterSeedStorerFunction = (seed: MasterSeed) => Promise<StoreResult>;
|
|
381
|
+
/**
|
|
382
|
+
* Generates a new master seed.
|
|
383
|
+
*
|
|
384
|
+
* @remarks
|
|
385
|
+
* The default SDK implementation follows these steps:
|
|
386
|
+
* 1. Constructs the message: `"Umbra Privacy - Master Seed Generation - {signerAddress}"`
|
|
387
|
+
* 2. Signs the message with the user's wallet signer (producing a deterministic Ed25519 signature)
|
|
388
|
+
* 3. Hashes the signature with KMAC256 (key derivation length = 64) to produce the 512-bit seed
|
|
389
|
+
*
|
|
390
|
+
* This approach makes seed generation deterministic and tied to the user's wallet keypair,
|
|
391
|
+
* so the same wallet always regenerates the same master seed. Applications that prefer
|
|
392
|
+
* non-deterministic seeds can supply a custom generator that uses `crypto.getRandomValues`.
|
|
393
|
+
*
|
|
394
|
+
* @returns A promise that resolves to the freshly generated `MasterSeed` (64 bytes)
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```typescript
|
|
398
|
+
* // Deterministic (default): derived from wallet signature
|
|
399
|
+
* const generator: MasterSeedGeneratorFunction = async () => {
|
|
400
|
+
* const msg = `Umbra Privacy - Master Seed Generation - ${signerAddress}`;
|
|
401
|
+
* const sig = await signer.signMessage(msg);
|
|
402
|
+
* return kmac256(sig, { dkLen: 64 }) as MasterSeed;
|
|
403
|
+
* };
|
|
404
|
+
* ```
|
|
405
|
+
*
|
|
406
|
+
* @see {@link MasterSeed}
|
|
407
|
+
* @see {@link MasterSeedLoaderFunction}
|
|
408
|
+
* @see {@link MasterSeedStorerFunction}
|
|
409
|
+
* @public
|
|
410
|
+
*/
|
|
411
|
+
type MasterSeedGeneratorFunction = () => Promise<MasterSeed>;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Solana Types Module.
|
|
415
|
+
*
|
|
416
|
+
* This module provides branded types and runtime assertion functions for
|
|
417
|
+
* Solana-specific data structures. By attaching nominal brands to primitive
|
|
418
|
+
* `string` and `Uint8Array` values, and to `@solana/kit` transaction objects,
|
|
419
|
+
* the SDK achieves compile-time safety that prevents common mistakes such as:
|
|
420
|
+
* - Passing a raw string where a `TransactionSignature` is expected.
|
|
421
|
+
* - Submitting an unsigned transaction to a `TransactionForwarder`.
|
|
422
|
+
* - Mixing up different byte-array representations.
|
|
423
|
+
*
|
|
424
|
+
* @remarks
|
|
425
|
+
* **Type hierarchy overview:**
|
|
426
|
+
*
|
|
427
|
+
* String types:
|
|
428
|
+
* - {@link String} — root branded string (base type for all string sub-brands)
|
|
429
|
+
* - {@link TransactionSignature} — base58-encoded Ed25519 signature string
|
|
430
|
+
*
|
|
431
|
+
* Byte array types:
|
|
432
|
+
* - {@link SolanaBytes} — root branded `Uint8Array` for Solana binary data
|
|
433
|
+
* - {@link SignatureBytes} — exactly 64-byte raw Ed25519 signature
|
|
434
|
+
*
|
|
435
|
+
* Transaction types (wrapping `@solana/kit`'s `Transaction`):
|
|
436
|
+
* - {@link UnsignedTransaction} — no signatures present
|
|
437
|
+
* - {@link SignedTransaction} — at least one signature; includes blockhash lifetime
|
|
438
|
+
* - {@link PartiallySignedTransaction} — some but not all required signatures
|
|
439
|
+
* - {@link FullySignedTransaction} — all required signatures present; ready for submission
|
|
440
|
+
*
|
|
441
|
+
* **Runtime assertions:**
|
|
442
|
+
* - {@link assertString} — narrows `string` to `String`
|
|
443
|
+
* - {@link assertTransactionSignature} — narrows `string` to `TransactionSignature` (base58 validated)
|
|
444
|
+
* - {@link assertSolanaBytes} — narrows `Uint8Array` to `SolanaBytes`
|
|
445
|
+
* - {@link assertSignatureBytes} — narrows `Uint8Array` to `SignatureBytes` (64-byte validated)
|
|
446
|
+
*
|
|
447
|
+
* **Error class:**
|
|
448
|
+
* - {@link SolanaAssertionError} — thrown by all assertion functions with structured context
|
|
449
|
+
*
|
|
450
|
+
* @example
|
|
451
|
+
* ```typescript
|
|
452
|
+
* import {
|
|
453
|
+
* assertTransactionSignature,
|
|
454
|
+
* assertSignatureBytes,
|
|
455
|
+
* TransactionSignature,
|
|
456
|
+
* SignatureBytes,
|
|
457
|
+
* } from "./types";
|
|
458
|
+
*
|
|
459
|
+
* const sigString = "5wHu1qwD7q5menT3ydT9VdFPQfkLaWvqPgVbqsM1qwD7";
|
|
460
|
+
* assertTransactionSignature(sigString);
|
|
461
|
+
* // sigString is now typed as TransactionSignature
|
|
462
|
+
*
|
|
463
|
+
* const sigBytes = new Uint8Array(64);
|
|
464
|
+
* assertSignatureBytes(sigBytes);
|
|
465
|
+
* // sigBytes is now typed as SignatureBytes
|
|
466
|
+
* ```
|
|
467
|
+
*
|
|
468
|
+
* @see {@link BrandedType} for the underlying branding utility
|
|
469
|
+
* @see {@link Bytes} for the generic byte array base type
|
|
470
|
+
*
|
|
471
|
+
* @packageDocumentation
|
|
472
|
+
* @module types/solana
|
|
473
|
+
*/
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Length of an Ed25519 signature in bytes.
|
|
477
|
+
*
|
|
478
|
+
* The Ed25519 signature scheme, used by Solana for all transaction signing,
|
|
479
|
+
* always produces exactly 64-byte signatures:
|
|
480
|
+
* - Bytes 0–31: the R component (a compressed point on the Ed25519 curve)
|
|
481
|
+
* - Bytes 32–63: the S component (a scalar value)
|
|
482
|
+
*
|
|
483
|
+
* @remarks
|
|
484
|
+
* This constant is used by {@link assertSignatureBytes} to validate the length
|
|
485
|
+
* of raw signature byte arrays before branding them as {@link SignatureBytes}.
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* ```typescript
|
|
489
|
+
* const raw = new Uint8Array(SIGNATURE_BYTE_LENGTH); // 64-byte zeroed buffer
|
|
490
|
+
* assertSignatureBytes(raw);
|
|
491
|
+
* ```
|
|
492
|
+
*
|
|
493
|
+
* @public
|
|
494
|
+
*/
|
|
495
|
+
declare const SIGNATURE_BYTE_LENGTH = 64;
|
|
496
|
+
/**
|
|
497
|
+
* Error thrown when a Solana type assertion function fails.
|
|
498
|
+
*
|
|
499
|
+
* All assertion functions in this module (`assertString`,
|
|
500
|
+
* `assertTransactionSignature`, `assertSolanaBytes`, `assertSignatureBytes`)
|
|
501
|
+
* throw `SolanaAssertionError` when the supplied value does not satisfy the
|
|
502
|
+
* type constraint. The error carries structured fields that make it easy to
|
|
503
|
+
* log diagnostics or write type-specific error handlers.
|
|
504
|
+
*
|
|
505
|
+
* @remarks
|
|
506
|
+
* `SolanaAssertionError` sets `Error.captureStackTrace` (available in V8
|
|
507
|
+
* environments) to exclude the assertion function's own frame from the stack,
|
|
508
|
+
* making the stack trace point to the caller rather than the assertion body.
|
|
509
|
+
*
|
|
510
|
+
* The prototype is explicitly reset via `Object.setPrototypeOf` to ensure
|
|
511
|
+
* `instanceof` works correctly across CommonJS module boundary re-exports.
|
|
512
|
+
*
|
|
513
|
+
* @example
|
|
514
|
+
* Catching and inspecting a failed assertion:
|
|
515
|
+
* ```typescript
|
|
516
|
+
* import { assertTransactionSignature, SolanaAssertionError } from "./types";
|
|
517
|
+
*
|
|
518
|
+
* try {
|
|
519
|
+
* assertTransactionSignature("invalid!sig");
|
|
520
|
+
* } catch (error) {
|
|
521
|
+
* if (error instanceof SolanaAssertionError) {
|
|
522
|
+
* console.error(`Expected: ${error.expectedType}`);
|
|
523
|
+
* console.error(`Constraint: ${error.constraint}`);
|
|
524
|
+
* console.error(`Got: ${String(error.value)}`);
|
|
525
|
+
* }
|
|
526
|
+
* }
|
|
527
|
+
* ```
|
|
528
|
+
*
|
|
529
|
+
* @public
|
|
530
|
+
*/
|
|
531
|
+
declare class SolanaAssertionError extends Error {
|
|
532
|
+
/**
|
|
533
|
+
* The actual value that was passed to the assertion function and failed
|
|
534
|
+
* the type check.
|
|
535
|
+
*
|
|
536
|
+
* @remarks
|
|
537
|
+
* Typed as `unknown` because assertion functions accept values of unknown
|
|
538
|
+
* type before narrowing. Inspect carefully — the value may be `undefined`,
|
|
539
|
+
* `null`, or any primitive or object.
|
|
540
|
+
*/
|
|
541
|
+
readonly value: unknown;
|
|
542
|
+
/**
|
|
543
|
+
* The name of the expected type (e.g., `"TransactionSignature"`,
|
|
544
|
+
* `"SignatureBytes"`).
|
|
545
|
+
*
|
|
546
|
+
* @remarks
|
|
547
|
+
* Matches the TypeScript type name for the intended branded type, not a
|
|
548
|
+
* JavaScript `typeof` string.
|
|
549
|
+
*/
|
|
550
|
+
readonly expectedType: string;
|
|
551
|
+
/**
|
|
552
|
+
* A human-readable description of the specific constraint that was violated,
|
|
553
|
+
* if applicable.
|
|
554
|
+
*
|
|
555
|
+
* @remarks
|
|
556
|
+
* Examples:
|
|
557
|
+
* - `"length > 0"` — empty string passed to `assertTransactionSignature`
|
|
558
|
+
* - `"length === 64"` — wrong byte count passed to `assertSignatureBytes`
|
|
559
|
+
* - `"characters must be in base58 alphabet: 123...xyz"` — invalid character
|
|
560
|
+
*
|
|
561
|
+
* `undefined` when the only constraint is the TypeScript type itself
|
|
562
|
+
* (e.g., `assertString` only checks `typeof value === "string"`).
|
|
563
|
+
*/
|
|
564
|
+
readonly constraint: string | undefined;
|
|
565
|
+
/**
|
|
566
|
+
* Creates a new `SolanaAssertionError`.
|
|
567
|
+
*
|
|
568
|
+
* @param message - Human-readable description of the assertion failure.
|
|
569
|
+
* @param options - Structured context for the failed assertion.
|
|
570
|
+
* @param options.value - The value that failed the assertion.
|
|
571
|
+
* @param options.expectedType - The name of the expected branded type.
|
|
572
|
+
* @param options.constraint - The specific constraint that was violated, if any.
|
|
573
|
+
*/
|
|
574
|
+
constructor(message: string, options: {
|
|
575
|
+
value: unknown;
|
|
576
|
+
expectedType: string;
|
|
577
|
+
constraint?: string;
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Root branded string type for the Solana types module.
|
|
582
|
+
*
|
|
583
|
+
* This is the base type from which all specialized Solana string types derive.
|
|
584
|
+
* Branding it prevents raw `string` literals from being used where a validated
|
|
585
|
+
* Solana string is required, while still allowing structural compatibility with
|
|
586
|
+
* `string` via the underlying primitive.
|
|
587
|
+
*
|
|
588
|
+
* @remarks
|
|
589
|
+
* Prefer the more specific sub-types (e.g., {@link TransactionSignature}) over
|
|
590
|
+
* `String` when the string has additional semantic meaning. Use `String` only
|
|
591
|
+
* when building generic utilities that operate on any validated Solana string
|
|
592
|
+
* and need to accept multiple sub-brands.
|
|
593
|
+
*
|
|
594
|
+
* @example
|
|
595
|
+
* ```typescript
|
|
596
|
+
* function logSolanaString(s: String): void {
|
|
597
|
+
* console.log("Solana string:", s);
|
|
598
|
+
* }
|
|
599
|
+
*
|
|
600
|
+
* const raw = "hello";
|
|
601
|
+
* assertString(raw);
|
|
602
|
+
* logSolanaString(raw); // OK — raw is now branded as String
|
|
603
|
+
* ```
|
|
604
|
+
*
|
|
605
|
+
* @see {@link assertString} for the corresponding runtime assertion
|
|
606
|
+
* @public
|
|
607
|
+
*/
|
|
608
|
+
type String = BrandedType<string, "String">;
|
|
609
|
+
/**
|
|
610
|
+
* Base58-encoded Solana transaction signature.
|
|
611
|
+
*
|
|
612
|
+
* A transaction signature is the result of an Ed25519 sign operation on the
|
|
613
|
+
* serialized transaction message. Solana encodes it as a base58 string
|
|
614
|
+
* (approximately 87–88 characters) for human-readable display and API usage.
|
|
615
|
+
*
|
|
616
|
+
* @remarks
|
|
617
|
+
* **Encoding details:**
|
|
618
|
+
* - Underlying binary: 64 bytes (see {@link SignatureBytes})
|
|
619
|
+
* - Base58 alphabet: 58 characters (`123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`)
|
|
620
|
+
* - Excluded characters: `0`, `O`, `I`, `l` (visually ambiguous)
|
|
621
|
+
* - Typical string length: 87–88 characters
|
|
622
|
+
*
|
|
623
|
+
* **Type safety:** The {@link assertTransactionSignature} function validates
|
|
624
|
+
* that a `string` value is non-empty and contains only Base58 characters before
|
|
625
|
+
* branding it as `TransactionSignature`. This catches obvious data errors at
|
|
626
|
+
* the ingestion boundary.
|
|
627
|
+
*
|
|
628
|
+
* **Note:** Validation is character-level only. The assertion does NOT verify
|
|
629
|
+
* cryptographic correctness (i.e., it does not verify that the signature was
|
|
630
|
+
* produced by a specific keypair).
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* Narrowing a raw string to `TransactionSignature`:
|
|
634
|
+
* ```typescript
|
|
635
|
+
* const raw = "5wHu1qwD7q5menT3ydT9VdFPQfkLaWvqPgVbqsM1qwD7";
|
|
636
|
+
* assertTransactionSignature(raw);
|
|
637
|
+
* // raw is now typed as TransactionSignature
|
|
638
|
+
* ```
|
|
639
|
+
*
|
|
640
|
+
* @example
|
|
641
|
+
* Using in a function signature:
|
|
642
|
+
* ```typescript
|
|
643
|
+
* async function getTransactionDetails(sig: TransactionSignature) {
|
|
644
|
+
* return await rpc.getTransaction(sig).send();
|
|
645
|
+
* }
|
|
646
|
+
* ```
|
|
647
|
+
*
|
|
648
|
+
* @see {@link assertTransactionSignature} for the runtime assertion
|
|
649
|
+
* @see {@link SignatureBytes} for the raw binary equivalent
|
|
650
|
+
* @public
|
|
651
|
+
*/
|
|
652
|
+
type TransactionSignature = SubBrandedType<String, "TransactionSignature">;
|
|
653
|
+
/**
|
|
654
|
+
* Root branded `Uint8Array` type for Solana binary data.
|
|
655
|
+
*
|
|
656
|
+
* This is the base type for all Solana-specific byte array types. It derives
|
|
657
|
+
* from the generic `Bytes` type and adds the `"SolanaBytes"` nominal brand,
|
|
658
|
+
* distinguishing Solana binary data from other `Uint8Array` values in the SDK.
|
|
659
|
+
*
|
|
660
|
+
* @remarks
|
|
661
|
+
* No specific length constraint — the length depends on the particular data
|
|
662
|
+
* being represented. Sub-types like {@link SignatureBytes} impose their own
|
|
663
|
+
* length requirements.
|
|
664
|
+
*
|
|
665
|
+
* Use `SolanaBytes` when the binary data is Solana-specific but does not fall
|
|
666
|
+
* into a more precise category (e.g., serialized account data, raw message
|
|
667
|
+
* bytes).
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* ```typescript
|
|
671
|
+
* const accountData = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
|
|
672
|
+
* assertSolanaBytes(accountData);
|
|
673
|
+
* // accountData is now typed as SolanaBytes
|
|
674
|
+
* ```
|
|
675
|
+
*
|
|
676
|
+
* @see {@link assertSolanaBytes} for the runtime assertion
|
|
677
|
+
* @see {@link SignatureBytes} for the 64-byte Ed25519 signature sub-type
|
|
678
|
+
* @public
|
|
679
|
+
*/
|
|
680
|
+
type SolanaBytes = SubBrandedType<Bytes, "SolanaBytes">;
|
|
681
|
+
/**
|
|
682
|
+
* 64-byte raw Ed25519 signature in binary form.
|
|
683
|
+
*
|
|
684
|
+
* This type represents the binary encoding of an Ed25519 signature as used
|
|
685
|
+
* internally by the Solana runtime and the `@solana/kit` library. It is the
|
|
686
|
+
* binary equivalent of a {@link TransactionSignature} string — the same 64
|
|
687
|
+
* bytes, without the Base58 encoding.
|
|
688
|
+
*
|
|
689
|
+
* @remarks
|
|
690
|
+
* **Structure:**
|
|
691
|
+
* - Bytes 0–31: R component — a compressed Ed25519 curve point
|
|
692
|
+
* - Bytes 32–63: S component — a scalar in the range `[0, l)` where `l` is
|
|
693
|
+
* the order of the Ed25519 base point
|
|
694
|
+
*
|
|
695
|
+
* **Conversion:**
|
|
696
|
+
* - `SignatureBytes` → `TransactionSignature`: Base58-encode the bytes
|
|
697
|
+
* - `TransactionSignature` → `SignatureBytes`: Base58-decode the string
|
|
698
|
+
*
|
|
699
|
+
* The {@link assertSignatureBytes} function validates both that the value is a
|
|
700
|
+
* `Uint8Array` and that it is exactly {@link SIGNATURE_BYTE_LENGTH} (64) bytes.
|
|
701
|
+
*
|
|
702
|
+
* @example
|
|
703
|
+
* Creating and validating a signature byte array:
|
|
704
|
+
* ```typescript
|
|
705
|
+
* const sigBytes = new Uint8Array(64);
|
|
706
|
+
* // ... fill sigBytes with actual Ed25519 signature material ...
|
|
707
|
+
* assertSignatureBytes(sigBytes);
|
|
708
|
+
* // sigBytes is now typed as SignatureBytes
|
|
709
|
+
* ```
|
|
710
|
+
*
|
|
711
|
+
* @example
|
|
712
|
+
* Using with a Solana transaction builder:
|
|
713
|
+
* ```typescript
|
|
714
|
+
* declare function attachSignature(
|
|
715
|
+
* tx: UnsignedTransaction,
|
|
716
|
+
* publicKey: Address,
|
|
717
|
+
* signature: SignatureBytes,
|
|
718
|
+
* ): PartiallySignedTransaction;
|
|
719
|
+
* ```
|
|
720
|
+
*
|
|
721
|
+
* @see {@link assertSignatureBytes} for the runtime assertion
|
|
722
|
+
* @see {@link TransactionSignature} for the base58-encoded equivalent
|
|
723
|
+
* @see {@link SIGNATURE_BYTE_LENGTH} for the required byte length constant
|
|
724
|
+
* @public
|
|
725
|
+
*/
|
|
726
|
+
type SignatureBytes = SubSubBrandedType<SolanaBytes, "SignatureBytes">;
|
|
727
|
+
/**
|
|
728
|
+
* An unsigned Solana transaction — a transaction that has been constructed
|
|
729
|
+
* but has not yet received any signatures.
|
|
730
|
+
*
|
|
731
|
+
* @remarks
|
|
732
|
+
* This type wraps `Transaction` from `@solana/kit` with the `"UnsignedTransaction"`
|
|
733
|
+
* brand. The brand prevents an unsigned transaction from being passed to a
|
|
734
|
+
* `TransactionForwarder` or any function that requires a {@link SignedTransaction},
|
|
735
|
+
* catching the mistake at compile time.
|
|
736
|
+
*
|
|
737
|
+
* **Lifecycle:** Build the transaction message → compile to `Transaction` →
|
|
738
|
+
* brand as `UnsignedTransaction` → sign to produce {@link SignedTransaction}.
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* Type-safe transaction building:
|
|
742
|
+
* ```typescript
|
|
743
|
+
* import { UnsignedTransaction } from "./types";
|
|
744
|
+
*
|
|
745
|
+
* function buildTransfer(from: Address, to: Address, amount: bigint): UnsignedTransaction {
|
|
746
|
+
* // ... build and compile message ...
|
|
747
|
+
* return compiledTx as UnsignedTransaction;
|
|
748
|
+
* }
|
|
749
|
+
*
|
|
750
|
+
* const tx = buildTransfer(sender, recipient, 1_000_000n);
|
|
751
|
+
* // tx cannot be passed to forwarder.forwardSequentially — compile error
|
|
752
|
+
* ```
|
|
753
|
+
*
|
|
754
|
+
* @see {@link SignedTransaction} for the signed counterpart
|
|
755
|
+
* @public
|
|
756
|
+
*/
|
|
757
|
+
type UnsignedTransaction = BrandedType<Transaction, "UnsignedTransaction">;
|
|
758
|
+
/**
|
|
759
|
+
* A Solana transaction that has at least one signature and a blockhash
|
|
760
|
+
* lifetime constraint.
|
|
761
|
+
*
|
|
762
|
+
* This is the base type for all signed transaction variants. It intersects
|
|
763
|
+
* `Transaction` with `TransactionWithBlockhashLifetime` from `@solana/kit`,
|
|
764
|
+
* ensuring the blockhash expiry window (`blockhash` + `lastValidBlockHeight`)
|
|
765
|
+
* is present alongside the signature data.
|
|
766
|
+
*
|
|
767
|
+
* @remarks
|
|
768
|
+
* **Blockhash lifetime** — The `TransactionWithBlockhashLifetime` intersection
|
|
769
|
+
* ensures that every `SignedTransaction` carries `lifetimeConstraint.blockhash`
|
|
770
|
+
* and `lifetimeConstraint.lastValidBlockHeight`. The forwarder uses these to
|
|
771
|
+
* detect expiry before submission.
|
|
772
|
+
*
|
|
773
|
+
* **Sub-types** — `SignedTransaction` is the common type used in forwarder
|
|
774
|
+
* method signatures because it accepts both {@link PartiallySignedTransaction}
|
|
775
|
+
* and {@link FullySignedTransaction}. Use the more specific sub-types when the
|
|
776
|
+
* exact signing completeness matters to the API contract.
|
|
777
|
+
*
|
|
778
|
+
* @example
|
|
779
|
+
* Accepting any signed transaction (partial or full):
|
|
780
|
+
* ```typescript
|
|
781
|
+
* function logSignedTx(tx: SignedTransaction): void {
|
|
782
|
+
* console.log(`Valid until block: ${tx.lifetimeConstraint.lastValidBlockHeight}`);
|
|
783
|
+
* }
|
|
784
|
+
* ```
|
|
785
|
+
*
|
|
786
|
+
* @see {@link PartiallySignedTransaction} for partial signing state
|
|
787
|
+
* @see {@link FullySignedTransaction} for full signing state
|
|
788
|
+
* @see {@link UnsignedTransaction} for the unsigned state
|
|
789
|
+
* @public
|
|
790
|
+
*/
|
|
791
|
+
type SignedTransaction = BrandedType<Transaction & TransactionWithBlockhashLifetime, "SignedTransaction">;
|
|
792
|
+
/**
|
|
793
|
+
* A Solana transaction with some but potentially not all required signatures.
|
|
794
|
+
*
|
|
795
|
+
* A sub-brand of {@link SignedTransaction}. The transaction has been signed by
|
|
796
|
+
* at least one party but may still be missing signatures from other required
|
|
797
|
+
* signers. This state is common in multi-signature workflows where different
|
|
798
|
+
* parties sign at different times or different locations.
|
|
799
|
+
*
|
|
800
|
+
* @remarks
|
|
801
|
+
* In the Umbra SDK, partial signing arises when:
|
|
802
|
+
* - The user's wallet signs the transaction locally.
|
|
803
|
+
* - The relayer counter-signs before submission.
|
|
804
|
+
* - A hardware wallet adds its signature after an initial software signature.
|
|
805
|
+
*
|
|
806
|
+
* A `PartiallySignedTransaction` cannot be submitted to the network until all
|
|
807
|
+
* required signatures are collected, at which point it can be cast (or built
|
|
808
|
+
* up) to {@link FullySignedTransaction}.
|
|
809
|
+
*
|
|
810
|
+
* @example
|
|
811
|
+
* Multi-sig workflow:
|
|
812
|
+
* ```typescript
|
|
813
|
+
* const partial: PartiallySignedTransaction = await userWallet.sign(unsigned);
|
|
814
|
+
* const full: FullySignedTransaction = await relayer.countersign(partial);
|
|
815
|
+
* await forwarder.forwardSequentially([full]);
|
|
816
|
+
* ```
|
|
817
|
+
*
|
|
818
|
+
* @see {@link FullySignedTransaction} for the fully-signed state
|
|
819
|
+
* @see {@link SignedTransaction} for the base signed state
|
|
820
|
+
* @public
|
|
821
|
+
*/
|
|
822
|
+
type PartiallySignedTransaction = SubBrandedType<SignedTransaction, "PartiallySignedTransaction">;
|
|
823
|
+
/**
|
|
824
|
+
* A Solana transaction with all required signatures present and ready for
|
|
825
|
+
* network submission.
|
|
826
|
+
*
|
|
827
|
+
* This is the terminal transaction state in the signing lifecycle. A
|
|
828
|
+
* `FullySignedTransaction` has been signed by every required signer and can be
|
|
829
|
+
* submitted to the Solana network via a {@link TransactionForwarder}.
|
|
830
|
+
*
|
|
831
|
+
* @remarks
|
|
832
|
+
* **Compile-time guarantee** — The {@link TransactionForwarder} interface's
|
|
833
|
+
* `forwardSequentially` and `forwardInParallel` methods accept
|
|
834
|
+
* `readonly SignedTransaction[]` (the common base type). Callers that track
|
|
835
|
+
* signing state explicitly can use `FullySignedTransaction` in their own
|
|
836
|
+
* function signatures to signal that all signatures are present.
|
|
837
|
+
*
|
|
838
|
+
* **Branding vs. validation** — The brand is a compile-time assertion — the
|
|
839
|
+
* runtime does NOT verify that all required pubkeys have corresponding
|
|
840
|
+
* signatures. It is the responsibility of the signing workflow to ensure
|
|
841
|
+
* completeness before casting to `FullySignedTransaction`.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* Enforcing full signing at a function boundary:
|
|
845
|
+
* ```typescript
|
|
846
|
+
* import { FullySignedTransaction } from "./types";
|
|
847
|
+
* import type { TransactionForwarder } from "./interfaces";
|
|
848
|
+
*
|
|
849
|
+
* async function submitAll(
|
|
850
|
+
* forwarder: TransactionForwarder,
|
|
851
|
+
* txs: readonly FullySignedTransaction[],
|
|
852
|
+
* ) {
|
|
853
|
+
* return forwarder.forwardSequentially(txs);
|
|
854
|
+
* }
|
|
855
|
+
* ```
|
|
856
|
+
*
|
|
857
|
+
* @see {@link PartiallySignedTransaction} for partial signing state
|
|
858
|
+
* @see {@link SignedTransaction} for the base signed state
|
|
859
|
+
* @see {@link TransactionForwarder} for the submission interface
|
|
860
|
+
* @public
|
|
861
|
+
*/
|
|
862
|
+
type FullySignedTransaction = SubSubBrandedType<PartiallySignedTransaction, "FullySignedTransaction">;
|
|
863
|
+
/**
|
|
864
|
+
* Asserts that a value is a primitive `string` and narrows it to {@link String}.
|
|
865
|
+
*
|
|
866
|
+
* This is the base assertion for all Solana branded string types. It only
|
|
867
|
+
* checks that the input is a primitive `string` — no format or content
|
|
868
|
+
* validation is performed.
|
|
869
|
+
*
|
|
870
|
+
* @param value - The value to assert. Must be a primitive string.
|
|
871
|
+
* @throws {SolanaAssertionError} If `typeof value !== "string"`.
|
|
872
|
+
*
|
|
873
|
+
* @remarks
|
|
874
|
+
* For more specific string types, use the dedicated assertion functions:
|
|
875
|
+
* - {@link assertTransactionSignature} — for base58-encoded signatures
|
|
876
|
+
*
|
|
877
|
+
* @example
|
|
878
|
+
* ```typescript
|
|
879
|
+
* const raw: unknown = "hello world";
|
|
880
|
+
* assertString(raw as string);
|
|
881
|
+
* // raw is now typed as String
|
|
882
|
+
*
|
|
883
|
+
* assertString(123 as unknown as string); // Throws: not a string
|
|
884
|
+
* assertString(null as unknown as string); // Throws: not a string
|
|
885
|
+
* ```
|
|
886
|
+
*
|
|
887
|
+
* @see {@link String} for the branded type
|
|
888
|
+
* @see {@link assertTransactionSignature} for the signature-specific assertion
|
|
889
|
+
* @public
|
|
890
|
+
*/
|
|
891
|
+
declare function assertString(value: string): asserts value is String;
|
|
892
|
+
/**
|
|
893
|
+
* Asserts that a value is a valid base58-encoded Solana transaction signature
|
|
894
|
+
* and narrows it to {@link TransactionSignature}.
|
|
895
|
+
*
|
|
896
|
+
* Validation rules:
|
|
897
|
+
* 1. `typeof value === "string"`
|
|
898
|
+
* 2. `value.length > 0` (non-empty)
|
|
899
|
+
* 3. Every character is in the Base58 alphabet (`123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`)
|
|
900
|
+
*
|
|
901
|
+
* @param value - The string to assert as a base58 transaction signature.
|
|
902
|
+
* @throws {SolanaAssertionError} If the value is not a string, is empty, or
|
|
903
|
+
* contains characters outside the Base58 alphabet. The error's `constraint`
|
|
904
|
+
* field identifies which rule was violated, and for alphabet violations,
|
|
905
|
+
* includes the position and character that failed.
|
|
906
|
+
*
|
|
907
|
+
* @remarks
|
|
908
|
+
* **Validation scope** — This function performs character-level validation only.
|
|
909
|
+
* It does NOT verify cryptographic correctness (i.e., it does not check that
|
|
910
|
+
* the 64 decoded bytes constitute a valid Ed25519 signature, nor that the
|
|
911
|
+
* signature was produced by any particular keypair).
|
|
912
|
+
*
|
|
913
|
+
* **Excluded characters** — The Base58 alphabet deliberately omits `0` (zero),
|
|
914
|
+
* `O` (capital O), `I` (capital I), and `l` (lowercase L) to avoid
|
|
915
|
+
* transcription errors. Any of these characters in `value` will cause a throw.
|
|
916
|
+
*
|
|
917
|
+
* **Typical usage** — Call this function at SDK ingestion boundaries (e.g.,
|
|
918
|
+
* when accepting a signature string from user input or an external API) to
|
|
919
|
+
* ensure downstream code only ever sees branded, validated values.
|
|
920
|
+
*
|
|
921
|
+
* @example
|
|
922
|
+
* Validating a signature from an external source:
|
|
923
|
+
* ```typescript
|
|
924
|
+
* const rawSig: string = apiResponse.signature;
|
|
925
|
+
* assertTransactionSignature(rawSig);
|
|
926
|
+
* // rawSig is now typed as TransactionSignature
|
|
927
|
+
* ```
|
|
928
|
+
*
|
|
929
|
+
* @example
|
|
930
|
+
* Invalid inputs:
|
|
931
|
+
* ```typescript
|
|
932
|
+
* assertTransactionSignature(""); // Throws: empty string
|
|
933
|
+
* assertTransactionSignature("hello!world"); // Throws: invalid char '!'
|
|
934
|
+
* assertTransactionSignature("0abc"); // Throws: '0' not in Base58
|
|
935
|
+
* assertTransactionSignature("IIII"); // Throws: 'I' not in Base58
|
|
936
|
+
* ```
|
|
937
|
+
*
|
|
938
|
+
* @see {@link TransactionSignature} for the branded type
|
|
939
|
+
* @see {@link SignatureBytes} for the binary equivalent
|
|
940
|
+
* @public
|
|
941
|
+
*/
|
|
942
|
+
declare function assertTransactionSignature(value: string): asserts value is TransactionSignature;
|
|
943
|
+
/**
|
|
944
|
+
* Asserts that a value is a `Uint8Array` and narrows it to {@link SolanaBytes}.
|
|
945
|
+
*
|
|
946
|
+
* This is the base assertion for all Solana byte array types. It only checks
|
|
947
|
+
* that the input is a `Uint8Array` instance — no length validation is performed.
|
|
948
|
+
*
|
|
949
|
+
* @param value - The `Uint8Array` to assert. Must be an instance of `Uint8Array`.
|
|
950
|
+
* @throws {SolanaAssertionError} If `!(value instanceof Uint8Array)`.
|
|
951
|
+
*
|
|
952
|
+
* @remarks
|
|
953
|
+
* For byte arrays with specific length requirements, use the more specific
|
|
954
|
+
* assertion function:
|
|
955
|
+
* - {@link assertSignatureBytes} — for 64-byte Ed25519 signature arrays
|
|
956
|
+
*
|
|
957
|
+
* @example
|
|
958
|
+
* ```typescript
|
|
959
|
+
* const data = new Uint8Array([0x01, 0x02, 0x03, 0x04]);
|
|
960
|
+
* assertSolanaBytes(data);
|
|
961
|
+
* // data is now typed as SolanaBytes
|
|
962
|
+
*
|
|
963
|
+
* assertSolanaBytes([] as unknown as Uint8Array); // Throws: not a Uint8Array
|
|
964
|
+
* assertSolanaBytes("bytes" as unknown as Uint8Array); // Throws: not a Uint8Array
|
|
965
|
+
* ```
|
|
966
|
+
*
|
|
967
|
+
* @see {@link SolanaBytes} for the branded type
|
|
968
|
+
* @see {@link assertSignatureBytes} for the length-constrained variant
|
|
969
|
+
* @public
|
|
970
|
+
*/
|
|
971
|
+
declare function assertSolanaBytes(value: Uint8Array): asserts value is SolanaBytes;
|
|
972
|
+
/**
|
|
973
|
+
* Asserts that a value is a `Uint8Array` of exactly {@link SIGNATURE_BYTE_LENGTH}
|
|
974
|
+
* (64) bytes and narrows it to {@link SignatureBytes}.
|
|
975
|
+
*
|
|
976
|
+
* Validation rules:
|
|
977
|
+
* 1. `value instanceof Uint8Array`
|
|
978
|
+
* 2. `value.length === 64`
|
|
979
|
+
*
|
|
980
|
+
* @param value - The `Uint8Array` to assert as a 64-byte Ed25519 signature.
|
|
981
|
+
* @throws {SolanaAssertionError} If the value is not a `Uint8Array` or is not
|
|
982
|
+
* exactly 64 bytes. The error's `constraint` field specifies
|
|
983
|
+
* `"length === 64"` for length violations.
|
|
984
|
+
*
|
|
985
|
+
* @remarks
|
|
986
|
+
* The 64-byte requirement is defined by the Ed25519 specification and is
|
|
987
|
+
* invariant across all Solana Ed25519 signatures. Passing a 63- or 65-byte
|
|
988
|
+
* array will always throw, regardless of the content.
|
|
989
|
+
*
|
|
990
|
+
* **Structure:**
|
|
991
|
+
* - Bytes 0–31: R component (compressed curve point)
|
|
992
|
+
* - Bytes 32–63: S component (scalar)
|
|
993
|
+
*
|
|
994
|
+
* @example
|
|
995
|
+
* Validating a signature byte array:
|
|
996
|
+
* ```typescript
|
|
997
|
+
* const sigBytes = new Uint8Array(64);
|
|
998
|
+
* // ... populate with actual Ed25519 signature bytes ...
|
|
999
|
+
* assertSignatureBytes(sigBytes);
|
|
1000
|
+
* // sigBytes is now typed as SignatureBytes
|
|
1001
|
+
* ```
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* Invalid inputs:
|
|
1005
|
+
* ```typescript
|
|
1006
|
+
* assertSignatureBytes(new Uint8Array(63)); // Throws: expected 64 bytes, got 63
|
|
1007
|
+
* assertSignatureBytes(new Uint8Array(65)); // Throws: expected 64 bytes, got 65
|
|
1008
|
+
* assertSignatureBytes([] as unknown as Uint8Array); // Throws: not a Uint8Array
|
|
1009
|
+
* ```
|
|
1010
|
+
*
|
|
1011
|
+
* @see {@link SignatureBytes} for the branded type
|
|
1012
|
+
* @see {@link SIGNATURE_BYTE_LENGTH} for the required length constant
|
|
1013
|
+
* @see {@link TransactionSignature} for the base58-encoded equivalent
|
|
1014
|
+
* @public
|
|
1015
|
+
*/
|
|
1016
|
+
declare function assertSignatureBytes(value: Uint8Array): asserts value is SignatureBytes;
|
|
1017
|
+
/**
|
|
1018
|
+
* Brands a compiled `Transaction` as a {@link SignedTransaction} for relay
|
|
1019
|
+
* submission.
|
|
1020
|
+
*
|
|
1021
|
+
* @remarks
|
|
1022
|
+
* This function is used for transactions that will be signed by the **relayer**,
|
|
1023
|
+
* not by the client. In the Umbra relay flow, the client builds and serializes
|
|
1024
|
+
* a transaction message, which is then passed to the relayer service. The
|
|
1025
|
+
* relayer signs and submits the transaction. Because the blockhash lifetime is
|
|
1026
|
+
* already embedded in the compiled message bytes (`messageBytes`), the
|
|
1027
|
+
* transaction is treated as carrying a blockhash constraint even though
|
|
1028
|
+
* `lifetimeConstraint` is not set as a TypeScript property.
|
|
1029
|
+
*
|
|
1030
|
+
* **This is an escape hatch** — it performs an unchecked cast. Use only when
|
|
1031
|
+
* you are certain the compiled message bytes include a valid blockhash and the
|
|
1032
|
+
* relayer will supply the required signature before submission.
|
|
1033
|
+
*
|
|
1034
|
+
* @param tx - A `Transaction` object whose compiled message bytes already
|
|
1035
|
+
* embed the blockhash lifetime constraint.
|
|
1036
|
+
* @returns The same `Transaction` value cast to {@link SignedTransaction}.
|
|
1037
|
+
*
|
|
1038
|
+
* @example
|
|
1039
|
+
* ```typescript
|
|
1040
|
+
* import { asRelayableSignedTransaction } from "./types";
|
|
1041
|
+
*
|
|
1042
|
+
* const compiledTx: Transaction = compileTransactionMessage(txMessage);
|
|
1043
|
+
* const relayable = asRelayableSignedTransaction(compiledTx);
|
|
1044
|
+
* // relayable can now be passed to relayer submission APIs
|
|
1045
|
+
* ```
|
|
1046
|
+
*
|
|
1047
|
+
* @public
|
|
1048
|
+
*/
|
|
1049
|
+
declare function asRelayableSignedTransaction(tx: Transaction): SignedTransaction;
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Umbra Protocol Types
|
|
1053
|
+
*
|
|
1054
|
+
* Defines the TypeScript interfaces for the core on-chain state accounts used by the
|
|
1055
|
+
* Umbra privacy protocol, and the discriminated union result types returned by SDK
|
|
1056
|
+
* query functions.
|
|
1057
|
+
*
|
|
1058
|
+
* ## EncryptedUserAccount
|
|
1059
|
+
*
|
|
1060
|
+
* The central on-chain record for each Umbra user. It is stored at a PDA derived from
|
|
1061
|
+
* the user's on-chain wallet address and contains:
|
|
1062
|
+
* - Protocol version and PDA metadata
|
|
1063
|
+
* - Status flags that gate access to privacy features
|
|
1064
|
+
* - The X25519 public key used for shared-mode encryption
|
|
1065
|
+
* - The Poseidon commitment to the user's secret key tree
|
|
1066
|
+
* - A generation counter for deterministic nonce derivation
|
|
1067
|
+
* - A random seed for additional entropy during nonce computation
|
|
1068
|
+
*
|
|
1069
|
+
* ## Result Types
|
|
1070
|
+
*
|
|
1071
|
+
* `QueryUserAccountResult` and `QueryComplianceGrantResult` are discriminated unions
|
|
1072
|
+
* whose `state` field acts as a type discriminant. Pattern-match on `state` to safely
|
|
1073
|
+
* access the data payload:
|
|
1074
|
+
*
|
|
1075
|
+
* ```typescript
|
|
1076
|
+
* const result = await queryUserAccount(address);
|
|
1077
|
+
* if (result.state === "exists") {
|
|
1078
|
+
* // result.data: EncryptedUserAccount
|
|
1079
|
+
* }
|
|
1080
|
+
* ```
|
|
1081
|
+
*
|
|
1082
|
+
* @packageDocumentation
|
|
1083
|
+
* @module umbra/types
|
|
1084
|
+
*/
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* The decoded on-chain state of a user's Arcium encrypted user account.
|
|
1088
|
+
*
|
|
1089
|
+
* @remarks
|
|
1090
|
+
* This account is the root of a user's participation in the Umbra protocol. It is a
|
|
1091
|
+
* Program Derived Account (PDA) owned by the Umbra program and indexed by the user's
|
|
1092
|
+
* wallet public key. The account is written by `init_encrypted_user_account`
|
|
1093
|
+
* and mutated by registration and conversion instructions.
|
|
1094
|
+
*
|
|
1095
|
+
* ### Token Account Mode Logic
|
|
1096
|
+
*
|
|
1097
|
+
* Umbra supports two modes for encrypted token accounts:
|
|
1098
|
+
*
|
|
1099
|
+
* - **MXE-only** — balances are encrypted under the Arcium MXE (Multi-party eXecution
|
|
1100
|
+
* Environment) network key. Only the MXE can decrypt them; the user themselves cannot
|
|
1101
|
+
* decrypt without going through MPC. Available to all users regardless of X25519 key
|
|
1102
|
+
* registration.
|
|
1103
|
+
* - **Shared** — balances are encrypted under the user's X25519 public key. The user
|
|
1104
|
+
* can decrypt locally without MPC involvement. Only available after
|
|
1105
|
+
* `isUserAccountX25519KeyRegistered` is `true`.
|
|
1106
|
+
*
|
|
1107
|
+
* ### Status Flag Progression
|
|
1108
|
+
*
|
|
1109
|
+
* A freshly initialized account will have all flags `false`. The user registers their
|
|
1110
|
+
* keys via separate instructions:
|
|
1111
|
+
*
|
|
1112
|
+
* 1. `isInitialized = true` — set by `init_encrypted_user_account`.
|
|
1113
|
+
* 2. `isUserAccountX25519KeyRegistered = true` — set by `register_x25519_public_key`.
|
|
1114
|
+
* 3. `isUserCommitmentRegistered = true` — set by `register_user_commitment`.
|
|
1115
|
+
* 4. `isActiveForAnonymousUsage = true` — set by `activate_for_anonymous_usage`
|
|
1116
|
+
* (requires step 2 and 3 to be complete).
|
|
1117
|
+
*
|
|
1118
|
+
* ### Nonce Derivation
|
|
1119
|
+
*
|
|
1120
|
+
* New token account nonces are derived deterministically from:
|
|
1121
|
+
* - `generationIndex` (incremented each time a new token account is created or a
|
|
1122
|
+
* conversion is performed)
|
|
1123
|
+
* - `randomGenerationSeed` (fixed at registration; can be rotated via
|
|
1124
|
+
* `update_random_generation_seed`)
|
|
1125
|
+
* - `x25519PublicKey` (user identity anchor)
|
|
1126
|
+
*
|
|
1127
|
+
* This ensures that a given user's token account nonces are unique across all mints and
|
|
1128
|
+
* instruction variants without requiring on-chain storage of each nonce individually.
|
|
1129
|
+
*
|
|
1130
|
+
* @example
|
|
1131
|
+
* ```typescript
|
|
1132
|
+
* import { getQueryUserAccountFunction } from "@umbra-privacy/sdk";
|
|
1133
|
+
* import type { EncryptedUserAccount } from "@umbra-privacy/sdk";
|
|
1134
|
+
*
|
|
1135
|
+
* const query = getQueryUserAccountFunction({ accountInfoProvider });
|
|
1136
|
+
* const result = await query(userWalletAddress);
|
|
1137
|
+
*
|
|
1138
|
+
* if (result.state === "exists") {
|
|
1139
|
+
* const account: EncryptedUserAccount = result.data;
|
|
1140
|
+
*
|
|
1141
|
+
* // Check whether Shared mode token accounts are available:
|
|
1142
|
+
* if (!account.isUserAccountX25519KeyRegistered) {
|
|
1143
|
+
* console.warn("Register X25519 key first to enable Shared mode.");
|
|
1144
|
+
* }
|
|
1145
|
+
*
|
|
1146
|
+
* // Use the generation index for nonce derivation:
|
|
1147
|
+
* console.log("Current generation index:", account.generationIndex);
|
|
1148
|
+
* }
|
|
1149
|
+
* ```
|
|
1150
|
+
*
|
|
1151
|
+
* @see {@link QueryUserAccountResult} for the result wrapper returned by query functions.
|
|
1152
|
+
* @public
|
|
1153
|
+
*/
|
|
1154
|
+
interface EncryptedUserAccount {
|
|
1155
|
+
/**
|
|
1156
|
+
* Protocol version byte stored at the beginning of the account discriminator.
|
|
1157
|
+
*
|
|
1158
|
+
* @remarks
|
|
1159
|
+
* Used for forward-compatibility checks. The SDK verifies that the decoded version
|
|
1160
|
+
* matches the expected protocol version before further processing. A mismatch
|
|
1161
|
+
* indicates that the account was created by an incompatible version of the program.
|
|
1162
|
+
*
|
|
1163
|
+
* @readonly
|
|
1164
|
+
*/
|
|
1165
|
+
readonly versionByte: U8;
|
|
1166
|
+
/**
|
|
1167
|
+
* Canonical bump seed for this account's PDA.
|
|
1168
|
+
*
|
|
1169
|
+
* @remarks
|
|
1170
|
+
* Stored on-chain to avoid recomputing `findProgramAddress` in callbacks. Anchor
|
|
1171
|
+
* macro-generated accounts always store their canonical bump as the first non-discriminator
|
|
1172
|
+
* field. The SDK reads it back during PDA validation to confirm account authenticity.
|
|
1173
|
+
*
|
|
1174
|
+
* @readonly
|
|
1175
|
+
*/
|
|
1176
|
+
readonly canonicalBump: U8;
|
|
1177
|
+
/**
|
|
1178
|
+
* Whether the account has been fully initialized by `init_encrypted_user_account`.
|
|
1179
|
+
*
|
|
1180
|
+
* @remarks
|
|
1181
|
+
* Set to `true` during the initial `init_encrypted_user_account` instruction. If
|
|
1182
|
+
* `false`, all other fields should be considered uninitialized / zero-valued. The
|
|
1183
|
+
* on-chain program checks this flag before allowing any state-mutating instruction to
|
|
1184
|
+
* proceed.
|
|
1185
|
+
*
|
|
1186
|
+
* @readonly
|
|
1187
|
+
*/
|
|
1188
|
+
readonly isInitialised: boolean;
|
|
1189
|
+
/**
|
|
1190
|
+
* Whether anonymous usage (mixer transactions using unspent output notes) has been enabled for this
|
|
1191
|
+
* account.
|
|
1192
|
+
*
|
|
1193
|
+
* @remarks
|
|
1194
|
+
* Controlled by the `activate_for_anonymous_usage` instruction. Both
|
|
1195
|
+
* `isUserAccountX25519KeyRegistered` and `isUserCommitmentRegistered` must be `true` before
|
|
1196
|
+
* anonymous usage can be activated. When `false`, the user cannot submit output note
|
|
1197
|
+
* creation or claim instructions.
|
|
1198
|
+
*
|
|
1199
|
+
* @readonly
|
|
1200
|
+
*/
|
|
1201
|
+
readonly isActiveForAnonymousUsage: boolean;
|
|
1202
|
+
/**
|
|
1203
|
+
* Whether the user has registered a Poseidon commitment to their secret key tree.
|
|
1204
|
+
*
|
|
1205
|
+
* @remarks
|
|
1206
|
+
* Set by the `register_user_commitment` instruction. The commitment is the Poseidon
|
|
1207
|
+
* hash of the root node of the user's binary secret tree (see {@link userCommitment}).
|
|
1208
|
+
* It is used in challenge transcript computation and ZK proof public inputs for note
|
|
1209
|
+
* claim proofs to bind the proof to the user's identity without revealing any private
|
|
1210
|
+
* key material.
|
|
1211
|
+
*
|
|
1212
|
+
* @readonly
|
|
1213
|
+
*/
|
|
1214
|
+
readonly isUserCommitmentRegistered: boolean;
|
|
1215
|
+
/**
|
|
1216
|
+
* Whether the user has registered an X25519 public key for shared-mode encryption.
|
|
1217
|
+
*
|
|
1218
|
+
* @remarks
|
|
1219
|
+
* Set by the `register_x25519_public_key` instruction. Until this flag is `true`:
|
|
1220
|
+
* - The `x25519PublicKey` field is zero-valued (all bytes are 0).
|
|
1221
|
+
* - Token accounts must be created in MXE-only mode; Shared mode is unavailable.
|
|
1222
|
+
* - Anonymous usage cannot be activated.
|
|
1223
|
+
*
|
|
1224
|
+
* Once `true`, the user can create Shared-mode token accounts and the stored key is
|
|
1225
|
+
* used in nonce derivation for all subsequent account creation operations.
|
|
1226
|
+
*
|
|
1227
|
+
* @readonly
|
|
1228
|
+
*/
|
|
1229
|
+
readonly isUserAccountX25519KeyRegistered: boolean;
|
|
1230
|
+
/**
|
|
1231
|
+
* The user's X25519 public key for shared-mode token account encryption and note
|
|
1232
|
+
* operations.
|
|
1233
|
+
*
|
|
1234
|
+
* @remarks
|
|
1235
|
+
* This is the 32-byte Curve25519 public key derived from the user's X25519 private
|
|
1236
|
+
* key during the `register_x25519_public_key` instruction. It serves as the
|
|
1237
|
+
* user's persistent on-chain identity for:
|
|
1238
|
+
*
|
|
1239
|
+
* - Encrypting Shared-mode token account balances: the sender encrypts under this key
|
|
1240
|
+
* so the receiver can decrypt locally without MPC.
|
|
1241
|
+
* - Identity anchoring in challenge transcripts and ZK proof public inputs:
|
|
1242
|
+
* the key is hashed into the challenge transcript and the aggregated Poseidon hash
|
|
1243
|
+
* to bind proofs to this user.
|
|
1244
|
+
* - Nonce derivation for new token account initialization: the key is included in the
|
|
1245
|
+
* nonce hash pre-image alongside `generationIndex` and `randomGenerationSeed`.
|
|
1246
|
+
*
|
|
1247
|
+
* If `isUserAccountX25519KeyRegistered` is `false`, this field contains a zero-valued byte
|
|
1248
|
+
* array and must not be used.
|
|
1249
|
+
*
|
|
1250
|
+
* @readonly
|
|
1251
|
+
*/
|
|
1252
|
+
readonly x25519PublicKey: X25519PublicKey$1;
|
|
1253
|
+
/**
|
|
1254
|
+
* Poseidon hash commitment to the root of the user's secret binary tree.
|
|
1255
|
+
*
|
|
1256
|
+
* @remarks
|
|
1257
|
+
* The user's private key material is arranged as a 4-leaf binary tree whose structure
|
|
1258
|
+
* is committed to on-chain:
|
|
1259
|
+
*
|
|
1260
|
+
* ```
|
|
1261
|
+
* userCommitment (this field)
|
|
1262
|
+
* |
|
|
1263
|
+
* +-------------+-------------+
|
|
1264
|
+
* | |
|
|
1265
|
+
* +------+------+ +------+------+
|
|
1266
|
+
* | | | |
|
|
1267
|
+
* masterViewing masterViewing shieldingPriv shieldingPriv
|
|
1268
|
+
* Key Commitment Key KeyBlindingFactor
|
|
1269
|
+
* ```
|
|
1270
|
+
*
|
|
1271
|
+
* The on-chain program never sees any leaf values — only this root commitment. ZK proofs
|
|
1272
|
+
* (ZK proofs) demonstrate knowledge of the leaves and their correct Poseidon relationship
|
|
1273
|
+
* to this root. The Fiat-Shamir challenge computation also includes this commitment to
|
|
1274
|
+
* prevent replay attacks.
|
|
1275
|
+
*
|
|
1276
|
+
* Set by `register_user_commitment`; the commitment is recomputed from private inputs
|
|
1277
|
+
* in the ZK circuit and must match the on-chain stored value for proof verification
|
|
1278
|
+
* to succeed.
|
|
1279
|
+
*
|
|
1280
|
+
* If `isUserCommitmentRegistered` is `false`, this field is zero-valued.
|
|
1281
|
+
*
|
|
1282
|
+
* @readonly
|
|
1283
|
+
*/
|
|
1284
|
+
readonly userCommitment: PoseidonHash;
|
|
1285
|
+
/**
|
|
1286
|
+
* Monotonically increasing 128-bit counter used for deterministic nonce derivation.
|
|
1287
|
+
*
|
|
1288
|
+
* @remarks
|
|
1289
|
+
* Incremented by the on-chain program at the end of the handler instruction (before
|
|
1290
|
+
* the MPC callback fires) for each of the following operations:
|
|
1291
|
+
*
|
|
1292
|
+
* - Creating a new MXE-mode encrypted token account.
|
|
1293
|
+
* - Creating a new Shared-mode encrypted token account.
|
|
1294
|
+
* - Converting an existing MXE-mode token account to Shared-mode.
|
|
1295
|
+
*
|
|
1296
|
+
* When the SDK computes the nonce for a new token account (`newNonce`), it reads the
|
|
1297
|
+
* current `generationIndex` from the fetched `EncryptedUserAccount` and combines it
|
|
1298
|
+
* with `randomGenerationSeed` and `x25519PublicKey` in a domain-separated Poseidon
|
|
1299
|
+
* hash. The handler then increments this counter on-chain so that the next account
|
|
1300
|
+
* creation produces a distinct nonce.
|
|
1301
|
+
*
|
|
1302
|
+
* For existing token accounts the nonce is stored in the `ArciumEncryptedTokenAccount`
|
|
1303
|
+
* PDA and is validated by the callback without using `generationIndex`.
|
|
1304
|
+
*
|
|
1305
|
+
* @readonly
|
|
1306
|
+
*/
|
|
1307
|
+
readonly generationIndex: U128;
|
|
1308
|
+
/**
|
|
1309
|
+
* 256-bit random seed providing additional entropy during nonce derivation.
|
|
1310
|
+
*
|
|
1311
|
+
* @remarks
|
|
1312
|
+
* Set once during account initialization (via `init_encrypted_user_account` or the
|
|
1313
|
+
* first registration call) using a client-generated random value. Can be rotated by
|
|
1314
|
+
* the user via the `update_random_generation_seed` instruction without invalidating
|
|
1315
|
+
* existing token accounts (which store their own nonces).
|
|
1316
|
+
*
|
|
1317
|
+
* Included in the nonce hash pre-image alongside `generationIndex` and
|
|
1318
|
+
* `x25519PublicKey`. Its purpose is to ensure that even if an adversary learns the
|
|
1319
|
+
* user's `generationIndex`, they cannot predict token account nonces without also
|
|
1320
|
+
* knowing this seed.
|
|
1321
|
+
*
|
|
1322
|
+
* Stored as a 32-byte little-endian byte array (`U256LeBytes`).
|
|
1323
|
+
*
|
|
1324
|
+
* @readonly
|
|
1325
|
+
*/
|
|
1326
|
+
readonly randomGenerationSeed: U256LeBytes;
|
|
1327
|
+
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Result of querying whether an `EncryptedUserAccount` exists on-chain for a given
|
|
1330
|
+
* wallet address.
|
|
1331
|
+
*
|
|
1332
|
+
* @remarks
|
|
1333
|
+
* Returned by `getQueryUserAccountFunction` implementations. The `state` field is the
|
|
1334
|
+
* discriminant for narrowing:
|
|
1335
|
+
*
|
|
1336
|
+
* - `"non_existent"` — no account was found at the expected PDA. The user has not yet
|
|
1337
|
+
* called `init_encrypted_user_account`.
|
|
1338
|
+
* - `"exists"` — the account was found and successfully decoded. The `data` property
|
|
1339
|
+
* contains the parsed `EncryptedUserAccount`.
|
|
1340
|
+
*
|
|
1341
|
+
* Pattern-match on `state` to safely access `data`:
|
|
1342
|
+
*
|
|
1343
|
+
* ```typescript
|
|
1344
|
+
* const result: QueryUserAccountResult = await query(walletAddress);
|
|
1345
|
+
*
|
|
1346
|
+
* switch (result.state) {
|
|
1347
|
+
* case "non_existent":
|
|
1348
|
+
* console.log("User not registered — call initEncryptedUserAccount first.");
|
|
1349
|
+
* break;
|
|
1350
|
+
* case "exists":
|
|
1351
|
+
* const { isUserAccountX25519KeyRegistered, generationIndex } = result.data;
|
|
1352
|
+
* break;
|
|
1353
|
+
* }
|
|
1354
|
+
* ```
|
|
1355
|
+
*
|
|
1356
|
+
* @example
|
|
1357
|
+
* ```typescript
|
|
1358
|
+
* import { getQueryUserAccountFunction } from "@umbra-privacy/sdk";
|
|
1359
|
+
*
|
|
1360
|
+
* const query = getQueryUserAccountFunction({ accountInfoProvider });
|
|
1361
|
+
* const result = await query(userAddress);
|
|
1362
|
+
*
|
|
1363
|
+
* if (result.state === "non_existent") {
|
|
1364
|
+
* // Prompt the user to register.
|
|
1365
|
+
* return;
|
|
1366
|
+
* }
|
|
1367
|
+
*
|
|
1368
|
+
* // Narrowed: result.data is EncryptedUserAccount
|
|
1369
|
+
* console.log("Generation index:", result.data.generationIndex);
|
|
1370
|
+
* ```
|
|
1371
|
+
*
|
|
1372
|
+
* @see {@link EncryptedUserAccount} for the account data shape.
|
|
1373
|
+
* @public
|
|
1374
|
+
*/
|
|
1375
|
+
type QueryUserAccountResult = {
|
|
1376
|
+
readonly state: "non_existent";
|
|
1377
|
+
} | {
|
|
1378
|
+
readonly state: "exists";
|
|
1379
|
+
readonly data: EncryptedUserAccount;
|
|
1380
|
+
};
|
|
1381
|
+
/**
|
|
1382
|
+
* Result of checking whether a compliance grant PDA exists on-chain for a given
|
|
1383
|
+
* (granter, nonce, receiver) triple.
|
|
1384
|
+
*
|
|
1385
|
+
* @remarks
|
|
1386
|
+
* Compliance grants are marker accounts — their existence on-chain is the sole signal
|
|
1387
|
+
* that a grant is active. There is no structured data payload to decode; only the
|
|
1388
|
+
* account's presence matters.
|
|
1389
|
+
*
|
|
1390
|
+
* - `"non_existent"` — no grant PDA was found. The granter has not granted the
|
|
1391
|
+
* receiver access to view their encrypted balances for the given nonce.
|
|
1392
|
+
* - `"exists"` — the grant PDA exists. The receiver is permitted to request
|
|
1393
|
+
* re-encryption of the granter's encrypted outputs (subject to on-chain program checks).
|
|
1394
|
+
*
|
|
1395
|
+
* Returned by `getQueryComplianceGrantFunction` implementations.
|
|
1396
|
+
*
|
|
1397
|
+
* @example
|
|
1398
|
+
* ```typescript
|
|
1399
|
+
* import { getQueryComplianceGrantFunction } from "@umbra-privacy/sdk";
|
|
1400
|
+
*
|
|
1401
|
+
* const query = getQueryComplianceGrantFunction({ accountInfoProvider });
|
|
1402
|
+
* const result = await query(granterAddress, nonce, receiverAddress);
|
|
1403
|
+
*
|
|
1404
|
+
* if (result.state === "non_existent") {
|
|
1405
|
+
* console.log("No compliance grant — receiver cannot view balances.");
|
|
1406
|
+
* } else {
|
|
1407
|
+
* console.log("Compliance grant active — receiver has been granted access.");
|
|
1408
|
+
* }
|
|
1409
|
+
* ```
|
|
1410
|
+
*
|
|
1411
|
+
* @see {@link QueryUserAccountResult} for the analogous user account query result type.
|
|
1412
|
+
* @public
|
|
1413
|
+
*/
|
|
1414
|
+
type QueryComplianceGrantResult = {
|
|
1415
|
+
readonly state: "non_existent";
|
|
1416
|
+
} | {
|
|
1417
|
+
readonly state: "exists";
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* Key Derivation Types
|
|
1422
|
+
*
|
|
1423
|
+
* This module defines branded types for Curve25519 (Ed25519/X25519) key exchange,
|
|
1424
|
+
* Keccak hashing, master seeds, and hierarchical viewing key derivation.
|
|
1425
|
+
*
|
|
1426
|
+
* @remarks
|
|
1427
|
+
* All types in this module are "branded" nominal types built on top of primitive
|
|
1428
|
+
* TypeScript types (`Uint8Array` or `bigint`). Branding prevents accidental
|
|
1429
|
+
* cross-use of structurally identical values that have different semantic meanings
|
|
1430
|
+
* (e.g., passing an X25519 public key where a private key is expected).
|
|
1431
|
+
*
|
|
1432
|
+
* ## Type Hierarchy Overview
|
|
1433
|
+
*
|
|
1434
|
+
* ```
|
|
1435
|
+
* Bytes
|
|
1436
|
+
* └── X25519Bytes (32 bytes, endianness-agnostic)
|
|
1437
|
+
* ├── X25519PrivateKey — secret scalar; never share
|
|
1438
|
+
* ├── X25519PublicKey — public curve point; safe to share
|
|
1439
|
+
* └── SharedSecret — ECDH output; feed into a KDF before use
|
|
1440
|
+
*
|
|
1441
|
+
* LeBytes
|
|
1442
|
+
* └── U256LeBytes
|
|
1443
|
+
* └── Keccak256Hash — 32-byte Keccak-256 digest
|
|
1444
|
+
* └── U512LeBytes
|
|
1445
|
+
* └── Keccak512Hash — 64-byte Keccak-512 digest
|
|
1446
|
+
* ├── MasterSeed — root of the key hierarchy; 64 bytes
|
|
1447
|
+
* └── GenerationSeed — ephemeral seed input; 64 bytes
|
|
1448
|
+
*
|
|
1449
|
+
* bigint
|
|
1450
|
+
* └── U256
|
|
1451
|
+
* └── Bn254FieldElement
|
|
1452
|
+
* ├── MasterViewingKey — < 2^252, views all txs
|
|
1453
|
+
* ├── YearlyViewingKey — views one calendar year
|
|
1454
|
+
* ├── MonthlyViewingKey — views one calendar month
|
|
1455
|
+
* ├── DailyViewingKey — views one calendar day
|
|
1456
|
+
* ├── HourlyViewingKey — views one calendar hour
|
|
1457
|
+
* ├── MinuteViewingKey — views one calendar minute
|
|
1458
|
+
* ├── SecondViewingKey — views one calendar second
|
|
1459
|
+
* └── MintViewingKey — views one token (mint)
|
|
1460
|
+
* ```
|
|
1461
|
+
*
|
|
1462
|
+
* ## Security Model
|
|
1463
|
+
*
|
|
1464
|
+
* - The `MasterSeed` is the single secret from which all other keys are derived.
|
|
1465
|
+
* Compromising it compromises the entire key hierarchy.
|
|
1466
|
+
* - KMAC256 domain separation ensures that different derived keys are
|
|
1467
|
+
* computationally independent: knowledge of one does not reveal another.
|
|
1468
|
+
* - Viewing keys (MVK and sub-keys) grant read-only access to transaction
|
|
1469
|
+
* history and can be selectively shared for compliance purposes.
|
|
1470
|
+
* - X25519 private keys are used for ECDH with token senders to enable
|
|
1471
|
+
* encrypted token account balance decryption.
|
|
1472
|
+
*
|
|
1473
|
+
* @packageDocumentation
|
|
1474
|
+
* @public
|
|
1475
|
+
*
|
|
1476
|
+
* @module crypto/key-derivation/types
|
|
1477
|
+
*/
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Base branded byte-array type for X25519 key exchange operations.
|
|
1481
|
+
*
|
|
1482
|
+
* X25519 is an elliptic curve Diffie-Hellman (ECDH) protocol using Curve25519
|
|
1483
|
+
* in Montgomery form. All X25519 values (public keys, private keys, shared
|
|
1484
|
+
* secrets) are exactly 32 bytes.
|
|
1485
|
+
*
|
|
1486
|
+
* @remarks
|
|
1487
|
+
* - Size: 32 bytes (256 bits)
|
|
1488
|
+
* - Used as the parent brand for X25519PrivateKey, X25519PublicKey, and SharedSecret
|
|
1489
|
+
* - Parallel to LeBytes/BeBytes as a sub-brand of Bytes
|
|
1490
|
+
* - Endianness-agnostic: the X25519 spec defines a fixed byte ordering
|
|
1491
|
+
*
|
|
1492
|
+
* ## Type Hierarchy
|
|
1493
|
+
* ```
|
|
1494
|
+
* Bytes (base)
|
|
1495
|
+
* └── X25519Bytes (sub-brand, 32 bytes)
|
|
1496
|
+
* ├── X25519PrivateKey
|
|
1497
|
+
* ├── X25519PublicKey
|
|
1498
|
+
* └── SharedSecret
|
|
1499
|
+
* ```
|
|
1500
|
+
*
|
|
1501
|
+
* @see {@link X25519PrivateKey}
|
|
1502
|
+
* @see {@link X25519PublicKey}
|
|
1503
|
+
* @see {@link SharedSecret}
|
|
1504
|
+
* @see https://cr.yp.to/ecdh/curve25519-20060209.pdf
|
|
1505
|
+
* @see https://tools.ietf.org/html/rfc7748
|
|
1506
|
+
* @public
|
|
1507
|
+
*/
|
|
1508
|
+
type X25519Bytes = SubBrandedType<Bytes, "X25519Bytes">;
|
|
1509
|
+
/**
|
|
1510
|
+
* X25519 private key for elliptic curve Diffie-Hellman key exchange.
|
|
1511
|
+
*
|
|
1512
|
+
* A private key is a 32-byte scalar derived from the master seed via KMAC256
|
|
1513
|
+
* with domain separator `"UserAccountX25519Keypair"`. It is used together
|
|
1514
|
+
* with a counterparty's public key to compute a shared secret.
|
|
1515
|
+
*
|
|
1516
|
+
* @remarks
|
|
1517
|
+
* - Size: 32 bytes (256 bits)
|
|
1518
|
+
* - MUST be kept secret and never shared or logged
|
|
1519
|
+
* - The X25519 algorithm applies RFC 8032 clamping to the scalar during use
|
|
1520
|
+
* - In Umbra, private keys are derived deterministically from the master seed;
|
|
1521
|
+
* they must never be generated independently with a random number generator
|
|
1522
|
+
*
|
|
1523
|
+
* ## Security Warning
|
|
1524
|
+
*
|
|
1525
|
+
* Exposure of this key allows the holder to decrypt all balances encrypted to
|
|
1526
|
+
* the corresponding public key. Store it only in memory during use.
|
|
1527
|
+
*
|
|
1528
|
+
* ## Type Hierarchy
|
|
1529
|
+
* ```
|
|
1530
|
+
* Bytes (base)
|
|
1531
|
+
* └── X25519Bytes (sub-brand, 32 bytes)
|
|
1532
|
+
* └── X25519PrivateKey (sub-sub-brand)
|
|
1533
|
+
* ```
|
|
1534
|
+
*
|
|
1535
|
+
* @example
|
|
1536
|
+
* ```typescript
|
|
1537
|
+
* // Generate a new private key
|
|
1538
|
+
* const rawPrivateKey = crypto.getRandomValues(new Uint8Array(32));
|
|
1539
|
+
* assertX25519PrivateKey(rawPrivateKey);
|
|
1540
|
+
* // rawPrivateKey is now typed as X25519PrivateKey
|
|
1541
|
+
*
|
|
1542
|
+
* // Derive the public key
|
|
1543
|
+
* const publicKey = x25519GetPublicKey(rawPrivateKey);
|
|
1544
|
+
* ```
|
|
1545
|
+
*
|
|
1546
|
+
* @see {@link assertX25519PrivateKey}
|
|
1547
|
+
* @see {@link X25519Keypair}
|
|
1548
|
+
* @see https://tools.ietf.org/html/rfc7748#section-5
|
|
1549
|
+
* @public
|
|
1550
|
+
*/
|
|
1551
|
+
type X25519PrivateKey = SubSubBrandedType<X25519Bytes, "X25519PrivateKey">;
|
|
1552
|
+
/**
|
|
1553
|
+
* X25519 public key for elliptic curve Diffie-Hellman key exchange.
|
|
1554
|
+
*
|
|
1555
|
+
* A public key is a 32-byte Montgomery curve point derived from a private key
|
|
1556
|
+
* via scalar multiplication of the Curve25519 base point. It is stored on-chain
|
|
1557
|
+
* in the encrypted token account and used by token senders to encrypt balances
|
|
1558
|
+
* for the account holder.
|
|
1559
|
+
*
|
|
1560
|
+
* @remarks
|
|
1561
|
+
* - Size: 32 bytes (256 bits)
|
|
1562
|
+
* - Safe to transmit over insecure channels and store on-chain
|
|
1563
|
+
* - Derived from the corresponding X25519PrivateKey
|
|
1564
|
+
* - Used as input to X25519 ECDH: both parties independently compute the same shared secret
|
|
1565
|
+
*
|
|
1566
|
+
* ## Type Hierarchy
|
|
1567
|
+
* ```
|
|
1568
|
+
* Bytes (base)
|
|
1569
|
+
* └── X25519Bytes (sub-brand, 32 bytes)
|
|
1570
|
+
* └── X25519PublicKey (sub-sub-brand)
|
|
1571
|
+
* ```
|
|
1572
|
+
*
|
|
1573
|
+
* @example
|
|
1574
|
+
* ```typescript
|
|
1575
|
+
* // Receive a public key from another party
|
|
1576
|
+
* const theirPublicKey = receivePublicKey();
|
|
1577
|
+
* assertX25519PublicKey(theirPublicKey);
|
|
1578
|
+
* // theirPublicKey is now typed as X25519PublicKey
|
|
1579
|
+
*
|
|
1580
|
+
* // Compute shared secret
|
|
1581
|
+
* const sharedSecret = x25519(myPrivateKey, theirPublicKey);
|
|
1582
|
+
* ```
|
|
1583
|
+
*
|
|
1584
|
+
* @see {@link assertX25519PublicKey}
|
|
1585
|
+
* @see {@link X25519Keypair}
|
|
1586
|
+
* @see https://tools.ietf.org/html/rfc7748#section-5
|
|
1587
|
+
* @public
|
|
1588
|
+
*/
|
|
1589
|
+
type X25519PublicKey = SubSubBrandedType<X25519Bytes, "X25519PublicKey">;
|
|
1590
|
+
/**
|
|
1591
|
+
* An X25519 key pair consisting of a private key and its corresponding public key.
|
|
1592
|
+
*
|
|
1593
|
+
* X25519 is an elliptic curve Diffie-Hellman (ECDH) protocol using Curve25519
|
|
1594
|
+
* in Montgomery form. In Umbra, this keypair is derived deterministically from
|
|
1595
|
+
* the master seed and is used to establish shared secrets with token senders,
|
|
1596
|
+
* enabling the account holder to decrypt their encrypted balance.
|
|
1597
|
+
*
|
|
1598
|
+
* @remarks
|
|
1599
|
+
* ## Key Generation
|
|
1600
|
+
* - Private key: 32 bytes derived via KMAC256 from the master seed
|
|
1601
|
+
* - Public key: Scalar multiplication of the private key with the Curve25519 base point
|
|
1602
|
+
*
|
|
1603
|
+
* ## Security Properties
|
|
1604
|
+
* - Private key MUST be kept secret; it enables decryption of all balances sent to this account
|
|
1605
|
+
* - Public key can be freely shared and is stored on-chain in the token account PDA
|
|
1606
|
+
* - Shared secret is computed as: `X25519(myPrivate, senderPublic)` — symmetric by construction
|
|
1607
|
+
* - Compromise of the private key does not reveal the master seed or other derived keys
|
|
1608
|
+
*
|
|
1609
|
+
* ## Use Cases in Umbra
|
|
1610
|
+
* - `registerTokenPublicKey`: The public key is registered on-chain for a token account
|
|
1611
|
+
* - Balance decryption: The private key is used to decrypt AES-GCM ciphertexts from senders
|
|
1612
|
+
* - ECDH with ephemeral sender keys for forward secrecy of individual transfers
|
|
1613
|
+
*
|
|
1614
|
+
* @example
|
|
1615
|
+
* ```typescript
|
|
1616
|
+
* import { X25519Keypair, generateX25519Keypair } from "./cryptography";
|
|
1617
|
+
*
|
|
1618
|
+
* // Generate a new keypair
|
|
1619
|
+
* const keypair: X25519Keypair = await generateX25519Keypair();
|
|
1620
|
+
*
|
|
1621
|
+
* // Share public key with peer
|
|
1622
|
+
* sendPublicKey(peer, keypair.publicKey);
|
|
1623
|
+
*
|
|
1624
|
+
* // Compute shared secret with peer's public key
|
|
1625
|
+
* const sharedSecret = x25519(keypair.privateKey, peerPublicKey);
|
|
1626
|
+
* ```
|
|
1627
|
+
*
|
|
1628
|
+
* @example
|
|
1629
|
+
* ```typescript
|
|
1630
|
+
* // Ephemeral key exchange for forward secrecy
|
|
1631
|
+
* const ephemeralKeypair: X25519Keypair = await generateX25519Keypair();
|
|
1632
|
+
*
|
|
1633
|
+
* // Use ephemeral keypair for this session only
|
|
1634
|
+
* const sessionKey = deriveSessionKey(
|
|
1635
|
+
* x25519(ephemeralKeypair.privateKey, recipientPublicKey)
|
|
1636
|
+
* );
|
|
1637
|
+
*
|
|
1638
|
+
* // Discard private key after use for forward secrecy
|
|
1639
|
+
* ```
|
|
1640
|
+
*
|
|
1641
|
+
* @see {@link assertX25519Keypair}
|
|
1642
|
+
* @see {@link X25519PrivateKey}
|
|
1643
|
+
* @see {@link X25519PublicKey}
|
|
1644
|
+
* @public
|
|
1645
|
+
*/
|
|
1646
|
+
interface X25519Keypair {
|
|
1647
|
+
/**
|
|
1648
|
+
* The X25519 private key (32 bytes).
|
|
1649
|
+
*
|
|
1650
|
+
* This key MUST be kept secret. It is used to:
|
|
1651
|
+
* - Compute shared secrets with other parties' public keys
|
|
1652
|
+
* - Derive the corresponding public key
|
|
1653
|
+
*
|
|
1654
|
+
* @remarks
|
|
1655
|
+
* The private key is derived from the master seed via KMAC256 with the
|
|
1656
|
+
* domain separator `"UserAccountX25519Keypair"`. The X25519 algorithm applies
|
|
1657
|
+
* RFC 8032 §5.1.5 clamping to the scalar during use.
|
|
1658
|
+
*
|
|
1659
|
+
* @readonly
|
|
1660
|
+
*/
|
|
1661
|
+
readonly privateKey: X25519PrivateKey;
|
|
1662
|
+
/**
|
|
1663
|
+
* The X25519 public key (32 bytes).
|
|
1664
|
+
*
|
|
1665
|
+
* This key can be freely shared. It is:
|
|
1666
|
+
* - Computed from the private key via scalar multiplication with the base point
|
|
1667
|
+
* - Used by other parties to compute shared secrets with their own private key
|
|
1668
|
+
* - Stored on-chain in the Umbra token account PDA
|
|
1669
|
+
* - Safe to transmit over insecure channels
|
|
1670
|
+
*
|
|
1671
|
+
* @readonly
|
|
1672
|
+
*/
|
|
1673
|
+
readonly publicKey: X25519PublicKey;
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Ed25519 keypair for digital signatures on the Edwards form of Curve25519.
|
|
1677
|
+
*
|
|
1678
|
+
* Ed25519 is the signing scheme defined in RFC 8032. In Umbra, an Ed25519
|
|
1679
|
+
* keypair is derived from the same 32-byte seed as the corresponding X25519
|
|
1680
|
+
* keypair (they are related via the birational equivalence of Curve25519 in
|
|
1681
|
+
* Edwards vs Montgomery form). The Ed25519 public key is used as the on-chain
|
|
1682
|
+
* signer identity in registration transactions.
|
|
1683
|
+
*
|
|
1684
|
+
* @remarks
|
|
1685
|
+
* ## Key Derivation
|
|
1686
|
+
* - Seed: First 32 bytes of a 64-byte KMAC256 output keyed by the master seed
|
|
1687
|
+
* - Public key: Derived via `ed25519.getPublicKey(seed)`; uses SHA-512 internally
|
|
1688
|
+
* - The relationship to X25519: the Ed25519 public key can be converted to the
|
|
1689
|
+
* Montgomery form via `ed25519.utils.toMontgomery(edPublicKey)`
|
|
1690
|
+
*
|
|
1691
|
+
* ## Relationship to X25519
|
|
1692
|
+
* - Ed25519 uses compressed Edwards curve coordinates
|
|
1693
|
+
* - X25519 uses Montgomery curve coordinates
|
|
1694
|
+
* - Both represent points on the same underlying elliptic curve (Curve25519)
|
|
1695
|
+
* - The birational map between them is cheap and deterministic
|
|
1696
|
+
*
|
|
1697
|
+
* @example
|
|
1698
|
+
* ```typescript
|
|
1699
|
+
* import { ed25519 } from "@noble/curves/ed25519";
|
|
1700
|
+
*
|
|
1701
|
+
* const seed = crypto.getRandomValues(new Uint8Array(32));
|
|
1702
|
+
* const publicKey = ed25519.getPublicKey(seed);
|
|
1703
|
+
*
|
|
1704
|
+
* const keypair: Ed25519Keypair = { seed, publicKey };
|
|
1705
|
+
* ```
|
|
1706
|
+
*
|
|
1707
|
+
* @see {@link assertEd25519Keypair}
|
|
1708
|
+
* @see {@link Curve25519KeypairResult}
|
|
1709
|
+
* @public
|
|
1710
|
+
*/
|
|
1711
|
+
interface Ed25519Keypair {
|
|
1712
|
+
/**
|
|
1713
|
+
* The Ed25519 private key seed (32 bytes).
|
|
1714
|
+
*
|
|
1715
|
+
* This is the raw 32-byte seed from which both the signing scalar and the
|
|
1716
|
+
* public key are derived (via SHA-512 key expansion defined in RFC 8032).
|
|
1717
|
+
* MUST be kept secret.
|
|
1718
|
+
*/
|
|
1719
|
+
readonly seed: Uint8Array;
|
|
1720
|
+
/**
|
|
1721
|
+
* The Ed25519 public key (32 bytes).
|
|
1722
|
+
*
|
|
1723
|
+
* A compressed Edwards curve point derived from the seed via SHA-512 key
|
|
1724
|
+
* expansion and scalar multiplication. Can be freely shared and is used
|
|
1725
|
+
* as the on-chain signer identity.
|
|
1726
|
+
*/
|
|
1727
|
+
readonly publicKey: Uint8Array;
|
|
1728
|
+
}
|
|
1729
|
+
/**
|
|
1730
|
+
* Combined Curve25519 keypair result containing both Ed25519 and X25519 keypairs.
|
|
1731
|
+
*
|
|
1732
|
+
* This interface represents the result of deriving both Ed25519 (Edwards curve)
|
|
1733
|
+
* and X25519 (Montgomery curve) keypairs from a single 32-byte seed. Both
|
|
1734
|
+
* keypairs are cryptographically linked through the same underlying Curve25519
|
|
1735
|
+
* and are derived together for efficiency.
|
|
1736
|
+
*
|
|
1737
|
+
* @remarks
|
|
1738
|
+
* ## Derivation Pipeline
|
|
1739
|
+
*
|
|
1740
|
+
* 1. **Ed25519 Keypair**:
|
|
1741
|
+
* - Input: First 32 bytes of a 64-byte KMAC256 output
|
|
1742
|
+
* - Output: Ed25519 public key via `ed25519.getPublicKey(seed)`
|
|
1743
|
+
*
|
|
1744
|
+
* 2. **X25519 Keypair**:
|
|
1745
|
+
* - Hash seed with SHA-512 → 64 bytes
|
|
1746
|
+
* - Clamp first 32 bytes per RFC 8032 §5.1.5
|
|
1747
|
+
* - Use clamped bytes as X25519 private key
|
|
1748
|
+
* - Derive X25519 public key via birational map: `ed25519.utils.toMontgomery(ed25519Pub)`
|
|
1749
|
+
*
|
|
1750
|
+
* ## Security Properties
|
|
1751
|
+
*
|
|
1752
|
+
* - **Deterministic**: Same master seed always produces the same keypairs
|
|
1753
|
+
* - **Domain Separated**: Independent from all other key derivations via KMAC256
|
|
1754
|
+
* - **Cryptographically Linked**: Both keypairs share the same Curve25519 foundation
|
|
1755
|
+
*
|
|
1756
|
+
* @example
|
|
1757
|
+
* ```typescript
|
|
1758
|
+
* import { getUserAccountX25519KeypairGenerator } from "@umbra-privacy/sdk";
|
|
1759
|
+
*
|
|
1760
|
+
* const generator = getUserAccountX25519KeypairGenerator({ client });
|
|
1761
|
+
* const result: Curve25519KeypairResult = await generator();
|
|
1762
|
+
*
|
|
1763
|
+
* // Use Ed25519 for signing
|
|
1764
|
+
* const signature = ed25519.sign(message, result.ed25519Keypair.seed);
|
|
1765
|
+
*
|
|
1766
|
+
* // Use X25519 for key exchange
|
|
1767
|
+
* const sharedSecret = x25519.getSharedSecret(
|
|
1768
|
+
* result.x25519Keypair.privateKey,
|
|
1769
|
+
* peerPublicKey
|
|
1770
|
+
* );
|
|
1771
|
+
* ```
|
|
1772
|
+
*
|
|
1773
|
+
* @see {@link Ed25519Keypair}
|
|
1774
|
+
* @see {@link X25519Keypair}
|
|
1775
|
+
* @see {@link assertCurve25519KeypairResult}
|
|
1776
|
+
* @public
|
|
1777
|
+
*/
|
|
1778
|
+
interface Curve25519KeypairResult {
|
|
1779
|
+
/**
|
|
1780
|
+
* Ed25519 keypair for digital signatures.
|
|
1781
|
+
*
|
|
1782
|
+
* Contains the 32-byte seed and the derived 32-byte compressed Edwards
|
|
1783
|
+
* public key. Used as the on-chain signer for registration transactions.
|
|
1784
|
+
*/
|
|
1785
|
+
readonly ed25519Keypair: Ed25519Keypair;
|
|
1786
|
+
/**
|
|
1787
|
+
* X25519 keypair for Diffie-Hellman key exchange.
|
|
1788
|
+
*
|
|
1789
|
+
* Contains the RFC 8032-clamped 32-byte private key (derived from SHA-512
|
|
1790
|
+
* of the Ed25519 seed) and the derived 32-byte Montgomery public key (via
|
|
1791
|
+
* birational conversion from the Ed25519 public key).
|
|
1792
|
+
*/
|
|
1793
|
+
readonly x25519Keypair: X25519Keypair;
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Asserts that a value is a valid Ed25519Keypair.
|
|
1797
|
+
*
|
|
1798
|
+
* Validates that the value is a non-null object with `seed` (exactly 32 bytes)
|
|
1799
|
+
* and `publicKey` (exactly 32 bytes) properties.
|
|
1800
|
+
*
|
|
1801
|
+
* @param value - The object to assert as an Ed25519Keypair (must have seed and publicKey properties)
|
|
1802
|
+
* @throws {CryptographyAssertionError} If the value is not a non-null object
|
|
1803
|
+
* @throws {CryptographyAssertionError} If `seed` or `publicKey` are missing
|
|
1804
|
+
* @throws {CryptographyAssertionError} If `seed` or `publicKey` are not Uint8Array(32)
|
|
1805
|
+
*
|
|
1806
|
+
* @example
|
|
1807
|
+
* ```typescript
|
|
1808
|
+
* const keypair = { seed, publicKey };
|
|
1809
|
+
* assertEd25519Keypair(keypair);
|
|
1810
|
+
* // keypair is now typed as Ed25519Keypair
|
|
1811
|
+
* ```
|
|
1812
|
+
*
|
|
1813
|
+
* @see {@link Ed25519Keypair}
|
|
1814
|
+
* @public
|
|
1815
|
+
*/
|
|
1816
|
+
declare function assertEd25519Keypair(value: {
|
|
1817
|
+
seed: Uint8Array;
|
|
1818
|
+
publicKey: Uint8Array;
|
|
1819
|
+
}): asserts value is Ed25519Keypair;
|
|
1820
|
+
/**
|
|
1821
|
+
* Asserts that a value is a valid Curve25519KeypairResult.
|
|
1822
|
+
*
|
|
1823
|
+
* Validates that the value is a non-null object with valid `ed25519Keypair`
|
|
1824
|
+
* and `x25519Keypair` fields, each of which must pass their respective assertions.
|
|
1825
|
+
*
|
|
1826
|
+
* @param value - The object to assert as a Curve25519KeypairResult
|
|
1827
|
+
* @throws {CryptographyAssertionError} If the value is not a non-null object
|
|
1828
|
+
* @throws {CryptographyAssertionError} If either nested keypair is missing or invalid
|
|
1829
|
+
*
|
|
1830
|
+
* @example
|
|
1831
|
+
* ```typescript
|
|
1832
|
+
* const result = { ed25519Keypair, x25519Keypair };
|
|
1833
|
+
* assertCurve25519KeypairResult(result);
|
|
1834
|
+
* // result is now typed as Curve25519KeypairResult
|
|
1835
|
+
* ```
|
|
1836
|
+
*
|
|
1837
|
+
* @see {@link Curve25519KeypairResult}
|
|
1838
|
+
* @see {@link assertEd25519Keypair}
|
|
1839
|
+
* @see {@link assertX25519Keypair}
|
|
1840
|
+
* @public
|
|
1841
|
+
*/
|
|
1842
|
+
declare function assertCurve25519KeypairResult(value: {
|
|
1843
|
+
ed25519Keypair: {
|
|
1844
|
+
seed: Uint8Array;
|
|
1845
|
+
publicKey: Uint8Array;
|
|
1846
|
+
};
|
|
1847
|
+
x25519Keypair: {
|
|
1848
|
+
privateKey: Uint8Array;
|
|
1849
|
+
publicKey: Uint8Array;
|
|
1850
|
+
};
|
|
1851
|
+
}): asserts value is Curve25519KeypairResult;
|
|
1852
|
+
|
|
1853
|
+
export { BrandedType, Bytes, type Curve25519KeypairResult, type Ed25519Keypair, type EncryptedUserAccount, type FullySignedTransaction, type GeneratorFunction, type KeyStorageContext, type LoadResult, type LoaderFunction, MasterSeed, type MasterSeedGeneratorFunction, type MasterSeedLoaderFunction, type MasterSeedStorerFunction, type ParameterizedGeneratorFunction, type ParameterizedLoaderFunction, type ParameterizedStorerFunction, type PartiallySignedTransaction, PoseidonHash, type QueryComplianceGrantResult, type QueryUserAccountResult, SIGNATURE_BYTE_LENGTH, type SignatureBytes, type SignedTransaction, SolanaAssertionError, type SolanaBytes, type StoreResult, type StorerFunction, type String, SubBrandedType, SubSubBrandedType, type TransactionSignature, U128, U256LeBytes, U512, U8, type UnsignedTransaction, X25519PublicKey$1 as X25519PublicKey, asRelayableSignedTransaction, assertCurve25519KeypairResult, assertEd25519Keypair, assertSignatureBytes, assertSolanaBytes, assertString, assertTransactionSignature };
|