@arcium-hq/client 0.9.2 → 0.9.3

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.
Files changed (92) hide show
  1. package/build/index.cjs +15 -3
  2. package/build/index.mjs +15 -4
  3. package/build/types/arcis/arcisModule.d.ts +26 -0
  4. package/build/types/arcis/arcisModule.d.ts.map +1 -0
  5. package/build/types/arcis/arcisType.d.ts +76 -0
  6. package/build/types/arcis/arcisType.d.ts.map +1 -0
  7. package/build/types/arcis/packer.d.ts +63 -0
  8. package/build/types/arcis/packer.d.ts.map +1 -0
  9. package/build/types/arcis/packing.d.ts +33 -0
  10. package/build/types/arcis/packing.d.ts.map +1 -0
  11. package/build/types/callback.d.ts +21 -0
  12. package/build/types/callback.d.ts.map +1 -0
  13. package/build/types/constants.d.ts +101 -0
  14. package/build/types/constants.d.ts.map +1 -0
  15. package/build/types/cryptography/aes128Cipher.d.ts +14 -0
  16. package/build/types/cryptography/aes128Cipher.d.ts.map +1 -0
  17. package/build/types/cryptography/aes192Cipher.d.ts +14 -0
  18. package/build/types/cryptography/aes192Cipher.d.ts.map +1 -0
  19. package/build/types/cryptography/aes256Cipher.d.ts +14 -0
  20. package/build/types/cryptography/aes256Cipher.d.ts.map +1 -0
  21. package/build/types/cryptography/aesCtrCipher.d.ts +36 -0
  22. package/build/types/cryptography/aesCtrCipher.d.ts.map +1 -0
  23. package/build/types/cryptography/arcisEd25519.d.ts +8 -0
  24. package/build/types/cryptography/arcisEd25519.d.ts.map +1 -0
  25. package/build/types/cryptography/cSplRescueCipher.d.ts +29 -0
  26. package/build/types/cryptography/cSplRescueCipher.d.ts.map +1 -0
  27. package/build/types/cryptography/cryptography.d.ts +38 -0
  28. package/build/types/cryptography/cryptography.d.ts.map +1 -0
  29. package/build/types/cryptography/hkdf.d.ts +37 -0
  30. package/build/types/cryptography/hkdf.d.ts.map +1 -0
  31. package/build/types/cryptography/hmac.d.ts +22 -0
  32. package/build/types/cryptography/hmac.d.ts.map +1 -0
  33. package/build/types/cryptography/rescueCipher.d.ts +29 -0
  34. package/build/types/cryptography/rescueCipher.d.ts.map +1 -0
  35. package/build/types/cryptography/rescueCipherCommon.d.ts +45 -0
  36. package/build/types/cryptography/rescueCipherCommon.d.ts.map +1 -0
  37. package/build/types/cryptography/rescueDesc.d.ts +80 -0
  38. package/build/types/cryptography/rescueDesc.d.ts.map +1 -0
  39. package/build/types/cryptography/rescuePrimeHash.d.ts +23 -0
  40. package/build/types/cryptography/rescuePrimeHash.d.ts.map +1 -0
  41. package/build/types/ctUtils.d.ts +50 -0
  42. package/build/types/ctUtils.d.ts.map +1 -0
  43. package/build/{index.d.ts → types/idl/arcium.d.ts} +5 -901
  44. package/build/types/idl/arcium.d.ts.map +1 -0
  45. package/build/types/idl/arcium_staking.d.ts +4589 -0
  46. package/build/types/idl/arcium_staking.d.ts.map +1 -0
  47. package/build/types/idl/index.d.ts +15 -0
  48. package/build/types/idl/index.d.ts.map +1 -0
  49. package/build/types/index.d.ts +33 -0
  50. package/build/types/index.d.ts.map +1 -0
  51. package/build/types/localEnv.d.ts +15 -0
  52. package/build/types/localEnv.d.ts.map +1 -0
  53. package/build/types/matrix.d.ts +39 -0
  54. package/build/types/matrix.d.ts.map +1 -0
  55. package/build/types/onchain.d.ts +223 -0
  56. package/build/types/onchain.d.ts.map +1 -0
  57. package/build/types/pda.d.ts +89 -0
  58. package/build/types/pda.d.ts.map +1 -0
  59. package/build/types/utils.d.ts +65 -0
  60. package/build/types/utils.d.ts.map +1 -0
  61. package/package.json +6 -6
  62. package/src/arcis/arcisModule.ts +39 -0
  63. package/src/arcis/arcisType.ts +303 -0
  64. package/src/arcis/packer.ts +152 -0
  65. package/src/arcis/packing.ts +115 -0
  66. package/src/callback.ts +101 -0
  67. package/src/constants.ts +104 -0
  68. package/src/cryptography/aes128Cipher.ts +16 -0
  69. package/src/cryptography/aes192Cipher.ts +16 -0
  70. package/src/cryptography/aes256Cipher.ts +16 -0
  71. package/src/cryptography/aesCtrCipher.ts +84 -0
  72. package/src/cryptography/arcisEd25519.ts +96 -0
  73. package/src/cryptography/cSplRescueCipher.ts +41 -0
  74. package/src/cryptography/cryptography.ts +82 -0
  75. package/src/cryptography/hkdf.ts +58 -0
  76. package/src/cryptography/hmac.ts +66 -0
  77. package/src/cryptography/rescueCipher.ts +41 -0
  78. package/src/cryptography/rescueCipherCommon.ts +211 -0
  79. package/src/cryptography/rescueDesc.ts +492 -0
  80. package/src/cryptography/rescuePrimeHash.ts +72 -0
  81. package/src/ctUtils.ts +124 -0
  82. package/src/idl/arcium.json +12281 -0
  83. package/src/idl/arcium.ts +12287 -0
  84. package/src/idl/arcium_staking.json +4582 -0
  85. package/src/idl/arcium_staking.ts +4588 -0
  86. package/src/idl/index.ts +20 -0
  87. package/src/index.ts +32 -0
  88. package/src/localEnv.ts +39 -0
  89. package/src/matrix.ts +215 -0
  90. package/src/onchain.ts +1020 -0
  91. package/src/pda.ts +203 -0
  92. package/src/utils.ts +126 -0
package/src/onchain.ts ADDED
@@ -0,0 +1,1020 @@
1
+ import { Program, AnchorProvider } from '@coral-xyz/anchor';
2
+ import * as anchor from '@coral-xyz/anchor';
3
+ import { ConfirmOptions, PublicKey } from '@solana/web3.js';
4
+ import { ARCIUM_IDL, ArciumIdlType } from './idl/index.js';
5
+ import { sha256 } from './cryptography/cryptography.js';
6
+ import { Arcium } from './idl/arcium.js';
7
+ import {
8
+ COMP_DEF_ACC_SEED,
9
+ COMP_DEF_OFFSET_SIZE,
10
+ DISCRIMINATOR_SIZE,
11
+ MAX_ACCOUNT_SIZE,
12
+ MAX_EMBIGGEN_IX_PER_TX,
13
+ MAX_REALLOC_PER_IX,
14
+ MAX_UPLOAD_PER_TX_BYTES,
15
+ OFFSET_BUFFER_SIZE,
16
+ } from './constants.js';
17
+ import {
18
+ getArciumProgramId,
19
+ getMXEAccAddress,
20
+ getComputationAccAddress,
21
+ getMempoolAccAddress,
22
+ getExecutingPoolAccAddress,
23
+ getFeePoolAccAddress,
24
+ getRawCircuitAccAddress,
25
+ getLookupTableAddress,
26
+ } from './pda.js';
27
+ import { ComputationReference, isNullRef, optionalLog } from './utils.js';
28
+
29
+ const TINY_MEMPOOL_ACC_NAME: Arcium['accounts']['21']['name'] = 'tinyMempool';
30
+ const TINY_MEMPOOL_DISCRIMINATOR: Arcium['accounts']['21']['discriminator'] = [
31
+ 176,
32
+ 33,
33
+ 67,
34
+ 108,
35
+ 73,
36
+ 135,
37
+ 110,
38
+ 166,
39
+ ];
40
+
41
+ const TINY_EXECPOOL_ACC_NAME: Arcium['accounts']['20']['name'] = 'tinyExecPool';
42
+ const TINY_EXECPOOL_DISCRIMINATOR: Arcium['accounts']['20']['discriminator'] = [
43
+ 80,
44
+ 245,
45
+ 5,
46
+ 90,
47
+ 154,
48
+ 189,
49
+ 190,
50
+ 172,
51
+ ];
52
+
53
+ const SMALL_MEMPOOL_ACC_NAME: Arcium['accounts']['19']['name'] = 'smallMempool';
54
+ const SMALL_MEMPOOL_DISCRIMINATOR: Arcium['accounts']['19']['discriminator'] = [
55
+ 123,
56
+ 153,
57
+ 151,
58
+ 118,
59
+ 126,
60
+ 71,
61
+ 73,
62
+ 92,
63
+ ];
64
+
65
+ const SMALL_EXECPOOL_ACC_NAME: Arcium['accounts']['18']['name'] = 'smallExecPool';
66
+ const SMALL_EXECPOOL_DISCRIMINATOR: Arcium['accounts']['18']['discriminator'] = [
67
+ 37,
68
+ 147,
69
+ 249,
70
+ 253,
71
+ 217,
72
+ 136,
73
+ 3,
74
+ 87,
75
+ ];
76
+ const MEDIUM_MEMPOOL_ACC_NAME: Arcium['accounts']['12']['name'] = 'mediumMempool';
77
+ const MEDIUM_MEMPOOL_DISCRIMINATOR: Arcium['accounts']['12']['discriminator'] = [
78
+ 10,
79
+ 249,
80
+ 58,
81
+ 39,
82
+ 255,
83
+ 231,
84
+ 199,
85
+ 168,
86
+ ];
87
+
88
+ const MEDIUM_EXECPOOL_ACC_NAME: Arcium['accounts']['11']['name'] = 'mediumExecPool';
89
+ const MEDIUM_EXECPOOL_DISCRIMINATOR: Arcium['accounts']['11']['discriminator'] = [
90
+ 97,
91
+ 117,
92
+ 128,
93
+ 202,
94
+ 213,
95
+ 76,
96
+ 5,
97
+ 163,
98
+ ];
99
+
100
+ const LARGE_MEMPOOL_ACC_NAME: Arcium['accounts']['9']['name'] = 'largeMempool';
101
+ const LARGE_MEMPOOL_DISCRIMINATOR: Arcium['accounts']['9']['discriminator'] = [
102
+ 16,
103
+ 168,
104
+ 90,
105
+ 235,
106
+ 249,
107
+ 207,
108
+ 73,
109
+ 223,
110
+ ];
111
+
112
+ const LARGE_EXECPOOL_ACC_NAME: Arcium['accounts']['8']['name'] = 'largeExecPool';
113
+ const LARGE_EXECPOOL_DISCRIMINATOR: Arcium['accounts']['8']['discriminator'] = [
114
+ 147,
115
+ 145,
116
+ 148,
117
+ 170,
118
+ 30,
119
+ 13,
120
+ 43,
121
+ 216,
122
+ ];
123
+
124
+ const MEMPOOL_DISCRIMINATOR_MAP = {
125
+ [TINY_MEMPOOL_DISCRIMINATOR.toString()]: TINY_MEMPOOL_ACC_NAME,
126
+ [SMALL_MEMPOOL_DISCRIMINATOR.toString()]: SMALL_MEMPOOL_ACC_NAME,
127
+ [MEDIUM_MEMPOOL_DISCRIMINATOR.toString()]: MEDIUM_MEMPOOL_ACC_NAME,
128
+ [LARGE_MEMPOOL_DISCRIMINATOR.toString()]: LARGE_MEMPOOL_ACC_NAME,
129
+ } as const;
130
+
131
+ const EXECPOOL_DISCRIMINATOR_MAP = {
132
+ [TINY_EXECPOOL_DISCRIMINATOR.toString()]: TINY_EXECPOOL_ACC_NAME,
133
+ [SMALL_EXECPOOL_DISCRIMINATOR.toString()]: SMALL_EXECPOOL_ACC_NAME,
134
+ [MEDIUM_EXECPOOL_DISCRIMINATOR.toString()]: MEDIUM_EXECPOOL_ACC_NAME,
135
+ [LARGE_EXECPOOL_DISCRIMINATOR.toString()]: LARGE_EXECPOOL_ACC_NAME,
136
+ } as const;
137
+
138
+ /**
139
+ * Represents a mempool account of any size (tiny, small, medium, or large).
140
+ * Mempools store pending computations prioritized by fee, with a time-to-live of 180 slots.
141
+ * Each size supports different maximum heap capacities:
142
+ * - Tiny: 1 computation
143
+ * - Small: 3 computations
144
+ * - Medium: 10 computations
145
+ * - Large: 100 computations
146
+ */
147
+ export type MempoolAccount = anchor.IdlTypes<ArciumIdlType>['tinyMempool'] | anchor.IdlTypes<ArciumIdlType>['smallMempool'] | anchor.IdlTypes<ArciumIdlType>['mediumMempool'] | anchor.IdlTypes<ArciumIdlType>['largeMempool'];
148
+
149
+ /**
150
+ * Represents an executing pool account of any size (tiny, small, medium, or large).
151
+ * Executing pools manage parallel computation execution with account locking.
152
+ * Each size supports different maximum parallel computations:
153
+ * - Tiny: 1 parallel computation
154
+ * - Small: 3 parallel computations
155
+ * - Medium: 10 parallel computations
156
+ * - Large: 100 parallel computations
157
+ */
158
+ export type ExecutingPoolAccount = anchor.IdlTypes<ArciumIdlType>['tinyExecPool'] | anchor.IdlTypes<ArciumIdlType>['smallExecPool'] | anchor.IdlTypes<ArciumIdlType>['mediumExecPool'] | anchor.IdlTypes<ArciumIdlType>['largeExecPool'];
159
+
160
+ /**
161
+ * IDL-generated type for circuit source location.
162
+ * Discriminated union with variants: local, onChain, offChain.
163
+ */
164
+ export type CircuitSource = anchor.IdlTypes<ArciumIdlType>['circuitSource'];
165
+
166
+ /**
167
+ * Fetch and decode the mempool account info for any mempool account size.
168
+ * @param provider - Anchor provider to use for fetching accounts.
169
+ * @param mempoolAccPubkey - Public key of the mempool account.
170
+ * @returns Decoded mempool account info.
171
+ * @throws Error if the account cannot be fetched or the discriminator is unknown.
172
+ */
173
+ export async function getMempoolAccInfo(provider: AnchorProvider, mempoolAccPubkey: anchor.web3.PublicKey): Promise<MempoolAccount> {
174
+ const accData = await provider.connection.getAccountInfo(mempoolAccPubkey);
175
+ if (accData === null) {
176
+ throw new Error(`Failed to fetch mempool account ${mempoolAccPubkey.toBase58()}`);
177
+ }
178
+
179
+ const discriminator = Array.from(accData.data.subarray(0, DISCRIMINATOR_SIZE)).toString();
180
+ const accName = MEMPOOL_DISCRIMINATOR_MAP[discriminator];
181
+
182
+ if (accName === undefined) {
183
+ throw new Error(`Unknown mempool account discriminator: ${discriminator}`);
184
+ }
185
+ const program = getArciumProgram(provider);
186
+ return program.coder.accounts.decode(accName, accData.data);
187
+ }
188
+
189
+ /**
190
+ * Fetch and decode the executing pool account info for any pool size.
191
+ * @param provider - Anchor provider to use for fetching accounts.
192
+ * @param executingPoolAccPubkey - Public key of the executing pool account.
193
+ * @returns Decoded executing pool account info.
194
+ * @throws Error if the account cannot be fetched or the discriminator is unknown.
195
+ */
196
+ export async function getExecutingPoolAccInfo(provider: AnchorProvider, executingPoolAccPubkey: anchor.web3.PublicKey): Promise<ExecutingPoolAccount> {
197
+ const accData = await provider.connection.getAccountInfo(executingPoolAccPubkey);
198
+ if (accData === null) {
199
+ throw new Error(`Failed to fetch executing pool account ${executingPoolAccPubkey.toBase58()}`);
200
+ }
201
+
202
+ const discriminator = Array.from(accData.data.subarray(0, DISCRIMINATOR_SIZE)).toString();
203
+ const accName = EXECPOOL_DISCRIMINATOR_MAP[discriminator];
204
+
205
+ if (accName === undefined) {
206
+ throw new Error(`Unknown executing pool account discriminator: ${discriminator}`);
207
+ }
208
+ const program = getArciumProgram(provider);
209
+ return program.coder.accounts.decode(accName, accData.data);
210
+ }
211
+
212
+ /**
213
+ * Return all computation references in the mempool for a given account.
214
+ * Only non-stake computations are included.
215
+ * @param arciumProgram - Anchor program instance.
216
+ * @param address - Public key of the mempool account.
217
+ * @returns Array of ComputationReference objects.
218
+ */
219
+ export async function getComputationsInMempool(
220
+ arciumProgram: anchor.Program<ArciumIdlType>,
221
+ address: PublicKey,
222
+ ): Promise<ComputationReference[]> {
223
+ const mempool = await getMempoolAccInfo(
224
+ arciumProgram.provider as anchor.AnchorProvider,
225
+ address,
226
+ );
227
+ const startIndex = mempool.inner.computations.startIndex;
228
+ const length = mempool.inner.computations.length;
229
+ const elems = mempool.inner.computations.elems;
230
+
231
+ function isValid(validBits: number[], idx: number): boolean {
232
+ const byte = idx >>> 3;
233
+ const bit = idx & 7;
234
+
235
+ if (byte >= validBits.length) {
236
+ // This should never happen, so we'll want to know about it
237
+ throw new Error(
238
+ `isValid: byte ${byte} >= validBits.length ${validBits.length}`,
239
+ );
240
+ }
241
+
242
+ return (validBits[byte] & (1 << bit)) !== 0;
243
+ }
244
+
245
+ // Handle circular buffer wraparound
246
+ const refs = [];
247
+ for (let i = 0; i < length; i++) {
248
+ const idx = (startIndex + i) % elems.length;
249
+ // Only save non-stake computations
250
+ if (isValid(mempool.inner.computations.validBits, idx)) {
251
+ refs.push(...elems[idx].entries);
252
+ }
253
+ }
254
+
255
+ return refs
256
+ .flat()
257
+ .filter(
258
+ (ref) => !isNullRef(ref as ComputationReference),
259
+ ) as ComputationReference[];
260
+ }
261
+
262
+ /**
263
+ * Statistics about priority fees for computations in a mempool.
264
+ * @property mean - Average priority fee across all computations.
265
+ * @property median - Middle value of priority fees when sorted.
266
+ * @property min - Lowest priority fee in the mempool.
267
+ * @property max - Highest priority fee in the mempool.
268
+ * @property count - Total number of computations in the mempool.
269
+ */
270
+ export interface MempoolPriorityFeeStats {
271
+ mean: anchor.BN;
272
+ median: anchor.BN;
273
+ min: anchor.BN;
274
+ max: anchor.BN;
275
+ count: number;
276
+ }
277
+
278
+ /**
279
+ * Calculate priority fee statistics for computations in a mempool.
280
+ * @param arciumProgram - Anchor program instance.
281
+ * @param mempoolAddress - Public key of the mempool account.
282
+ * @returns Priority fee statistics (mean, median, min, max, count).
283
+ */
284
+ export async function getMempoolPriorityFeeStats(
285
+ arciumProgram: anchor.Program<ArciumIdlType>,
286
+ mempoolAddress: PublicKey,
287
+ ): Promise<MempoolPriorityFeeStats> {
288
+ const refs = await getComputationsInMempool(arciumProgram, mempoolAddress);
289
+
290
+ if (refs.length === 0) {
291
+ const zero = new anchor.BN(0);
292
+ return { mean: zero, median: zero, min: zero, max: zero, count: 0 };
293
+ }
294
+
295
+ const fees = refs.map(ref => ref.priorityFee).sort((a, b) => a.cmp(b));
296
+
297
+ const sum = fees.reduce((acc, fee) => acc.add(fee), new anchor.BN(0));
298
+ const mean = sum.div(new anchor.BN(fees.length));
299
+
300
+ const mid = Math.floor(fees.length / 2);
301
+ const median = fees.length % 2 === 0
302
+ ? fees[mid - 1].add(fees[mid]).div(new anchor.BN(2))
303
+ : fees[mid];
304
+
305
+ return {
306
+ mean,
307
+ median,
308
+ min: fees[0],
309
+ max: fees[fees.length - 1],
310
+ count: fees.length,
311
+ };
312
+ }
313
+
314
+ /**
315
+ * Fetch a specific utility key from the MXE account.
316
+ * @param provider - Anchor provider to use for fetching accounts.
317
+ * @param mxeProgramId - Public key of the MXE program.
318
+ * @param field - Field name to extract ('x25519Pubkey' or 'ed25519VerifyingKey').
319
+ * @returns Utility key as a Uint8Array, or null if not set.
320
+ */
321
+ async function getMXEUtilityKey(
322
+ provider: AnchorProvider,
323
+ mxeProgramId: anchor.web3.PublicKey,
324
+ field: 'x25519Pubkey' | 'ed25519VerifyingKey',
325
+ ): Promise<Uint8Array | null> {
326
+ const program = getArciumProgram(provider);
327
+ const mxeAccAddress = getMXEAccAddress(mxeProgramId);
328
+ const mxeAccInfo = await program.account.mxeAccount.fetch(mxeAccAddress);
329
+
330
+ if ('set' in mxeAccInfo.utilityPubkeys) {
331
+ const setData = mxeAccInfo.utilityPubkeys.set as {
332
+ 0:
333
+ {
334
+ x25519Pubkey: number[],
335
+ ed25519VerifyingKey: number[],
336
+ elGamalPubkey: number[],
337
+ pubkeyValidityProof: number[]
338
+ }
339
+ };
340
+ return new Uint8Array(setData[0][field]);
341
+ }
342
+ else if ('unset' in mxeAccInfo.utilityPubkeys) {
343
+ const unsetData = mxeAccInfo.utilityPubkeys.unset as {
344
+ 0: {
345
+ x25519Pubkey: number[],
346
+ ed25519VerifyingKey: number[],
347
+ elGamalPubkey: number[],
348
+ pubkeyValidityProof: number[]
349
+ }; 1: boolean[]
350
+ };
351
+ if (unsetData[1].every(Boolean)) {
352
+ return new Uint8Array(unsetData[0][field]);
353
+ }
354
+ }
355
+ return null;
356
+ }
357
+
358
+ /**
359
+ * Fetch and extract the MXE X25519 public key from the MXE account.
360
+ * @param provider - Anchor provider to use for fetching accounts.
361
+ * @param mxeProgramId - Public key of the MXE program.
362
+ * @returns MXE's X25519 public key as a Uint8Array, or null if not set.
363
+ */
364
+ export async function getMXEPublicKey(
365
+ provider: AnchorProvider,
366
+ mxeProgramId: anchor.web3.PublicKey,
367
+ ): Promise<Uint8Array | null> {
368
+ return getMXEUtilityKey(provider, mxeProgramId, 'x25519Pubkey');
369
+ }
370
+
371
+ /**
372
+ * Fetch and extract the MXE arcis Ed25519 verifying key from the MXE account.
373
+ * @param provider - Anchor provider to use for fetching accounts.
374
+ * @param mxeProgramId - Public key of the MXE program.
375
+ * @returns MXE's arcis Ed25519 verifying key as a Uint8Array, or null if not set.
376
+ */
377
+ export async function getMXEArcisEd25519VerifyingKey(
378
+ provider: AnchorProvider,
379
+ mxeProgramId: anchor.web3.PublicKey,
380
+ ): Promise<Uint8Array | null> {
381
+ return getMXEUtilityKey(provider, mxeProgramId, 'ed25519VerifyingKey');
382
+ }
383
+
384
+ /**
385
+ * Represents the current state of a circuit's on-chain presence.
386
+ * @internal Used by `uploadCircuit` - most users don't need this directly.
387
+ */
388
+ export type CircuitState = 'OnchainPending' | 'OnchainFinalized' | 'Offchain';
389
+
390
+ /**
391
+ * Determine the current state of a circuit based on its on-chain configuration.
392
+ * @internal Called internally by `uploadCircuit` - most users don't need this directly.
393
+ * @param circuitSource - circuitSource field from ComputationDefinitionAccount.
394
+ * @returns Current state of the circuit.
395
+ */
396
+ export function getCircuitState(circuitSource: CircuitSource): CircuitState {
397
+ if (!('onChain' in circuitSource) || !circuitSource.onChain) {
398
+ return 'Offchain';
399
+ }
400
+ if (circuitSource.onChain[0].isCompleted) {
401
+ return 'OnchainFinalized';
402
+ }
403
+ return 'OnchainPending';
404
+ }
405
+
406
+ /**
407
+ * Upload a circuit to the blockchain, splitting it into multiple accounts if necessary.
408
+ * @param provider - Anchor provider to use for transactions.
409
+ * @param circuitName - Name of the circuit.
410
+ * @param mxeProgramId - Public key of the MXE program.
411
+ * @param rawCircuit - Raw circuit data as a Uint8Array.
412
+ * @param logging - Whether to log progress (default: true).
413
+ * @param chunkSize - Number of upload transactions to send in parallel (default: 500).
414
+ * @param confirmOptions - Transaction confirmation options.
415
+ * @returns Array of transaction signatures for all upload and finalize transactions.
416
+ */
417
+ export async function uploadCircuit(
418
+ provider: AnchorProvider,
419
+ circuitName: string,
420
+ mxeProgramId: anchor.web3.PublicKey,
421
+ rawCircuit: Uint8Array,
422
+ logging: boolean = true,
423
+ chunkSize: number = 500,
424
+ confirmOptions?: ConfirmOptions,
425
+ ): Promise<string[]> {
426
+ const compDefAccInfo = getCompDefAccInfo(circuitName, mxeProgramId);
427
+ const program = getArciumProgram(provider);
428
+ const compDefAcc = await program.account.computationDefinitionAccount.fetch(compDefAccInfo.pubkey);
429
+
430
+ // Type assertion at boundary where Anchor's fetch() returns the account data
431
+ const state = getCircuitState(compDefAcc.circuitSource as CircuitSource);
432
+ if (state !== 'OnchainPending') {
433
+ optionalLog(logging, `Circuit ${circuitName} skipped: ${state}`);
434
+ return [];
435
+ }
436
+
437
+ // 9 = 8-byte discriminator + 1-byte bump for ComputationDefinitionRaw account
438
+ const numAccs = Math.ceil(rawCircuit.length / (MAX_ACCOUNT_SIZE - 9));
439
+ const sigs: string[] = [];
440
+ const uploadPromises = [];
441
+ for (let i = 0; i < numAccs; i++) {
442
+ uploadPromises.push(
443
+ uploadToCircuitAcc(
444
+ provider,
445
+ program,
446
+ rawCircuit.subarray(
447
+ i * (MAX_ACCOUNT_SIZE - 9),
448
+ (i + 1) * (MAX_ACCOUNT_SIZE - 9),
449
+ ),
450
+ i,
451
+ compDefAccInfo,
452
+ mxeProgramId,
453
+ logging,
454
+ chunkSize,
455
+ confirmOptions,
456
+ ),
457
+ );
458
+ }
459
+
460
+ sigs.push(...(await Promise.all(uploadPromises)).flat());
461
+
462
+ const finalizeCompDefTx = await buildFinalizeCompDefTx(provider, compDefAccInfo.offset, mxeProgramId);
463
+ sigs.push(await signAndSendWithBlockhash(provider, finalizeCompDefTx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions));
464
+
465
+ return sigs;
466
+ }
467
+
468
+ /**
469
+ * Queue a key recovery initialization for an MXE on a given cluster.
470
+ * @param provider - Anchor provider for signing and sending.
471
+ * @param clusterOffset - Cluster offset to recover keys from.
472
+ * @param mxeProgramId - Public key of the MXE program.
473
+ * @param confirmOptions - Transaction confirmation options.
474
+ * @returns Array of transaction signatures.
475
+ */
476
+ export async function queueKeyRecoveryInit(
477
+ provider: AnchorProvider,
478
+ clusterOffset: number,
479
+ mxeProgramId: anchor.web3.PublicKey,
480
+ confirmOptions?: ConfirmOptions,
481
+ ): Promise<string[]> {
482
+ const program = getArciumProgram(provider);
483
+ const sigs: string[] = [];
484
+ const queueKeyRecoveryInitTx = await program.methods
485
+ .queueKeyRecoveryInit(clusterOffset, mxeProgramId)
486
+ .accounts({
487
+ signer: provider.publicKey,
488
+ mxeProgram: mxeProgramId,
489
+ })
490
+ .transaction();
491
+ sigs.push(await signAndSendWithBlockhash(
492
+ provider,
493
+ queueKeyRecoveryInitTx,
494
+ await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || "confirmed" }),
495
+ confirmOptions,
496
+ ));
497
+ return sigs;
498
+ }
499
+
500
+ /**
501
+ * Build a transaction to finalize a computation definition.
502
+ * @param provider - Anchor provider to use for transactions.
503
+ * @param compDefOffset - Offset of the computation definition.
504
+ * @param mxeProgramId - Public key of the MXE program.
505
+ * @returns Transaction to finalize the computation definition.
506
+ */
507
+ export async function buildFinalizeCompDefTx(
508
+ provider: AnchorProvider,
509
+ compDefOffset: number,
510
+ mxeProgramId: anchor.web3.PublicKey,
511
+ ): Promise<anchor.web3.Transaction> {
512
+ const program = getArciumProgram(provider);
513
+ const compDefOffsetBuffer = Buffer.alloc(OFFSET_BUFFER_SIZE);
514
+ compDefOffsetBuffer.writeUInt32LE(compDefOffset, 0);
515
+ return program.methods
516
+ .finalizeComputationDefinition(compDefOffset, mxeProgramId)
517
+ .accounts({
518
+ signer: provider.publicKey,
519
+ })
520
+ .transaction();
521
+ }
522
+
523
+ async function uploadToCircuitAcc(
524
+ provider: AnchorProvider,
525
+ program: Program<Arcium>,
526
+ rawCircuitPart: Uint8Array,
527
+ rawCircuitIndex: number,
528
+ compDefAccInfo: { pubkey: anchor.web3.PublicKey; offset: number },
529
+ mxeProgramId: anchor.web3.PublicKey,
530
+ shouldLog: boolean = true,
531
+ chunkSize: number = 500,
532
+ confirmOptions?: ConfirmOptions,
533
+ ): Promise<string[]> {
534
+ const rawCircuitPda = getRawCircuitAccAddress(compDefAccInfo.pubkey, rawCircuitIndex);
535
+ const existingAcc = await provider.connection.getAccountInfo(rawCircuitPda);
536
+
537
+ // Skip entirely if account exists with correct size (pre-seeded at genesis or already uploaded)
538
+ // Account layout: 8-byte discriminator + 1-byte bump + circuit data
539
+ const requiredAccountSize = rawCircuitPart.length + 9;
540
+ if (existingAcc !== null && existingAcc.data.length >= requiredAccountSize) {
541
+ optionalLog(
542
+ shouldLog,
543
+ `Raw circuit acc ${rawCircuitIndex} already exists with sufficient size, skipping`,
544
+ );
545
+ return [];
546
+ }
547
+
548
+ const sigs: string[] = [];
549
+
550
+ if (existingAcc === null) {
551
+ const initTx = await program.methods
552
+ .initRawCircuitAcc(compDefAccInfo.offset, mxeProgramId, rawCircuitIndex)
553
+ .accounts({
554
+ signer: provider.publicKey,
555
+ })
556
+ .rpc();
557
+ optionalLog(
558
+ shouldLog,
559
+ `Initiated raw circuit acc with raw circuit index ${rawCircuitIndex}`,
560
+ );
561
+ sigs.push(initTx);
562
+ }
563
+
564
+ if (rawCircuitPart.length > MAX_REALLOC_PER_IX) {
565
+ // We only need to add size if the init didn't already make us big enough
566
+ const nonAsyncTxCount = Math.ceil(
567
+ rawCircuitPart.length / (MAX_REALLOC_PER_IX * MAX_EMBIGGEN_IX_PER_TX),
568
+ );
569
+
570
+ for (let i = 0; i < nonAsyncTxCount; i++) {
571
+ optionalLog(shouldLog, `Sending resize tx ${i} of ${nonAsyncTxCount}`);
572
+ // eslint-disable-next-line no-await-in-loop
573
+ const tx = await buildResizeTx(
574
+ program,
575
+ provider.publicKey,
576
+ compDefAccInfo,
577
+ mxeProgramId,
578
+ rawCircuitIndex,
579
+ MAX_REALLOC_PER_IX
580
+ + i * (MAX_REALLOC_PER_IX * MAX_EMBIGGEN_IX_PER_TX),
581
+ rawCircuitPart.length,
582
+ );
583
+ // eslint-disable-next-line no-await-in-loop
584
+ const blockInfo = await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' });
585
+ sigs.push(
586
+ // eslint-disable-next-line no-await-in-loop
587
+ await signAndSendWithBlockhash(provider, tx, blockInfo, confirmOptions),
588
+ );
589
+ optionalLog(shouldLog, `Sent resize tx ${i} of ${nonAsyncTxCount}`);
590
+ }
591
+ }
592
+
593
+ optionalLog(shouldLog, 'Done sending resize txs');
594
+
595
+ // The previous txs have allocated all the space we need now, so we can send the remaining upload txs async to one another now
596
+ const remainingTxCount = Math.ceil(
597
+ rawCircuitPart.length / MAX_UPLOAD_PER_TX_BYTES,
598
+ );
599
+
600
+ optionalLog(shouldLog, `Sending ${remainingTxCount} upload txs`);
601
+
602
+ const remainingTxs: string[] = [];
603
+ const blockInfo = await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' });
604
+ for (let i = 0; i < remainingTxCount; i += chunkSize) {
605
+ optionalLog(
606
+ shouldLog,
607
+ `Sending chunk ${i / chunkSize + 1
608
+ } of ${Math.ceil(remainingTxCount / chunkSize)}`,
609
+ );
610
+ // Array to hold promises for the current chunk
611
+ const chunkPromises = [];
612
+ // Handle the last chunk
613
+ const currentChunkSize = Math.min(chunkSize, remainingTxCount - i);
614
+
615
+ for (let j = 0; j < currentChunkSize; j++) {
616
+ const offset = MAX_UPLOAD_PER_TX_BYTES * (i + j);
617
+ // eslint-disable-next-line no-await-in-loop
618
+ const tx = await buildUploadCircuitTx(
619
+ program,
620
+ provider.publicKey,
621
+ compDefAccInfo,
622
+ mxeProgramId,
623
+ Buffer.copyBytesFrom(rawCircuitPart, offset, MAX_UPLOAD_PER_TX_BYTES),
624
+ offset,
625
+ rawCircuitIndex,
626
+ );
627
+ chunkPromises.push(
628
+ signAndSendWithBlockhash(provider, tx, blockInfo, confirmOptions),
629
+ );
630
+ }
631
+
632
+ // Wait for the current chunk to complete before proceeding
633
+ // eslint-disable-next-line no-await-in-loop
634
+ const chunkResults = await Promise.all(chunkPromises);
635
+ // Add the results of the current chunk to the main array
636
+ remainingTxs.push(...chunkResults);
637
+ optionalLog(
638
+ shouldLog,
639
+ `Done sending chunk ${i / chunkSize + 1
640
+ } of ${Math.ceil(remainingTxCount / chunkSize)}`,
641
+ );
642
+ }
643
+
644
+ return sigs.concat(remainingTxs);
645
+ }
646
+
647
+ async function buildResizeTx(
648
+ program: anchor.Program<Arcium>,
649
+ signerPubkey: anchor.web3.PublicKey,
650
+ compDefAccInfo: { pubkey: anchor.web3.PublicKey; offset: number },
651
+ mxeProgramId: anchor.web3.PublicKey,
652
+ rawCircuitIndex: number,
653
+ currentSize: number,
654
+ requiredSize: number,
655
+ ): Promise<anchor.web3.Transaction> {
656
+ const ix = await program.methods
657
+ .embiggenRawCircuitAcc(compDefAccInfo.offset, mxeProgramId, rawCircuitIndex)
658
+ .accounts({
659
+ signer: signerPubkey,
660
+ })
661
+ .instruction();
662
+ const resizeSize = Math.min(
663
+ requiredSize - currentSize,
664
+ MAX_EMBIGGEN_IX_PER_TX * MAX_REALLOC_PER_IX,
665
+ );
666
+ // We might alloc a bit too much here, but that's ok because we fix it in the upload tx
667
+ const ixCount = Math.ceil(resizeSize / MAX_REALLOC_PER_IX);
668
+ const tx = new anchor.web3.Transaction();
669
+ for (let i = 0; i < ixCount; i++) {
670
+ tx.add(ix);
671
+ }
672
+
673
+ return tx;
674
+ }
675
+
676
+ async function buildUploadCircuitTx(
677
+ program: anchor.Program<Arcium>,
678
+ signerPubkey: anchor.web3.PublicKey,
679
+ compDefAccInfo: { pubkey: anchor.web3.PublicKey; offset: number },
680
+ mxeProgramId: anchor.web3.PublicKey,
681
+ bytes: Buffer,
682
+ circuitOffset: number,
683
+ rawCircuitIndex: number,
684
+ ): Promise<anchor.web3.Transaction> {
685
+ if (bytes.length > MAX_UPLOAD_PER_TX_BYTES) {
686
+ throw new Error(`Upload circuit bytes must be ${MAX_UPLOAD_PER_TX_BYTES} bytes or less per tx`);
687
+ }
688
+
689
+ let bytesInner = bytes;
690
+
691
+ if (bytesInner.length < MAX_UPLOAD_PER_TX_BYTES) {
692
+ const paddedBytes = Buffer.allocUnsafe(MAX_UPLOAD_PER_TX_BYTES);
693
+ paddedBytes.set(bytesInner);
694
+ bytesInner = paddedBytes;
695
+ }
696
+
697
+ return program.methods
698
+ .uploadCircuit(
699
+ compDefAccInfo.offset,
700
+ mxeProgramId,
701
+ rawCircuitIndex,
702
+ Array.from(bytesInner),
703
+ circuitOffset,
704
+ )
705
+ .accounts({
706
+ signer: signerPubkey,
707
+ })
708
+ .transaction();
709
+ }
710
+
711
+ async function signAndSendWithBlockhash(
712
+ provider: AnchorProvider,
713
+ tx: anchor.web3.Transaction,
714
+ block: anchor.web3.BlockhashWithExpiryBlockHeight,
715
+ confirmOptions?: ConfirmOptions,
716
+ ): Promise<string> {
717
+ // eslint-disable-next-line no-param-reassign
718
+ tx.recentBlockhash = block.blockhash;
719
+ // eslint-disable-next-line no-param-reassign
720
+ tx.lastValidBlockHeight = block.lastValidBlockHeight;
721
+ return provider.sendAndConfirm(tx, [], confirmOptions || { commitment: 'confirmed' });
722
+ }
723
+
724
+ /**
725
+ * Return the base seed for an Arcium account, given its name.
726
+ * @param accName - Name of the account.
727
+ * @returns Base seed as a Uint8Array.
728
+ */
729
+ export function getArciumAccountBaseSeed(accName: string): Uint8Array {
730
+ return Buffer.from(accName, 'utf-8');
731
+ }
732
+
733
+ /**
734
+ * Compute the offset for a computation definition account, based on the circuit name.
735
+ * @param circuitName - Name of the circuit.
736
+ * @returns Offset as a 4-byte Uint8Array.
737
+ */
738
+ export function getCompDefAccOffset(circuitName: string): Uint8Array {
739
+ const hash = new Uint8Array(sha256([Buffer.from(circuitName, 'utf-8')]));
740
+ return hash.slice(0, COMP_DEF_OFFSET_SIZE);
741
+ }
742
+
743
+ /**
744
+ * Return an Anchor program instance for the Arcium program.
745
+ * @param provider - Anchor provider to use.
746
+ * @returns Anchor program instance for Arcium.
747
+ */
748
+ export function getArciumProgram(provider: AnchorProvider): Program<Arcium> {
749
+ return new Program(ARCIUM_IDL as unknown as Arcium, provider);
750
+ }
751
+
752
+ /**
753
+ * Return the public key and offset for a computation definition account, given the circuit name and MXE program ID.
754
+ * @param circuitName - Name of the circuit.
755
+ * @param mxeProgramId - Public key of the MXE program.
756
+ * @returns Object containing the public key and offset for the computation definition account.
757
+ */
758
+ function getCompDefAccInfo(circuitName: string, mxeProgramId: anchor.web3.PublicKey): { pubkey: anchor.web3.PublicKey; offset: number } {
759
+ const offset = getCompDefAccOffset(circuitName);
760
+ const pda = getCompDefAccPDA(getArciumProgramId(), mxeProgramId, offset);
761
+ return { pubkey: pda, offset: Buffer.from(offset).readUInt32LE(0) };
762
+ }
763
+
764
+ /**
765
+ * Return the PDA for a computation definition account, given the program ID, MXE program ID, and offset.
766
+ * @param arciumProgramId - Public key of the Arcium program.
767
+ * @param mxeProgramId - Public key of the MXE program.
768
+ * @param offset - Offset as a Uint8Array.
769
+ * @returns PDA for the computation definition account.
770
+ */
771
+ function getCompDefAccPDA(arciumProgramId: anchor.web3.PublicKey, mxeProgramId: anchor.web3.PublicKey, offset: Uint8Array): anchor.web3.PublicKey {
772
+ return anchor.web3.PublicKey.findProgramAddressSync([Buffer.from(COMP_DEF_ACC_SEED, 'utf-8'), mxeProgramId.toBuffer(), offset], arciumProgramId)[0];
773
+ }
774
+
775
+ /**
776
+ * Set an MXE to Recovery status, initiating the key recovery process.
777
+ * @param provider - Anchor provider to use for transactions.
778
+ * @param mxeProgramId - Public key of the MXE program to recover.
779
+ * @param confirmOptions - Transaction confirmation options.
780
+ * @returns Transaction signature.
781
+ */
782
+ export async function recoverMxe(
783
+ provider: AnchorProvider,
784
+ mxeProgramId: anchor.web3.PublicKey,
785
+ confirmOptions?: ConfirmOptions,
786
+ ): Promise<string> {
787
+ const program = getArciumProgram(provider);
788
+ const tx = await program.methods
789
+ .recoverMxe(mxeProgramId)
790
+ .accountsPartial({
791
+ authority: provider.publicKey,
792
+ mxeProgram: mxeProgramId,
793
+ })
794
+ .transaction();
795
+ return signAndSendWithBlockhash(provider, tx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
796
+ }
797
+
798
+ /**
799
+ * Initialize key recovery execution by creating the MxeRecoveryAccount and
800
+ * registering the key_recovery_final computation definition on the backup MXE.
801
+ * This is split into two parts due to Solana's 10KB per-instruction allocation limit.
802
+ * @param provider - Anchor provider to use for transactions.
803
+ * @param originalMxeProgramId - Public key of the original MXE program being recovered.
804
+ * @param backupMxeProgramId - Public key of the backup MXE program that will take over.
805
+ * @param confirmOptions - Transaction confirmation options.
806
+ * @returns Transaction signature from part2.
807
+ */
808
+ export async function initKeyRecoveryExecution(
809
+ provider: AnchorProvider,
810
+ originalMxeProgramId: anchor.web3.PublicKey,
811
+ backupMxeProgramId: anchor.web3.PublicKey,
812
+ confirmOptions?: ConfirmOptions,
813
+ ): Promise<string> {
814
+ // Part 1: Create MxeRecoveryAccount with partial size
815
+ const program = getArciumProgram(provider);
816
+ const tx1 = await program.methods
817
+ .initKeyRecoveryExecutionPart1(originalMxeProgramId, backupMxeProgramId)
818
+ .accountsPartial({
819
+ payer: provider.publicKey,
820
+ originalMxeProgram: originalMxeProgramId,
821
+ backupMxeProgram: backupMxeProgramId,
822
+ })
823
+ .transaction();
824
+ await signAndSendWithBlockhash(provider, tx1, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
825
+
826
+ // Part 2: Reallocate to full size and create computation definition
827
+ const tx2 = await program.methods
828
+ .initKeyRecoveryExecutionPart2(originalMxeProgramId, backupMxeProgramId)
829
+ .accountsPartial({
830
+ authority: provider.publicKey,
831
+ payer: provider.publicKey,
832
+ originalMxeProgram: originalMxeProgramId,
833
+ backupMxeProgram: backupMxeProgramId,
834
+ })
835
+ .transaction();
836
+ return signAndSendWithBlockhash(provider, tx2, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
837
+ }
838
+
839
+ /**
840
+ * Submit a re-encrypted key recovery share from a recovery peer.
841
+ * Recovery peers must decrypt shares using their x25519 private key and re-encrypt
842
+ * them for the backup MXE before submission.
843
+ * @param provider - Anchor provider to use for transactions.
844
+ * @param originalMxeProgramId - Public key of the original MXE program being recovered.
845
+ * @param backupMxeProgramId - Public key of the backup MXE program.
846
+ * @param peerOffset - Offset of the recovery peer.
847
+ * @param peerIndex - Index of this peer in the recovery peers list.
848
+ * @param share - Re-encrypted share: 5 field elements of 32 bytes each (160 bytes total).
849
+ * @param confirmOptions - Transaction confirmation options.
850
+ * @returns Transaction signature.
851
+ */
852
+ export async function submitKeyRecoveryShare(
853
+ provider: AnchorProvider,
854
+ originalMxeProgramId: anchor.web3.PublicKey,
855
+ backupMxeProgramId: anchor.web3.PublicKey,
856
+ peerOffset: number,
857
+ peerIndex: number,
858
+ share: number[][] | Uint8Array[],
859
+ confirmOptions?: ConfirmOptions,
860
+ ): Promise<string> {
861
+ const program = getArciumProgram(provider);
862
+ // Convert to array of 5 elements, each 32 bytes
863
+ const shareArrays: number[][] = share.map(elem => Array.from(elem));
864
+ if (shareArrays.length !== 5) {
865
+ throw new Error(`Share must contain exactly 5 elements, got ${shareArrays.length}`);
866
+ }
867
+ for (let i = 0; i < 5; i++) {
868
+ if (shareArrays[i].length !== 32) {
869
+ throw new Error(`Share element ${i} must be exactly 32 bytes, got ${shareArrays[i].length}`);
870
+ }
871
+ }
872
+ const tx = await program.methods
873
+ .submitKeyRecoveryShare(
874
+ originalMxeProgramId,
875
+ backupMxeProgramId,
876
+ peerOffset,
877
+ peerIndex,
878
+ shareArrays as number[][],
879
+ )
880
+ .accountsPartial({
881
+ signer: provider.publicKey,
882
+ originalMxeProgram: originalMxeProgramId,
883
+ backupMxeProgram: backupMxeProgramId,
884
+ })
885
+ .transaction();
886
+ return signAndSendWithBlockhash(provider, tx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
887
+ }
888
+
889
+ /**
890
+ * Finalize key recovery execution after the submission threshold is met.
891
+ * This queues the key_recovery_finalize MPC computation on the backup cluster.
892
+ * @param provider - Anchor provider to use for transactions.
893
+ * @param originalMxeProgramId - Public key of the original MXE program being recovered.
894
+ * @param backupMxeProgramId - Public key of the backup MXE program.
895
+ * @param clusterOffset - Cluster offset where the backup MXE is deployed.
896
+ * @param keyRecoveryFinalizeOffset - Computation offset for the key_recovery_finalize computation.
897
+ * @param confirmOptions - Transaction confirmation options.
898
+ * @returns Transaction signature.
899
+ */
900
+ export async function finalizeKeyRecoveryExecution(
901
+ provider: AnchorProvider,
902
+ originalMxeProgramId: anchor.web3.PublicKey,
903
+ backupMxeProgramId: anchor.web3.PublicKey,
904
+ clusterOffset: number,
905
+ keyRecoveryFinalizeOffset: anchor.BN,
906
+ confirmOptions?: ConfirmOptions,
907
+ ): Promise<string> {
908
+ const program = getArciumProgram(provider);
909
+ const tx = await program.methods
910
+ .finalizeKeyRecoveryExecution(originalMxeProgramId, backupMxeProgramId, clusterOffset)
911
+ .accountsPartial({
912
+ authority: provider.publicKey,
913
+ payer: provider.publicKey,
914
+ keyRecoveryFinalizeComputation: getComputationAccAddress(clusterOffset, keyRecoveryFinalizeOffset),
915
+ executingPool: getExecutingPoolAccAddress(clusterOffset),
916
+ mempool: getMempoolAccAddress(clusterOffset),
917
+ originalMxeProgram: originalMxeProgramId,
918
+ backupMxeProgram: backupMxeProgramId,
919
+ })
920
+ .transaction();
921
+ return signAndSendWithBlockhash(provider, tx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
922
+ }
923
+
924
+ /**
925
+ * Initialize an MXE (part 1). Due to Solana's 10KB per-instruction allocation limit,
926
+ * this only partially allocates recovery_cluster_acc.
927
+ * Call initMxePart2 afterwards to finish allocation and add keygen to mempool.
928
+ * @param provider - Anchor provider to use for transactions.
929
+ * @param mxeProgramId - Public key to use as the MXE program ID.
930
+ * @param confirmOptions - Transaction confirmation options.
931
+ * @returns Transaction signature.
932
+ */
933
+ export async function initMxePart1(
934
+ provider: AnchorProvider,
935
+ mxeProgramId: anchor.web3.PublicKey,
936
+ confirmOptions?: ConfirmOptions,
937
+ ): Promise<string> {
938
+ const program = getArciumProgram(provider);
939
+ const tx = await program.methods
940
+ .initMxePart1()
941
+ .accountsPartial({
942
+ signer: provider.publicKey,
943
+ mxeProgram: mxeProgramId,
944
+ })
945
+ .transaction();
946
+ return signAndSendWithBlockhash(provider, tx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
947
+ }
948
+
949
+ /**
950
+ * Finish MXE initialization (part 2).
951
+ * Reallocate recovery_cluster_acc to full size, initialize recovery_peers,
952
+ * and add the keygen computation to the mempool.
953
+ * @param provider - Anchor provider to use for transactions.
954
+ * @param clusterOffset - Cluster offset to associate with the MXE.
955
+ * @param mxeProgramId - Public key to use as the MXE program ID.
956
+ * @param recoveryPeers - Array of 100 node offsets for recovery peers (0 for unused slots).
957
+ * @param keygenOffset - Computation offset for the keygen computation.
958
+ * @param keyRecoveryInitOffset - Computation offset for the key_recovery_init computation.
959
+ * @param lutOffset - Lookup table offset for the MXE's address lookup table.
960
+ * @param mxeAuthority - Optional authority for the MXE (defaults to provider.publicKey).
961
+ * @param confirmOptions - Transaction confirmation options.
962
+ * @returns Transaction signature.
963
+ */
964
+ export async function initMxePart2(
965
+ provider: AnchorProvider,
966
+ clusterOffset: number,
967
+ mxeProgramId: anchor.web3.PublicKey,
968
+ recoveryPeers: number[],
969
+ keygenOffset: anchor.BN,
970
+ keyRecoveryInitOffset: anchor.BN,
971
+ lutOffset: anchor.BN,
972
+ mxeAuthority?: anchor.web3.PublicKey,
973
+ confirmOptions?: ConfirmOptions,
974
+ ): Promise<string> {
975
+ const program = getArciumProgram(provider);
976
+ // Ensure recoveryPeers has exactly 100 elements
977
+ const paddedRecoveryPeers = [...recoveryPeers];
978
+ while (paddedRecoveryPeers.length < 100) {
979
+ paddedRecoveryPeers.push(0);
980
+ }
981
+ if (paddedRecoveryPeers.length > 100) {
982
+ throw new Error('recoveryPeers must have at most 100 elements');
983
+ }
984
+
985
+ const tx = await program.methods
986
+ .initMxePart2(
987
+ clusterOffset,
988
+ mxeProgramId,
989
+ paddedRecoveryPeers,
990
+ keygenOffset,
991
+ keyRecoveryInitOffset,
992
+ lutOffset,
993
+ )
994
+ .accountsPartial({
995
+ signer: provider.publicKey,
996
+ mxeProgram: mxeProgramId,
997
+ poolAccount: getFeePoolAccAddress(),
998
+ mxeAuthority: mxeAuthority ?? provider.publicKey,
999
+ addressLookupTable: getLookupTableAddress(mxeProgramId, lutOffset),
1000
+ })
1001
+ .transaction();
1002
+ return signAndSendWithBlockhash(provider, tx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
1003
+ }
1004
+
1005
+ export async function claimComputationRent(
1006
+ provider: AnchorProvider,
1007
+ clusterOffset: number,
1008
+ computationOffset: anchor.BN,
1009
+ confirmOptions?: ConfirmOptions,
1010
+ ): Promise<string> {
1011
+ const program = getArciumProgram(provider);
1012
+ const tx = await program.methods
1013
+ .claimComputationRent(computationOffset, clusterOffset)
1014
+ .accountsPartial({
1015
+ signer: provider.publicKey,
1016
+ comp: getComputationAccAddress(clusterOffset, computationOffset),
1017
+ })
1018
+ .transaction();
1019
+ return signAndSendWithBlockhash(provider, tx, await provider.connection.getLatestBlockhash({ commitment: confirmOptions?.commitment || 'confirmed' }), confirmOptions);
1020
+ }