@chipi-stack/backend 14.4.0 → 14.7.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.
@@ -1,33 +1,411 @@
1
1
  'use strict';
2
2
 
3
- var types = require('@chipi-stack/types');
4
- var shared = require('@chipi-stack/shared');
5
- var starknet = require('starknet');
6
3
  var CryptoES = require('crypto-es');
4
+ var starknet = require('starknet');
5
+ var shared = require('@chipi-stack/shared');
6
+ var types = require('@chipi-stack/types');
7
7
 
8
8
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
9
 
10
10
  var CryptoES__default = /*#__PURE__*/_interopDefault(CryptoES);
11
11
 
12
- // src/transactions.ts
13
- var decryptPrivateKey = (encryptedPrivateKey, password) => {
14
- if (!encryptedPrivateKey || !password) {
15
- throw new Error("Encrypted private key and password are required");
12
+ var __defProp = Object.defineProperty;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __esm = (fn, res) => function __init() {
15
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
+ };
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
20
+ };
21
+ var decryptPrivateKey;
22
+ var init_encryption = __esm({
23
+ "src/lib/encryption.ts"() {
24
+ decryptPrivateKey = (encryptedPrivateKey, password) => {
25
+ if (!encryptedPrivateKey || !password) {
26
+ throw new Error("Encrypted private key and password are required");
27
+ }
28
+ try {
29
+ const bytes = CryptoES__default.default.AES.decrypt(encryptedPrivateKey, password);
30
+ const decrypted = bytes.toString(CryptoES__default.default.enc.Utf8);
31
+ if (!decrypted) {
32
+ throw new Error("Decryption resulted in empty string");
33
+ }
34
+ return decrypted;
35
+ } catch (error) {
36
+ if (error instanceof Error) {
37
+ throw error;
38
+ }
39
+ throw new Error(`Decryption failed: ${error}`);
40
+ }
41
+ };
42
+ }
43
+ });
44
+
45
+ // src/encryption.ts
46
+ var init_encryption2 = __esm({
47
+ "src/encryption.ts"() {
48
+ init_encryption();
49
+ }
50
+ });
51
+ function toBigInt(x) {
52
+ if (typeof x === "bigint") return x;
53
+ if (typeof x === "number") return BigInt(x);
54
+ return BigInt(x);
55
+ }
56
+ function poseidonSpan(values) {
57
+ const felts = values.map((v) => typeof v === "bigint" ? "0x" + v.toString(16) : v);
58
+ return BigInt(starknet.hash.computePoseidonHashOnElements(felts));
59
+ }
60
+ function hashCall(call) {
61
+ const calldataFelts = starknet.CallData.toHex(
62
+ Array.isArray(call.calldata) ? call.calldata : [call.calldata]
63
+ );
64
+ const calldataItems = calldataFelts.map((x) => BigInt(x));
65
+ const calldataHash = poseidonSpan(calldataItems);
66
+ return poseidonSpan([
67
+ CALL_TYPE_HASH_REV1,
68
+ toBigInt(call.contractAddress),
69
+ toBigInt(starknet.hash.getSelectorFromName(call.entrypoint)),
70
+ calldataHash
71
+ ]);
72
+ }
73
+ function hashCalls(calls) {
74
+ const callHashes = calls.map(hashCall);
75
+ return poseidonSpan(callHashes);
76
+ }
77
+ function hashStarknetDomain(chainIdFelt) {
78
+ return poseidonSpan([
79
+ STARKNET_DOMAIN_TYPE_HASH_REV1,
80
+ BigInt(OE_DOMAIN_NAME),
81
+ OE_DOMAIN_VERSION,
82
+ toBigInt(chainIdFelt),
83
+ OE_DOMAIN_REVISION
84
+ ]);
85
+ }
86
+ function hashOutsideExecutionStruct(oe) {
87
+ const callsArrayHash = hashCalls(oe.calls);
88
+ return poseidonSpan([
89
+ OUTSIDE_EXECUTION_TYPE_HASH_REV1,
90
+ toBigInt(oe.caller),
91
+ toBigInt(oe.nonce),
92
+ oe.execute_after,
93
+ oe.execute_before,
94
+ callsArrayHash
95
+ ]);
96
+ }
97
+ function computeSnip12Hash(oe, contractAddress, chainIdFelt) {
98
+ const domainHash = hashStarknetDomain(chainIdFelt);
99
+ const structHash = hashOutsideExecutionStruct(oe);
100
+ return poseidonSpan([
101
+ BigInt(STARKNET_MESSAGE_PREFIX),
102
+ domainHash,
103
+ toBigInt(contractAddress),
104
+ structHash
105
+ ]);
106
+ }
107
+ function snip12HashToHexAsciiBytes(h) {
108
+ const hex = h.toString(16).padStart(64, "0");
109
+ return new TextEncoder().encode(hex);
110
+ }
111
+ var OUTSIDE_EXECUTION_TYPE_HASH_REV1, CALL_TYPE_HASH_REV1, STARKNET_DOMAIN_TYPE_HASH_REV1, STARKNET_MESSAGE_PREFIX, OE_DOMAIN_NAME, OE_DOMAIN_VERSION, OE_DOMAIN_REVISION, SIG_VERSION_V2_SNIP12, SIG_VERSION_V2_THRESHOLD;
112
+ var init_snip12_hash = __esm({
113
+ "src/shhh/snip12-hash.ts"() {
114
+ OUTSIDE_EXECUTION_TYPE_HASH_REV1 = 0x5a4b49e17039355cd95d1f0981d75901191d1319b1f4b05a9a791d218d7e0cn;
115
+ CALL_TYPE_HASH_REV1 = 0x3635c7f2a7ba93844c0d064e18e487f35ab90f7c39d00f186a781fc3f0c2ca9n;
116
+ STARKNET_DOMAIN_TYPE_HASH_REV1 = 0x1ff2f602e42168014d405a94f75e8a93d640751d71d16311266e140d8b0a210n;
117
+ STARKNET_MESSAGE_PREFIX = starknet.shortString.encodeShortString("StarkNet Message");
118
+ OE_DOMAIN_NAME = starknet.shortString.encodeShortString("Account.execute_from_outside");
119
+ OE_DOMAIN_VERSION = 2n;
120
+ OE_DOMAIN_REVISION = 1n;
121
+ starknet.shortString.encodeShortString("V1_HEX_ASCII");
122
+ SIG_VERSION_V2_SNIP12 = starknet.shortString.encodeShortString("V2_SNIP12");
123
+ SIG_VERSION_V2_THRESHOLD = starknet.shortString.encodeShortString("V2_THRESHOLD");
124
+ BigInt(
125
+ "0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872"
126
+ );
127
+ BigInt(
128
+ "0x94c5a761f34b25a4e603c651ac0e1fc4fad9cdb5517f7fa1bb54044c7e5ef8"
129
+ );
130
+ }
131
+ });
132
+ var init_threshold = __esm({
133
+ "src/shhh/threshold.ts"() {
134
+ init_snip12_hash();
135
+ BigInt(SIG_VERSION_V2_SNIP12);
136
+ BigInt(SIG_VERSION_V2_THRESHOLD);
137
+ }
138
+ });
139
+ function buildOutsideExecution(params) {
140
+ const nowSec = BigInt(Math.floor(Date.now() / 1e3));
141
+ const after = params.executeAfter ?? nowSec - BigInt(OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER);
142
+ const before = params.executeBefore ?? nowSec + BigInt(MAX_OE_VALIDITY_WINDOW_SECONDS) - BigInt(OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER);
143
+ if (before <= after) {
144
+ throw new Error(
145
+ `executeBefore (${before}) must be strictly greater than executeAfter (${after}).`
146
+ );
147
+ }
148
+ if (before - after > BigInt(MAX_OE_VALIDITY_WINDOW_SECONDS)) {
149
+ throw new Error(
150
+ `OE validity window ${before - after}s exceeds max ${MAX_OE_VALIDITY_WINDOW_SECONDS}s (audit M-2). Adjust executeBefore.`
151
+ );
152
+ }
153
+ return {
154
+ caller: params.caller ?? ANY_CALLER_FELT,
155
+ nonce: params.nonce,
156
+ execute_after: after,
157
+ execute_before: before,
158
+ calls: params.calls
159
+ };
160
+ }
161
+ function computeOeHash(oe, accountAddress, chainId) {
162
+ return computeSnip12Hash(oe, accountAddress, chainId);
163
+ }
164
+ function wrapV2Snip12Envelope(params) {
165
+ const tag = typeof params.kindTag === "bigint" ? params.kindTag : BigInt(starknet.shortString.encodeShortString(params.kindTag));
166
+ return [
167
+ BigInt(SIG_VERSION_V2_SNIP12),
168
+ params.ownerId,
169
+ tag,
170
+ ...params.payload.map((v) => typeof v === "bigint" ? v : BigInt(v))
171
+ ];
172
+ }
173
+ var ANY_CALLER_FELT, MAX_OE_VALIDITY_WINDOW_SECONDS, OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER;
174
+ var init_oe = __esm({
175
+ "src/shhh/oe.ts"() {
176
+ init_snip12_hash();
177
+ init_threshold();
178
+ ANY_CALLER_FELT = BigInt(starknet.shortString.encodeShortString("ANY_CALLER"));
179
+ MAX_OE_VALIDITY_WINDOW_SECONDS = 2 * 60 * 60;
180
+ OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER = 60;
181
+ }
182
+ });
183
+ var init_compute_wallet_address = __esm({
184
+ "src/shhh/compute-wallet-address.ts"() {
185
+ }
186
+ });
187
+ function buildStarkEnvelope(params) {
188
+ const pkHex = "0x" + (typeof params.privateKey === "bigint" ? params.privateKey : BigInt(params.privateKey)).toString(16).padStart(64, "0");
189
+ const sig = starknet.ec.starkCurve.sign("0x" + params.messageHash.toString(16), pkHex);
190
+ const r = BigInt("0x" + sig.r.toString(16));
191
+ const s = BigInt("0x" + sig.s.toString(16));
192
+ const ownerIndex = params.ownerIndex ?? 0n;
193
+ if (ownerIndex < 0n || ownerIndex >= 0x100000000n) {
194
+ throw new Error(
195
+ `buildStarkEnvelope: ownerIndex ${ownerIndex} is out of u32 range [0, 2^32). owner_id is the u32 INDEX into the wallet's owner_set (0 for primary, 1 for second, etc.); do NOT pass an owner_commitment or pubkey here.`
196
+ );
197
+ }
198
+ return wrapV2Snip12Envelope({
199
+ ownerId: ownerIndex,
200
+ kindTag: STARK_KIND_TAG,
201
+ payload: [r, s]
202
+ });
203
+ }
204
+ var STARK_KIND_TAG;
205
+ var init_stark = __esm({
206
+ "src/shhh/signers/stark.ts"() {
207
+ init_compute_wallet_address();
208
+ init_oe();
209
+ STARK_KIND_TAG = "STARK";
210
+ BigInt(starknet.shortString.encodeShortString(STARK_KIND_TAG));
16
211
  }
212
+ });
213
+ async function ensureGaragaInit() {
214
+ if (garagaInitPromise) return garagaInitPromise;
215
+ const attempt = (async () => {
216
+ const garaga = await import('garaga');
217
+ await garaga.init();
218
+ return garaga;
219
+ })();
220
+ garagaInitPromise = attempt;
17
221
  try {
18
- const bytes = CryptoES__default.default.AES.decrypt(encryptedPrivateKey, password);
19
- const decrypted = bytes.toString(CryptoES__default.default.enc.Utf8);
20
- if (!decrypted) {
21
- throw new Error("Decryption resulted in empty string");
222
+ return await attempt;
223
+ } catch (err) {
224
+ if (garagaInitPromise === attempt) garagaInitPromise = null;
225
+ throw err;
226
+ }
227
+ }
228
+ function bytesToLeBigInt(bytes) {
229
+ let x = 0n;
230
+ for (let i = bytes.length - 1; i >= 0; i--) x = x << 8n | BigInt(bytes[i]);
231
+ return x;
232
+ }
233
+ function ed25519SignedBytes(messageHash) {
234
+ return snip12HashToHexAsciiBytes(messageHash);
235
+ }
236
+ async function buildEd25519Envelope(params) {
237
+ const ownerIndex = params.ownerIndex ?? 0n;
238
+ if (ownerIndex < 0n || ownerIndex >= 0x100000000n) {
239
+ throw new Error(
240
+ `buildEd25519Envelope: ownerIndex ${ownerIndex} is out of u32 range [0, 2^32). owner_id is the u32 INDEX into the wallet's owner_set (0 for primary, 1 for second, etc.); do NOT pass an owner_commitment or pubkey here.`
241
+ );
242
+ }
243
+ if (params.privateKey.length !== 32) {
244
+ throw new Error(
245
+ `Ed25519 private key must be exactly 32 bytes; got ${params.privateKey.length}.`
246
+ );
247
+ }
248
+ const ed = await import('@noble/ed25519');
249
+ const publicKey = await ed.getPublicKeyAsync(params.privateKey);
250
+ const signedBytes = ed25519SignedBytes(params.messageHash);
251
+ const signature = await ed.signAsync(signedBytes, params.privateKey);
252
+ return assembleEd25519Envelope({
253
+ R: signature.slice(0, 32),
254
+ s: signature.slice(32, 64),
255
+ publicKey,
256
+ signedBytes,
257
+ ownerIndex
258
+ });
259
+ }
260
+ async function assembleEd25519Envelope(parts) {
261
+ const garaga = await ensureGaragaInit();
262
+ const payload = garaga.eddsaCalldataBuilder(
263
+ bytesToLeBigInt(parts.R),
264
+ bytesToLeBigInt(parts.s),
265
+ bytesToLeBigInt(parts.publicKey),
266
+ parts.signedBytes,
267
+ false
268
+ );
269
+ return wrapV2Snip12Envelope({
270
+ ownerId: parts.ownerIndex,
271
+ kindTag: ED25519_KIND_TAG,
272
+ payload
273
+ });
274
+ }
275
+ var ED25519_KIND_TAG, garagaInitPromise;
276
+ var init_ed25519 = __esm({
277
+ "src/shhh/signers/ed25519.ts"() {
278
+ init_compute_wallet_address();
279
+ init_oe();
280
+ init_snip12_hash();
281
+ ED25519_KIND_TAG = "ED25519";
282
+ BigInt(starknet.shortString.encodeShortString(ED25519_KIND_TAG));
283
+ garagaInitPromise = null;
284
+ }
285
+ });
286
+
287
+ // src/shhh/execute-paymaster-raw.ts
288
+ var execute_paymaster_raw_exports = {};
289
+ __export(execute_paymaster_raw_exports, {
290
+ executeShhhSponsoredRaw: () => executeShhhSponsoredRaw,
291
+ resolveShhhChainId: () => resolveShhhChainId,
292
+ serializeExecuteFromOutsideV2Calldata: () => serializeExecuteFromOutsideV2Calldata
293
+ });
294
+ function resolveShhhChainId(params) {
295
+ if (params.override !== void 0) {
296
+ return typeof params.override === "bigint" ? "0x" + params.override.toString(16) : params.override;
297
+ }
298
+ const url = (params.baseUrl ?? "").toLowerCase();
299
+ if (url.includes("sepolia")) return SN_SEPOLIA_CHAIN_ID;
300
+ return SN_MAIN_CHAIN_ID;
301
+ }
302
+ function serializeExecuteFromOutsideV2Calldata(params) {
303
+ const out = [];
304
+ const oe = params.outsideExecution;
305
+ const toBig = (v) => typeof v === "bigint" ? v : BigInt(v);
306
+ out.push(toBig(oe.caller));
307
+ out.push(toBig(oe.nonce));
308
+ out.push(oe.execute_after);
309
+ out.push(oe.execute_before);
310
+ out.push(BigInt(oe.calls.length));
311
+ for (const call of oe.calls) {
312
+ out.push(BigInt(call.contractAddress));
313
+ out.push(BigInt(starknet.hash.getSelectorFromName(call.entrypoint)));
314
+ const callData = Array.isArray(call.calldata) ? call.calldata : [];
315
+ out.push(BigInt(callData.length));
316
+ for (const datum of callData) {
317
+ out.push(typeof datum === "bigint" ? datum : BigInt(datum));
22
318
  }
23
- return decrypted;
24
- } catch (error) {
25
- if (error instanceof Error) {
26
- throw error;
319
+ }
320
+ out.push(BigInt(params.signature.length));
321
+ for (const sigFelt of params.signature) {
322
+ out.push(sigFelt);
323
+ }
324
+ return out.map((v) => "0x" + v.toString(16));
325
+ }
326
+ function newOeNonce() {
327
+ const tsMs = BigInt(Date.now());
328
+ const rand = BigInt(Math.floor(Math.random() * 16777215));
329
+ return tsMs << 24n | rand;
330
+ }
331
+ async function executeShhhSponsoredRaw(params) {
332
+ const signerKind = params.signerKind ?? "STARK";
333
+ const SDK_SHIPPED_OE_KINDS = ["STARK", "ED25519"];
334
+ if (!SDK_SHIPPED_OE_KINDS.includes(signerKind)) {
335
+ throw new Error(
336
+ `SHHH signerKind="${signerKind}" is not yet shipped from the SDK (Wave B ships STARK + ED25519). Wait for the follow-up PR that adds external-rooted (MetaMask, Phantom, passkey, Apple) signing.`
337
+ );
338
+ }
339
+ const privateKeyHex = decryptPrivateKey(params.wallet.encryptedPrivateKey, params.encryptKey);
340
+ if (!privateKeyHex) {
341
+ throw new Error(
342
+ "Failed to decrypt private key for SHHH OE. Verify the encryptKey matches the one used at wallet creation."
343
+ );
344
+ }
345
+ const oe = buildOutsideExecution({
346
+ nonce: params.nonce ?? newOeNonce(),
347
+ calls: params.calls,
348
+ caller: ANY_CALLER_FELT
349
+ });
350
+ const chainId = resolveShhhChainId({
351
+ override: params.chainId,
352
+ baseUrl: params.client.baseUrl
353
+ });
354
+ const messageHash = computeOeHash(oe, params.wallet.publicKey, chainId);
355
+ let signature;
356
+ if (signerKind === "STARK") {
357
+ signature = buildStarkEnvelope({
358
+ privateKey: privateKeyHex,
359
+ messageHash
360
+ });
361
+ } else {
362
+ const edKeyHex = privateKeyHex.startsWith("0x") ? privateKeyHex.slice(2) : privateKeyHex;
363
+ if (edKeyHex.length !== 64) {
364
+ throw new Error(
365
+ `SHHH ED25519 private key is ${edKeyHex.length} hex chars; expected 64 (32 raw bytes). The stored ciphertext does not look like a Chipi-held Ed25519 key \u2014 check wallet provenance.`
366
+ );
27
367
  }
28
- throw new Error(`Decryption failed: ${error}`);
368
+ const edPrivKey = Uint8Array.from(Buffer.from(edKeyHex, "hex"));
369
+ signature = await buildEd25519Envelope({
370
+ privateKey: edPrivKey,
371
+ messageHash
372
+ });
29
373
  }
30
- };
374
+ const calldata = serializeExecuteFromOutsideV2Calldata({
375
+ outsideExecution: oe,
376
+ signature
377
+ });
378
+ const response = await params.client.post({
379
+ endpoint: "/transactions/execute-sponsored-raw",
380
+ bearerToken: params.bearerToken,
381
+ body: {
382
+ accountAddress: params.wallet.publicKey,
383
+ calldata,
384
+ signerKind,
385
+ ...params.externalUserId ? { externalUserId: params.externalUserId } : {}
386
+ }
387
+ });
388
+ if (!response || !response.transactionHash) {
389
+ throw new Error(
390
+ "execute-sponsored-raw response missing transactionHash. Check the chipi-back logs for upstream paymaster errors."
391
+ );
392
+ }
393
+ return response.transactionHash;
394
+ }
395
+ var SN_MAIN_CHAIN_ID, SN_SEPOLIA_CHAIN_ID;
396
+ var init_execute_paymaster_raw = __esm({
397
+ "src/shhh/execute-paymaster-raw.ts"() {
398
+ init_encryption2();
399
+ init_oe();
400
+ init_stark();
401
+ init_ed25519();
402
+ SN_MAIN_CHAIN_ID = "0x534e5f4d41494e";
403
+ SN_SEPOLIA_CHAIN_ID = "0x534e5f5345504f4c4941";
404
+ }
405
+ });
406
+
407
+ // src/execute-paymaster-transaction.ts
408
+ init_encryption2();
31
409
  function normalizeSignature(signature) {
32
410
  if (Array.isArray(signature)) {
33
411
  if (signature.length < 2) {
@@ -67,6 +445,22 @@ var executePaymasterTransaction = async ({
67
445
  );
68
446
  }
69
447
  const walletType = wallet.walletType ?? "READY";
448
+ if (walletType === "SHHH") {
449
+ const { executeShhhSponsoredRaw: executeShhhSponsoredRaw2 } = await Promise.resolve().then(() => (init_execute_paymaster_raw(), execute_paymaster_raw_exports));
450
+ const shhhSignerKind = (wallet.signerKind ?? "STARK") || "STARK";
451
+ return await executeShhhSponsoredRaw2({
452
+ client,
453
+ bearerToken,
454
+ wallet: {
455
+ publicKey: wallet.publicKey,
456
+ encryptedPrivateKey: wallet.encryptedPrivateKey,
457
+ walletType: wallet.walletType
458
+ },
459
+ encryptKey,
460
+ calls,
461
+ signerKind: shhhSignerKind
462
+ });
463
+ }
70
464
  const isKnownType = Object.prototype.hasOwnProperty.call(shared.WALLET_RPC_ENDPOINTS, walletType) && Object.prototype.hasOwnProperty.call(shared.WALLET_CLASS_HASHES, walletType);
71
465
  const rpcUrl = isKnownType ? shared.WALLET_RPC_ENDPOINTS[walletType] : shared.WALLET_RPC_ENDPOINTS["CHIPI"];
72
466
  const accountClassHash = isKnownType ? shared.WALLET_CLASS_HASHES[walletType] : shared.WALLET_CLASS_HASHES["CHIPI"];
@@ -279,9 +673,9 @@ var ChipiTransactions = class {
279
673
  /**
280
674
  * Get on-chain transaction status
281
675
  */
282
- async getTransactionStatus(hash, bearerToken) {
676
+ async getTransactionStatus(hash4, bearerToken) {
283
677
  return this.client.get({
284
- endpoint: `${shared.API_ENDPOINTS.TRANSACTIONS}/${hash}/status`,
678
+ endpoint: `${shared.API_ENDPOINTS.TRANSACTIONS}/${hash4}/status`,
285
679
  bearerToken
286
680
  });
287
681
  }