@chipi-stack/backend 14.3.1 → 14.6.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 +1 @@
1
- {"version":3,"sources":["../src/lib/encryption.ts","../src/wallets.ts"],"names":["CryptoES","WALLET_RPC_ENDPOINTS","RpcProvider","ec","WALLET_CLASS_HASHES","hash","Account","API_ENDPOINTS","num","ChipiTransactionError","CairoCustomEnum","CairoOption","CairoOptionVariant","CallData","ChipiApiError"],"mappings":";;;;;;;;;;;AAEO,IAAM,iBAAA,GAAoB,CAAC,UAAA,EAAoB,QAAA,KAA6B;AACjF,EAAA,IAAI,CAAC,UAAA,IAAc,CAAC,QAAA,EAAU;AAC5B,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AAEA,EAAA,OAAOA,2BAAS,GAAA,CAAI,OAAA,CAAQ,UAAA,EAAY,QAAQ,EAAE,QAAA,EAAS;AAC7D,CAAA;;;ACkCO,IAAM,eAAN,MAAmB;AAAA,EACxB,YAAoB,MAAA,EAAqB;AAArB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AA0OpB,IAAA,IAAA,CAAA,eAAA,GAAkB,MAAM;AAGtB,MAAA,MAAM,eAAA,GAAkBA,0BAAAA,CAAS,GAAA,CAAI,SAAA,CAAU,OAAO,EAAE,CAAA;AAExD,MAAA,MAAM,UAAA,GAAa,eAAA,CAAgB,QAAA,CAASA,0BAAAA,CAAS,IAAI,GAAG,CAAA;AAG5D,MAAA,MAAM,cAAA,GAAiB,KAAK,UAAU,CAAA,CAAA;AAItC,MAAA,MAAM,gBAAA,GAAmB,MAAA;AAAA,QACvB;AAAA,OACF;AACA,MAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,cAAc,CAAA,GAAI,gBAAA;AAGlD,MAAA,OAAO,CAAA,EAAA,EAAK,gBAAA,CAAiB,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA;AAAA,IAC3C,CAAA;AAAA,EA7P0C;AAAA,EAE1C,MAAM,aACJ,MAAA,EAC+B;AAC/B,IAAA,IAAI;AACF,MAAA,MAAM;AAAA,QACJ,UAAA;AAAA,QACA,cAAA;AAAA,QACA,WAAA;AAAA,QACA,MAAA;AAAA,QACA,UAAA,GAAa,OAAA;AAAA;AAAA,QACb,SAAA,EAAW,eAAA;AAAA,QACX,UAAA;AAAA,QACA;AAAA,OACF,GAAI,MAAA;AAEJ,MAAA,IAAI,CAAC,UAAA,EAAY;AACf,QAAA,IAAI,UAAA,EAAY;AACd,UAAA,MAAM,IAAI,KAAA;AAAA,YACR;AAAA,WACF;AAAA,QACF;AACA,QAAA,MAAM,IAAI,MAAM,4CAA4C,CAAA;AAAA,MAC9D;AAGA,MAAA,MAAM,MAAA,GACJ,UAAA,KAAe,OAAA,GAAUC,2BAAA,CAAqB,QAAQA,2BAAA,CAAqB,KAAA;AAE7E,MAAA,MAAM,WAAW,IAAIC,oBAAA,CAAY,EAAE,OAAA,EAAS,QAAQ,CAAA;AAGpD,MAAA,MAAM,YAAA,GAAe,KAAK,eAAA,EAAgB;AAC1C,MAAA,MAAM,aAAA,GAAgBC,WAAA,CAAG,UAAA,CAAW,WAAA,CAAY,YAAY,CAAA;AAG5D,MAAA,MAAM,mBAAA,GAAsB,IAAA,CAAK,wBAAA,CAAyB,UAAA,EAAY,aAAa,CAAA;AAGnF,MAAA,MAAM,mBACJ,eAAA,KACC,UAAA,KAAe,OAAA,GAAUC,0BAAA,CAAoB,QAAQA,0BAAA,CAAoB,KAAA,CAAA;AAG5E,MAAA,MAAM,YAAYC,aAAA,CAAK,gCAAA;AAAA,QACrB,aAAA;AAAA,QACA,gBAAA;AAAA,QACA,mBAAA;AAAA,QACA;AAAA,OACF;AAGA,MAAA,MAAM,OAAA,GAAU,IAAIC,gBAAA,CAAQ,EAAE,UAAU,OAAA,EAAS,SAAA,EAAW,MAAA,EAAQ,YAAA,EAAc,CAAA;AAElF,MAAA,MAAM,iBAAA,GAAoB,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAoC;AAAA,QAC9E,QAAA,EAAU,CAAA,EAAGC,oBAAA,CAAc,aAAa,CAAA,iBAAA,CAAA;AAAA,QACxC,WAAA;AAAA,QACA,IAAA,EAAM;AAAA,UACJ,KAAA;AAAA,UACA,SAAA;AAAA,UACA,UAAA;AAAA,UACA,aAAA;AAAA,UACA,GAAI,eAAA,IAAmB,EAAE,SAAA,EAAW,eAAA;AAAgB;AACtD,OACD,CAAA;AAED,MAAA,MAAM,EAAE,SAAA,EAAW,gBAAA,EAAkB,wBAAA,EAAyB,GAAI,iBAAA;AAElE,MAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,CAAQ,WAAA,CAAY,SAAS,CAAA;AAEzD,MAAA,MAAM,cAAA,GAAiC;AAAA,QACrC,UAAA,EAAY,wBAAA;AAAA,QACZ,IAAA,EAAM,aAAA;AAAA,QACN,MAAA,EAAQ,CAAA,EAAGC,YAAA,CAAI,KAAA,CAAM,CAAC,CAAC,CAAA,CAAA;AAAA,QACvB,QAAA,EAAU,oBAAoB,GAAA,CAAI,CAAC,UAAUA,YAAA,CAAI,KAAA,CAAM,KAAK,CAAC;AAAA,OAC/D;AAEA,MAAA,MAAM,mBAAA,GAAsB,iBAAA,CAAkB,YAAA,EAAc,UAAU,CAAA;AAGtE,MAAA,MAAM,4BAA4B,MAAA,CAAO,gBAAA,GACrC,kBAAkB,YAAA,EAAc,MAAA,CAAO,gBAAgB,CAAA,GACvD,KAAA,CAAA;AAGJ,MAAA,MAAM,0BAAA,GAA6B,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAA2B;AAAA,QAC9E,QAAA,EAAU,CAAA,EAAGD,oBAAA,CAAc,aAAa,CAAA,CAAA;AAAA,QACxC,WAAA;AAAA,QACA,IAAA,EAAM;AAAA,UACJ,cAAA;AAAA,UACA,MAAA;AAAA,UACA,SAAA;AAAA,UACA,UAAA;AAAA,UACA,KAAA;AAAA,UACA,aAAA,EAAe;AAAA,YACb,CAAA,EAAI,aAAA,CAAsB,CAAA,CAAE,QAAA,EAAS;AAAA,YACrC,CAAA,EAAI,aAAA,CAAsB,CAAA,CAAE,QAAA,EAAS;AAAA,YACrC,UAAW,aAAA,CAAsB;AAAA,WACnC;AAAA,UACA,SAAA;AAAA,UACA,mBAAA;AAAA,UACA,GAAI,yBAAA,IAA6B,EAAE,yBAAA,EAA0B;AAAA,UAC7D,GAAI,MAAA,CAAO,UAAA,IAAc,EAAE,UAAA,EAAY,OAAO,UAAA,EAAW;AAAA,UACzD,GAAI,MAAA,CAAO,YAAA,IAAgB,EAAE,YAAA,EAAc,OAAO,YAAA,EAAa;AAAA,UAC/D,GAAI,MAAA,CAAO,YAAA,IAAgB,QAAQ,EAAE,YAAA,EAAc,OAAO,YAAA,EAAa;AAAA,UACvE,GAAI,MAAA,CAAO,UAAA,IAAc,EAAE,UAAA,EAAY,OAAO,UAAA,EAAW;AAAA,UACzD,cAAA,EAAgB;AAAA,YACd,GAAG,cAAA;AAAA,YACH,IAAA,EAAM,CAAA,EAAG,cAAA,CAAe,IAAI,CAAA,CAAA;AAAA,YAC5B,QAAA,EAAU,eAAe,QAAA,CAAS,GAAA,CAAI,CAAC,IAAA,KAAS,CAAA,EAAG,IAAI,CAAA,CAAE;AAAA;AAC3D;AACF,OACD,CAAA;AAED,MAAA,OAAO,0BAAA;AAAA,IACT,SAAS,KAAA,EAAgB;AACvB,MAAA,OAAA,CAAQ,KAAA,CAAM,mBAAmB,KAAK,CAAA;AAEtC,MAAA,IAAI,iBAAiB,KAAA,IAAS,KAAA,CAAM,OAAA,CAAQ,QAAA,CAAS,KAAK,CAAA,EAAG;AAC3D,QAAA,MAAM,IAAI,KAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AACA,MAAA,MAAM,IAAIE,4BAAA;AAAA,QACR,CAAA,yBAAA,EAA4B,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,UAAU,eAAe,CAAA,CAAA;AAAA,QACpF;AAAA,OACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,wBAAA,CAAyB,YAAwB,aAAA,EAAiC;AACxF,IAAA,IAAI,eAAe,OAAA,EAAS;AAE1B,MAAA,MAAM,QAAA,GAAW,IAAIC,wBAAA,CAAgB;AAAA,QACnC,QAAA,EAAU,EAAE,MAAA,EAAQ,aAAA;AAAc,OACnC,CAAA;AACD,MAAA,MAAM,UAAA,GAAa,IAAIC,oBAAA,CAAqBC,2BAAA,CAAmB,IAAI,CAAA;AAEnE,MAAA,OAAOC,kBAAS,OAAA,CAAQ;AAAA,QACtB,KAAA,EAAO,QAAA;AAAA,QACP,QAAA,EAAU;AAAA,OACX,CAAA;AAAA,IACH;AAGA,IAAA,OAAOA,kBAAS,OAAA,CAAQ;AAAA,MACtB,UAAA,EAAY;AAAA,KACb,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,qBAAA,CAAsB;AAAA,IAC1B,MAAA;AAAA,IACA;AAAA,GACF,EAGwB;AACtB,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAiB;AAAA,MAClD,QAAA,EAAU,CAAA,EAAGN,oBAAA,CAAc,aAAa,CAAA,UAAA,CAAA;AAAA,MACxC,WAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAED,IAAA,OAAO,QAAA;AAAA,EACT;AAAA,EAEA,MAAM,SAAA,CAAU,MAAA,EAAyB,WAAA,EAAwD;AAC/F,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,gBAAe,GAAI,MAAA;AAC3B,MAAA,MAAM,iBAAA,GAAoB,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAuB;AAAA,QACjE,QAAA,EAAU,CAAA,EAAGA,oBAAA,CAAc,aAAa,CAAA,QAAA,CAAA;AAAA,QACxC,MAAA,EAAQ,EAAE,cAAA,EAAe;AAAA,QACzB;AAAA,OACD,CAAA;AAED,MAAA,OAAO,iBAAA;AAAA,IACT,SAAS,GAAA,EAAK;AAEZ,MAAA,IAAI,GAAA,YAAeO,oBAAA,IAAiB,GAAA,CAAI,MAAA,KAAW,GAAA,EAAK;AACtD,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,qBAAA,CACJ,MAAA,EACA,WAAA,EAMC;AACD,IAAA,OAAO,IAAA,CAAK,OAAO,GAAA,CAAI;AAAA,MACrB,QAAA,EAAU,CAAA,EAAGP,oBAAA,CAAc,aAAa,CAAA,oBAAA,CAAA;AAAA,MACxC,MAAA,EAAQ,EAAE,cAAA,EAAgB,MAAA,CAAO,cAAA,EAAe;AAAA,MAChD;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,sBAAA,CACJ,MAAA,EACA,WAAA,EACyC;AACzC,IAAA,MAAM,IAAA,GAA+B;AAAA,MACnC,gBAAgB,MAAA,CAAO,cAAA;AAAA,MACvB,wBAAwB,MAAA,CAAO;AAAA,KACjC;AAEA,IAAA,IAAI,MAAA,CAAO,aAAa,IAAA,EAAM;AAC5B,MAAA,IAAA,CAAK,YAAY,MAAA,CAAO,SAAA;AAAA,IAC1B;AAEA,IAAA,OAAO,IAAA,CAAK,OAAO,KAAA,CAAsC;AAAA,MACvD,QAAA,EAAU,CAAA,EAAGA,oBAAA,CAAc,aAAa,CAAA,0BAAA,CAAA;AAAA,MACxC,WAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAuBA,MAAM,eAAA,CAAgB;AAAA,IACpB,MAAA;AAAA,IACA;AAAA,GACF,EAGqC;AACnC,IAAA,MAAM,kBAAA,GAAqB,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAA6B;AAAA,MACxE,QAAA,EAAU,CAAA,EAAGA,oBAAA,CAAc,aAAa,CAAA,cAAA,CAAA;AAAA,MACxC,MAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,OAAO,kBAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBAAA,CACJ,MAAA,EACA,WAAA,EACuC;AACvC,IAAA,OAAO,IAAA,CAAK,OAAO,IAAA,CAAmC;AAAA,MACpD,QAAA,EAAU,CAAA,EAAGA,oBAAA,CAAc,aAAa,CAAA,gBAAA,CAAA;AAAA,MACxC,WAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,oBAAA,CACJ,MAAA,EACA,WAAA,EACuC;AACvC,IAAA,OAAO,IAAA,CAAK,OAAO,IAAA,CAAmC;AAAA,MACpD,QAAA,EAAU,CAAA,EAAGA,oBAAA,CAAc,aAAa,CAAA,QAAA,CAAA;AAAA,MACxC,WAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AACF","file":"wallets.js","sourcesContent":["import CryptoES from \"crypto-es\";\n\nexport const encryptPrivateKey = (privateKey: string, password: string): string => {\n if (!privateKey || !password) {\n throw new Error(\"Private key and password are required\");\n }\n\n return CryptoES.AES.encrypt(privateKey, password).toString();\n};\n\nexport const decryptPrivateKey = (encryptedPrivateKey: string, password: string): string => {\n if (!encryptedPrivateKey || !password) {\n throw new Error(\"Encrypted private key and password are required\");\n }\n\n try {\n const bytes = CryptoES.AES.decrypt(encryptedPrivateKey, password);\n const decrypted = bytes.toString(CryptoES.enc.Utf8);\n if (!decrypted) {\n throw new Error(\"Decryption resulted in empty string\");\n }\n return decrypted;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Decryption failed: ${error}`);\n }\n};\n","import type {\n CreateWalletParams,\n CreateWalletResponse,\n PrepareWalletCreationResponse,\n CreateCustodialWalletParams,\n GetWalletParams,\n WalletData,\n GetWalletResponse,\n GetTokenBalanceParams,\n GetTokenBalanceResponse,\n WalletType,\n DeploymentData,\n UpdateWalletEncryptionParams,\n UpdateWalletEncryptionResponse,\n PrepareWalletUpgradeParams,\n PrepareWalletUpgradeResponse,\n ExecuteWalletUpgradeParams,\n ExecuteWalletUpgradeResponse,\n} from \"@chipi-stack/types\";\nimport { API_ENDPOINTS, WALLET_CLASS_HASHES, WALLET_RPC_ENDPOINTS } from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport CryptoES from \"crypto-es\";\n\nimport { ChipiTransactionError, ChipiApiError } from \"@chipi-stack/shared\";\nimport {\n Account,\n CairoCustomEnum,\n CairoOption,\n CairoOptionVariant,\n CallData,\n ec,\n hash,\n num,\n RpcProvider,\n stark,\n} from \"starknet\";\nimport { encryptPrivateKey } from \"./encryption\";\n\n// Local DeploymentData type (replacing @avnu/gasless-sdk dependency)\n/**\n * Wallet management utilities\n */\nexport class ChipiWallets {\n constructor(private client: ChipiClient) {}\n\n async createWallet(\n params: CreateWalletParams & { bearerToken: string }\n ): Promise<CreateWalletResponse> {\n try {\n const {\n encryptKey,\n externalUserId,\n bearerToken,\n userId,\n walletType = \"CHIPI\", // Default to CHIPI wallet\n classHash: customClassHash,\n usePasskey,\n chain,\n } = params;\n\n if (!encryptKey) {\n if (usePasskey) {\n throw new Error(\n \"encryptKey is required when using passkey. The passkey authentication should have provided the encryptKey.\"\n );\n }\n throw new Error(\"encryptKey is required for wallet creation\");\n }\n\n // Select RPC endpoint based on wallet type\n const rpcUrl =\n walletType === \"READY\" ? WALLET_RPC_ENDPOINTS.READY : WALLET_RPC_ENDPOINTS.CHIPI;\n\n const provider = new RpcProvider({ nodeUrl: rpcUrl });\n\n // Generating the private key with Stark Curve\n const privateKeyAX = this.getPrivateKeyAX();\n const starkKeyPubAX = ec.starkCurve.getStarkKey(privateKeyAX);\n\n // Build constructor calldata based on wallet type\n const constructorCallData = this.buildConstructorCallData(walletType, starkKeyPubAX);\n\n // Select class hash: custom > wallet type lookup > CHIPI default\n const accountClassHash =\n customClassHash ??\n (walletType === \"READY\" ? WALLET_CLASS_HASHES.READY : WALLET_CLASS_HASHES.CHIPI);\n\n // Calculate future address of the account\n const publicKey = hash.calculateContractAddressFromHash(\n starkKeyPubAX,\n accountClassHash,\n constructorCallData,\n 0\n );\n\n // Initiating Account\n const account = new Account({ provider, address: publicKey, signer: privateKeyAX });\n\n const typedDataResponse = await this.client.post<PrepareWalletCreationResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/prepare-creation`,\n bearerToken,\n body: {\n chain,\n publicKey,\n walletType,\n starkKeyPubAX,\n ...(customClassHash && { classHash: customClassHash }),\n },\n });\n\n const { typedData, accountClassHash: accountClassHashResponse } = typedDataResponse;\n\n const userSignature = await account.signMessage(typedData);\n\n const deploymentData: DeploymentData = {\n class_hash: accountClassHashResponse,\n salt: starkKeyPubAX,\n unique: `${num.toHex(0)}`,\n calldata: constructorCallData.map((value) => num.toHex(value)),\n };\n\n const encryptedPrivateKey = encryptPrivateKey(privateKeyAX, encryptKey);\n\n // Dual-key wrapping: if encryptKeyBackup provided (PIN), encrypt a second copy\n const encryptedPrivateKeyBackup = params.encryptKeyBackup\n ? encryptPrivateKey(privateKeyAX, params.encryptKeyBackup)\n : undefined;\n\n // Call the API to save the wallet in dashboard\n const executeTransactionResponse = await this.client.post<CreateWalletResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}`,\n bearerToken,\n body: {\n externalUserId,\n userId,\n publicKey,\n walletType,\n chain,\n userSignature: {\n r: (userSignature as any).r.toString(),\n s: (userSignature as any).s.toString(),\n recovery: (userSignature as any).recovery,\n },\n typedData,\n encryptedPrivateKey,\n ...(encryptedPrivateKeyBackup && { encryptedPrivateKeyBackup }),\n ...(params.authMethod && { authMethod: params.authMethod }),\n ...(params.credentialId && { credentialId: params.credentialId }),\n ...(params.prfSupported != null && { prfSupported: params.prfSupported }),\n ...(params.deviceType && { deviceType: params.deviceType }),\n deploymentData: {\n ...deploymentData,\n salt: `${deploymentData.salt}`,\n calldata: deploymentData.calldata.map((data) => `${data}`),\n },\n },\n });\n\n return executeTransactionResponse;\n } catch (error: unknown) {\n console.error(\"Detailed error:\", error);\n\n if (error instanceof Error && error.message.includes(\"SSL\")) {\n throw new Error(\n \"SSL connection error. Try using NODE_TLS_REJECT_UNAUTHORIZED=0 or verify the RPC URL\"\n );\n }\n throw new ChipiTransactionError(\n `Failed to create wallet: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"WALLET_CREATION_FAILED\"\n );\n }\n }\n\n /**\n * Build constructor calldata based on wallet type\n * - CHIPI: Simple OpenZeppelin account with just public_key\n * - ARGENT: Argent X Account with owner/guardian structure\n */\n private buildConstructorCallData(walletType: WalletType, starkKeyPubAX: string): string[] {\n if (walletType === \"READY\") {\n // Argent X Account: owner (CairoCustomEnum) + guardian (CairoOption None)\n const axSigner = new CairoCustomEnum({\n Starknet: { pubkey: starkKeyPubAX },\n });\n const axGuardian = new CairoOption<unknown>(CairoOptionVariant.None);\n\n return CallData.compile({\n owner: axSigner,\n guardian: axGuardian,\n });\n }\n\n // ChipiWallet (default): Simple OpenZeppelin account with just public_key\n return CallData.compile({\n public_key: starkKeyPubAX,\n });\n }\n\n /**\n * Create a custodial merchant wallet\n */\n async createCustodialWallet({\n params,\n bearerToken,\n }: {\n params: Omit<CreateCustodialWalletParams, \"orgId\">;\n bearerToken: string;\n }): Promise<WalletData> {\n const response = await this.client.post<WalletData>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/custodial`,\n bearerToken,\n body: params,\n });\n\n return response!;\n }\n\n async getWallet(params: GetWalletParams, bearerToken: string): Promise<GetWalletResponse | null> {\n try {\n const { externalUserId } = params;\n const getWalletResponse = await this.client.get<GetWalletResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/by-user`,\n params: { externalUserId },\n bearerToken,\n });\n\n return getWalletResponse;\n } catch (err) {\n // Check if it's a 404 error (wallet not found)\n if (err instanceof ChipiApiError && err.status === 404) {\n return null;\n }\n throw err;\n }\n }\n\n /**\n * Recover passkey credential metadata for a wallet.\n * Used when localStorage is cleared — SDK recovers credentialId from backend.\n */\n async getCredentialRecovery(\n params: { externalUserId: string },\n bearerToken: string\n ): Promise<{\n credentialId: string | null;\n authMethod: string | null;\n prfSupported: boolean | null;\n deviceType: string | null;\n }> {\n return this.client.get({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/credential-recovery`,\n params: { externalUserId: params.externalUserId },\n bearerToken,\n });\n }\n\n async updateWalletEncryption(\n params: UpdateWalletEncryptionParams,\n bearerToken: string\n ): Promise<UpdateWalletEncryptionResponse> {\n const body: Record<string, string> = {\n externalUserId: params.externalUserId,\n newEncryptedPrivateKey: params.newEncryptedPrivateKey,\n };\n\n if (params.publicKey != null) {\n body.publicKey = params.publicKey;\n }\n\n return this.client.patch<UpdateWalletEncryptionResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/update-encryption-details`,\n bearerToken,\n body,\n });\n }\n\n getPrivateKeyAX = () => {\n // Generate 32 random bytes (256 bits)\n // const privateKeyBytes = Crypto.getRandomBytes(32); old implementation\n const privateKeyBytes = CryptoES.lib.WordArray.random(32);\n // Convert to hex string and ensure it's 64 characters (32 bytes)\n const privateKey = privateKeyBytes.toString(CryptoES.enc.Hex);\n\n // Add '0x' prefix\n const fullPrivateKey = `0x${privateKey}`;\n\n // Ensure the private key is within Starknet's valid range (0 to 2^251 - 1)\n // Convert to BigInt and take modulo 2^251\n const maxStarknetValue = BigInt(\n \"0x800000000000000000000000000000000000000000000000000000000000000\"\n );\n const privateKeyBigInt = BigInt(fullPrivateKey) % maxStarknetValue;\n\n // Convert back to hex string with '0x' prefix\n return `0x${privateKeyBigInt.toString(16)}`;\n };\n\n async getTokenBalance({\n params,\n bearerToken,\n }: {\n params: GetTokenBalanceParams;\n bearerToken: string;\n }): Promise<GetTokenBalanceResponse> {\n const getBalanceResponse = await this.client.get<GetTokenBalanceResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/token-balance`,\n params,\n bearerToken,\n });\n\n return getBalanceResponse;\n }\n\n /**\n * Prepare a wallet upgrade (e.g., READY → CHIPI or CHIPI v29 → v33).\n * Returns typed data that must be signed by the wallet owner.\n */\n async prepareWalletUpgrade(\n params: PrepareWalletUpgradeParams,\n bearerToken: string\n ): Promise<PrepareWalletUpgradeResponse> {\n return this.client.post<PrepareWalletUpgradeResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/prepare-upgrade`,\n bearerToken,\n body: params,\n });\n }\n\n /**\n * Execute a wallet upgrade after the owner has signed the typed data.\n */\n async executeWalletUpgrade(\n params: ExecuteWalletUpgradeParams,\n bearerToken: string\n ): Promise<ExecuteWalletUpgradeResponse> {\n return this.client.post<ExecuteWalletUpgradeResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/upgrade`,\n bearerToken,\n body: params,\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/shhh/compute-wallet-address.ts","../src/shhh/snip12-hash.ts","../src/shhh/threshold.ts","../src/shhh/oe.ts","../src/shhh/signers/ed25519.ts","../src/lib/encryption.ts","../src/encryption.ts","../src/shhh/signers/stark.ts","../src/shhh/execute-paymaster-raw.ts","../src/wallets.ts","../src/migrate-to-shhh.ts","../src/execute-paymaster-transaction.ts","../src/paymaster-adapter.ts","../src/shhh/migrate.ts"],"names":["hash","shortString","CallData","CryptoES","init_encryption","ec","executeShhhSponsoredRaw","WALLET_RPC_ENDPOINTS","WALLET_CLASS_HASHES","RpcProvider","Account","typedData","apiMessage","randomBytes","SHHH_VERIFIER_CLASS_HASHES","API_ENDPOINTS","num","ChipiTransactionError","CairoCustomEnum","CairoOption","CairoOptionVariant","ChipiApiError"],"mappings":"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","file":"wallets.js","sourcesContent":["/**\n * Deterministic ShhhAccount (V8.4) address derivation.\n *\n * Vendored from haycarlitos/shhh-wallet-cairo @ v8.4 commit 3975532\n * source: scripts/ts/compute-wallet-address.ts\n * license: MIT (Copyright (c) 2026 Carlos Castillo and contributors)\n *\n * Given the primary owner (kind + raw pubkey bytes) and the\n * ShhhAccount class hash, returns the Starknet address the account\n * will deploy to.\n *\n * MUST match `src/account.cairo::constructor` at the same commit. If\n * the upstream constructor changes shape, the Shhh maintainer will\n * open a PR against this file.\n */\n\nimport { hash, shortString } from \"starknet\";\n\nexport type ComputeAddressInput = {\n /** SHHH ShhhAccount class hash — see WALLET_CLASS_HASHES.SHHH in @chipi-stack/shared. */\n classHash: bigint;\n /** Primary signer kind, e.g. 'STARK' / 'ED25519' / 'WEBAUTHN_P256'. */\n primaryKind: string;\n /** Raw pubkey bytes interpreted as felt252-sized halves (kind-specific layout). */\n pubkey: bigint[];\n /** Verifier class hash for primaryKind — see SHHH_VERIFIER_CLASS_HASHES. */\n verifierClassHash: bigint;\n /** Optional user-facing tag bound into the constructor (default 'primary'). */\n label?: string;\n /** Factory / UDC address; default 0 for self-deploy. */\n deployerAddress?: bigint;\n};\n\n// poseidon_hash_span matches core::poseidon::poseidon_hash_span on-chain.\nfunction poseidon(values: Array<bigint | string>): bigint {\n const felts = values.map((v) => (typeof v === \"bigint\" ? \"0x\" + v.toString(16) : v));\n return BigInt(hash.computePoseidonHashOnElements(felts));\n}\n\n/** Mirrors `src/signer/interface.cairo::owner_commitment` = poseidon(kind_tag, pubkey...). */\nexport function ownerCommitment(kindTag: bigint, pubkey: bigint[]): bigint {\n return poseidon([kindTag, ...pubkey]);\n}\n\n/**\n * Mirrors the V8.4 constructor's salt derivation:\n * salt = poseidon(primary_kind, primary_pubkey_hash)\n * where primary_pubkey_hash = owner_commitment(primary_kind, pubkey).\n * Binding kind into the salt ensures the same raw key under two\n * different kinds produces two distinct addresses.\n */\nexport function computeAddressSalt(primaryKind: string, pubkey: bigint[]): bigint {\n if (pubkey.length === 0) {\n // Fail-fast: an empty pubkey array would still produce a deterministic\n // (but unsignable) address. Reject up front so callers can't silently\n // ship a wallet that no signer can ever authorize.\n throw new Error(\n \"computeAddressSalt: pubkey must contain at least one felt — empty signer material is rejected to prevent silent address mismatch.\"\n );\n }\n const kindTag = BigInt(shortString.encodeShortString(primaryKind));\n const pubkeyHash = ownerCommitment(kindTag, pubkey);\n return poseidon([kindTag, pubkeyHash]);\n}\n\n/** Computes the deterministic deployment address for a SHHH ShhhAccount. */\nexport function computeShhhAddress(input: ComputeAddressInput): bigint {\n const {\n classHash,\n primaryKind,\n pubkey,\n verifierClassHash,\n label = \"primary\",\n deployerAddress = 0n,\n } = input;\n\n const salt = computeAddressSalt(primaryKind, pubkey);\n const kindTag = BigInt(shortString.encodeShortString(primaryKind));\n const labelFelt = BigInt(shortString.encodeShortString(label));\n\n // Constructor calldata: [primary_kind, primary_verifier, pubkey_len, pubkey..., label]\n const ctor: Array<bigint | string> = [\n kindTag,\n verifierClassHash,\n BigInt(pubkey.length),\n ...pubkey,\n labelFelt,\n ];\n\n const addressHex = hash.calculateContractAddressFromHash(\n \"0x\" + salt.toString(16),\n \"0x\" + classHash.toString(16),\n ctor.map((x) => (typeof x === \"bigint\" ? \"0x\" + x.toString(16) : x)),\n \"0x\" + deployerAddress.toString(16)\n );\n return BigInt(addressHex);\n}\n","/**\n * SNIP-12 typed-data hash for `OutsideExecution` — TypeScript reference.\n *\n * Vendored from haycarlitos/shhh-wallet-cairo @ v8.4 commit 3975532\n * source: scripts/ts/snip12-hash.ts\n * license: MIT (Copyright (c) 2026 Carlos Castillo and contributors)\n *\n * MUST produce byte-identical output to `compute_snip12_hash` in\n * `src/outside_execution.cairo` at the same commit. If the canonical\n * Cairo source ships a breaking change, the upstream Shhh maintainer\n * will open a PR against this file (per Q3 of SHHH_INTEGRATION_ANSWERS.md).\n */\n\nimport { hash, shortString, CallData, type Call, type RawArgs } from \"starknet\";\n\n// ================================================================\n// Constants — MUST match src/outside_execution.cairo exactly.\n// ================================================================\n\nexport const OUTSIDE_EXECUTION_TYPE_HASH_REV1 =\n 0x5a4b49e17039355cd95d1f0981d75901191d1319b1f4b05a9a791d218d7e0cn;\n\nexport const CALL_TYPE_HASH_REV1 =\n 0x3635c7f2a7ba93844c0d064e18e487f35ab90f7c39d00f186a781fc3f0c2ca9n;\n\nexport const STARKNET_DOMAIN_TYPE_HASH_REV1 =\n 0x1ff2f602e42168014d405a94f75e8a93d640751d71d16311266e140d8b0a210n;\n\nexport const STARKNET_MESSAGE_PREFIX = shortString.encodeShortString(\"StarkNet Message\");\n\nexport const OE_DOMAIN_NAME = shortString.encodeShortString(\"Account.execute_from_outside\");\nexport const OE_DOMAIN_VERSION = 2n;\nexport const OE_DOMAIN_REVISION = 1n;\n\nexport const SIG_VERSION_V1_HEX_ASCII = shortString.encodeShortString(\"V1_HEX_ASCII\");\nexport const SIG_VERSION_V2_SNIP12 = shortString.encodeShortString(\"V2_SNIP12\");\nexport const SIG_VERSION_V2_THRESHOLD = shortString.encodeShortString(\"V2_THRESHOLD\");\n\n// SRC-5 interface IDs — see Cairo `src/signer/interface.cairo::ISIGNER_ID`.\nexport const ISRC9_V2_ID = BigInt(\n \"0x1d1144bb2138366ff28d8e9ab57456b1d332ac42196230c3a602003c89872\"\n);\nexport const ISIGNER_ID = BigInt(\n \"0x94c5a761f34b25a4e603c651ac0e1fc4fad9cdb5517f7fa1bb54044c7e5ef8\"\n);\n\nexport const ISIGNER_CANONICAL_LABEL = \"ISigner_V1\";\n\n/** Recomputes the canonical `ISIGNER_ID` at runtime. */\nexport function computeISignerId(): bigint {\n return BigInt(hash.starknetKeccak(ISIGNER_CANONICAL_LABEL));\n}\n\n// ================================================================\n// Types\n// ================================================================\n\nexport type OutsideExecution = {\n caller: bigint | string; // ContractAddress (hex or bigint)\n nonce: bigint | string;\n execute_after: bigint; // u64\n execute_before: bigint; // u64\n calls: Call[];\n};\n\n// ================================================================\n// Hash functions\n// ================================================================\n\nfunction toBigInt(x: bigint | string | number): bigint {\n if (typeof x === \"bigint\") return x;\n if (typeof x === \"number\") return BigInt(x);\n return BigInt(x); // accepts \"0x...\" or decimal strings\n}\n\nfunction poseidonSpan(values: Array<bigint | string>): bigint {\n const felts = values.map((v) => (typeof v === \"bigint\" ? \"0x\" + v.toString(16) : v));\n return BigInt(hash.computePoseidonHashOnElements(felts));\n}\n\nexport function hashCall(call: Call): bigint {\n const calldataFelts: string[] = CallData.toHex(\n (Array.isArray(call.calldata) ? call.calldata : [call.calldata]) as RawArgs\n );\n const calldataItems = calldataFelts.map((x) => BigInt(x));\n const calldataHash = poseidonSpan(calldataItems);\n return poseidonSpan([\n CALL_TYPE_HASH_REV1,\n toBigInt(call.contractAddress),\n toBigInt(hash.getSelectorFromName(call.entrypoint)),\n calldataHash,\n ]);\n}\n\nexport function hashCalls(calls: Call[]): bigint {\n const callHashes = calls.map(hashCall);\n return poseidonSpan(callHashes);\n}\n\nexport function hashStarknetDomain(chainIdFelt: bigint | string): bigint {\n return poseidonSpan([\n STARKNET_DOMAIN_TYPE_HASH_REV1,\n BigInt(OE_DOMAIN_NAME),\n OE_DOMAIN_VERSION,\n toBigInt(chainIdFelt),\n OE_DOMAIN_REVISION,\n ]);\n}\n\nexport function hashOutsideExecutionStruct(oe: OutsideExecution): bigint {\n const callsArrayHash = hashCalls(oe.calls);\n return poseidonSpan([\n OUTSIDE_EXECUTION_TYPE_HASH_REV1,\n toBigInt(oe.caller),\n toBigInt(oe.nonce),\n oe.execute_after,\n oe.execute_before,\n callsArrayHash,\n ]);\n}\n\n/**\n * The authoritative SNIP-12 message hash for an OutsideExecution.\n * Signers sign over this felt252 (or its hex-ASCII re-encoding for\n * Phantom UX compatibility — see snip12HashToHexAsciiBytes).\n */\nexport function computeSnip12Hash(\n oe: OutsideExecution,\n contractAddress: bigint | string,\n chainIdFelt: bigint | string\n): bigint {\n const domainHash = hashStarknetDomain(chainIdFelt);\n const structHash = hashOutsideExecutionStruct(oe);\n return poseidonSpan([\n BigInt(STARKNET_MESSAGE_PREFIX),\n domainHash,\n toBigInt(contractAddress),\n structHash,\n ]);\n}\n\n/**\n * Phantom hex-ASCII envelope helper. Phantom signs a string; we give it\n * a 64-byte hex-ASCII representation of the SNIP-12 hash, and the Cairo\n * verifier re-computes the hash and compares byte-by-byte.\n */\nexport function snip12HashToHexAsciiBytes(h: bigint): Uint8Array {\n const hex = h.toString(16).padStart(64, \"0\"); // 32 bytes -> 64 ASCII chars\n return new TextEncoder().encode(hex);\n}\n","/**\n * SHHH V8.4 threshold envelope assembly + gas estimation.\n *\n * Wave B PR B2. The single-owner OE flow already ships via\n * `wrap_v2_snip12_envelope` in `oe.ts`. This module adds the\n * multi-owner threshold flow: N owners (possibly with mixed signer\n * kinds) co-sign a single OE, and the wallet's\n * `execute_from_outside_v2` walks the envelope to verify each owner\n * independently before checking total weight against the configured\n * threshold.\n *\n * Cairo dispatch shape (`src/account.cairo:418-460`):\n *\n * signature = [\n * V2_THRESHOLD, // tag\n * n, // u32, MUST be >= 2 and <= owner_count\n * env_1_len, // u32\n * ...env_1 (env_1_len felts),\n * env_2_len,\n * ...env_2,\n * ...\n * env_n_len,\n * ...env_n,\n * ]\n *\n * Each inner envelope is a V2_SNIP12 single-owner envelope\n * (`wrap_v2_snip12_envelope` output) from a different owner. Nested\n * threshold envelopes are NOT permitted — the Cairo `_verify_sub_envelope`\n * recursively checks the inner tag and rejects anything except\n * V2_SNIP12.\n *\n * Cairo-enforced invariants this module replicates client-side so\n * mis-shaped envelopes fail at the SDK boundary with a clear message\n * instead of with an opaque \"THRESH: …\" Starknet revert:\n *\n * - signature.len() >= 2\n * - n >= 2 (n=1 is invalid — use V2_SNIP12 directly for single-owner)\n * - each inner env_len >= 2\n * - no buffer overflow / truncation\n * - no trailing bytes after the last inner envelope\n * - no duplicate owner_id across inner envelopes (extracted from\n * envelope[1] of each inner)\n *\n * Total-weight vs threshold is enforced ON-CHAIN (the SDK can't know\n * the configured threshold without reading on-chain state). Use\n * `estimateThresholdGasOverhead` to predict paymaster fees.\n */\n\nimport { SHHH_KIND_GAS_OVERHEAD } from \"@chipi-stack/shared\";\nimport type { ShhhSignerKind } from \"@chipi-stack/types\";\nimport { shortString } from \"starknet\";\n\nimport { SIG_VERSION_V2_SNIP12, SIG_VERSION_V2_THRESHOLD } from \"./snip12-hash\";\n\n// ================================================================\n// Constants — encode-shortstring values matched against snip12-hash.ts\n// ================================================================\n\nconst V2_SNIP12_TAG = BigInt(SIG_VERSION_V2_SNIP12);\nconst V2_THRESHOLD_TAG = BigInt(SIG_VERSION_V2_THRESHOLD);\n\n/** Minimum number of inner envelopes in a threshold (Cairo enforces >= 2). */\nexport const MIN_THRESHOLD_OWNERS = 2;\n\n// ================================================================\n// Validation\n// ================================================================\n\n/**\n * Validate that an envelope is a well-formed V2_SNIP12 single-owner\n * envelope suitable for inclusion in a threshold envelope. Returns\n * the extracted `owner_id` (envelope[1]) so the caller can dedupe.\n *\n * Rejects nested threshold envelopes (`signature[0] == V2_THRESHOLD`)\n * — V8.4 does not allow nesting; the Cairo `_verify_sub_envelope`\n * would revert with `'SRC9: bad tag'`.\n */\nexport function assertValidV2Snip12Envelope(envelope: bigint[]): bigint {\n if (envelope.length < 2) {\n throw new Error(\n `THRESH: inner envelope must have at least 2 felts (tag + owner_id); got ${envelope.length}`\n );\n }\n const tag = envelope[0];\n if (tag !== V2_SNIP12_TAG) {\n if (tag === V2_THRESHOLD_TAG) {\n throw new Error(\n \"THRESH: nested threshold envelopes are not allowed; \" + \"inner envelopes must be V2_SNIP12\"\n );\n }\n const decoded = _tryDecodeShortString(tag);\n throw new Error(\n `THRESH: inner envelope tag must be V2_SNIP12; got ${decoded ?? \"0x\" + tag.toString(16)}`\n );\n }\n return envelope[1];\n}\n\nfunction _tryDecodeShortString(felt: bigint): string | null {\n try {\n return shortString.decodeShortString(\"0x\" + felt.toString(16));\n } catch {\n return null;\n }\n}\n\n// ================================================================\n// Assembly\n// ================================================================\n\n/**\n * Build a V2_THRESHOLD envelope from a list of pre-signed V2_SNIP12\n * inner envelopes. Each inner envelope must come from a DIFFERENT\n * owner — duplicates fail loudly here instead of on-chain.\n *\n * The typical UI flow:\n * 1. Compute the OE message hash once (computeOeHash from oe.ts)\n * 2. Distribute the hash to N owners (push notification, QR, etc.)\n * 3. Each owner signs with their kind's envelope builder and\n * returns the resulting `bigint[]`\n * 4. UI collects the envelopes and calls this function\n * 5. UI submits the resulting threshold envelope as the\n * `signature` field of `paymaster_executeSponsoredRaw.calldata`\n *\n * `buildThresholdEnvelope` does NOT submit anything. It returns the\n * felt span that goes into the OE calldata.\n */\nexport function buildThresholdEnvelope(args: { innerEnvelopes: bigint[][] }): bigint[] {\n const { innerEnvelopes } = args;\n\n if (innerEnvelopes.length < MIN_THRESHOLD_OWNERS) {\n throw new Error(\n `THRESH: need at least ${MIN_THRESHOLD_OWNERS} inner envelopes; ` +\n `got ${innerEnvelopes.length}. Use wrap_v2_snip12_envelope ` +\n `for single-owner OEs.`\n );\n }\n\n // Single pass: validate, dedup owner_id, append interleaved\n // [env_len, ...env_data] per inner envelope. Layout matches Cairo\n // `src/account.cairo:418-454` (cursor reads env_len, then env_data,\n // then advances) — NOT the grouped [...lens, ...flat] shape an\n // earlier latent `wrapV2ThresholdEnvelope` produced. That function\n // was never called in production; removed in this PR.\n const seenOwners = new Set<string>();\n const out: bigint[] = [V2_THRESHOLD_TAG, BigInt(innerEnvelopes.length)];\n\n for (let i = 0; i < innerEnvelopes.length; i++) {\n const env = innerEnvelopes[i];\n const ownerId = assertValidV2Snip12Envelope(env);\n const key = ownerId.toString();\n if (seenOwners.has(key)) {\n throw new Error(\n `THRESH: duplicate owner_id ${\"0x\" + ownerId.toString(16)} at inner envelope ${i}`\n );\n }\n seenOwners.add(key);\n out.push(BigInt(env.length));\n for (const felt of env) {\n if (felt < 0n) {\n throw new RangeError(\n `THRESH: envelope ${i} contains negative felt (cannot serialize as felt252)`\n );\n }\n out.push(felt);\n }\n }\n\n return out;\n}\n\n// ================================================================\n// Parsing\n// ================================================================\n\n/**\n * Split a V2_THRESHOLD envelope back into its constituent inner\n * V2_SNIP12 envelopes. Inverse of `buildThresholdEnvelope`.\n *\n * Useful for UI flows that need to display \"N of M signers signed\n * so far\" status without re-deriving from individual envelopes. Also\n * useful in tests for round-trip validation.\n *\n * Throws if the envelope is malformed (tag mismatch, n < 2, length\n * mismatch, trailing bytes). Mirrors the Cairo dispatch's structural\n * checks (`src/account.cairo:418-456`) so the same envelope that\n * would parse-fail here would also revert on-chain.\n */\nexport function parseThresholdEnvelope(envelope: bigint[]): {\n innerEnvelopes: bigint[][];\n} {\n if (envelope.length < 2) {\n throw new Error(`THRESH: envelope too short (need at least [tag, n]); got ${envelope.length}`);\n }\n if (envelope[0] !== V2_THRESHOLD_TAG) {\n throw new Error(`THRESH: envelope tag must be V2_THRESHOLD; got 0x${envelope[0].toString(16)}`);\n }\n const n = Number(envelope[1]);\n if (!Number.isInteger(n) || n < MIN_THRESHOLD_OWNERS) {\n throw new Error(`THRESH: n must be an integer >= ${MIN_THRESHOLD_OWNERS}; got ${envelope[1]}`);\n }\n\n // Re-validate each inner envelope while parsing so a framed-but-\n // malformed envelope (nested V2_THRESHOLD, duplicate owner_id,\n // negative felts) fails here with the same error as the builder\n // would have raised. Keeps parse / build symmetric.\n const seenOwners = new Set<string>();\n const innerEnvelopes: bigint[][] = [];\n let cursor = 2;\n for (let i = 0; i < n; i++) {\n if (cursor >= envelope.length) {\n throw new Error(`THRESH: envelope truncated at inner ${i} (no length felt)`);\n }\n const envLen = Number(envelope[cursor]);\n cursor += 1;\n if (!Number.isInteger(envLen) || envLen < 2) {\n throw new Error(`THRESH: inner ${i} env_len must be >= 2; got ${envLen}`);\n }\n if (cursor + envLen > envelope.length) {\n throw new Error(\n `THRESH: inner ${i} would overflow envelope (env_len ${envLen}, ` +\n `cursor ${cursor}, total ${envelope.length})`\n );\n }\n const inner = envelope.slice(cursor, cursor + envLen);\n for (const felt of inner) {\n if (felt < 0n) {\n throw new RangeError(\n `THRESH: inner ${i} contains negative felt (cannot serialize as felt252)`\n );\n }\n }\n const ownerId = assertValidV2Snip12Envelope(inner);\n const key = ownerId.toString();\n if (seenOwners.has(key)) {\n throw new Error(\n `THRESH: duplicate owner_id ${\"0x\" + ownerId.toString(16)} at inner envelope ${i}`\n );\n }\n seenOwners.add(key);\n innerEnvelopes.push(inner);\n cursor += envLen;\n }\n if (cursor !== envelope.length) {\n throw new Error(\n `THRESH: trailing bytes after inner ${n - 1} (cursor ${cursor}, ` +\n `length ${envelope.length})`\n );\n }\n return { innerEnvelopes };\n}\n\n// ================================================================\n// Gas estimation\n// ================================================================\n\n/**\n * Predict the on-chain L2 gas overhead a threshold envelope will\n * consume. Each inner envelope dispatches to its kind's verifier\n * class via `library_call`, so total overhead is the sum of\n * per-kind overheads.\n *\n * Returned value is the same unit as `SHHH_KIND_GAS_OVERHEAD` in\n * `@chipi-stack/shared` (raw L2 gas units). Use to predict paymaster\n * fees:\n * estimatedFee = baselineOEGas + estimateThresholdGasOverhead(owners)\n *\n * Does NOT include the wallet's threshold-validation loop overhead\n * (the deduplication walk and weight summation) — that's a constant\n * ~50K L2 gas regardless of N, and the paymaster's fee predictor\n * already accounts for it.\n */\nexport function estimateThresholdGasOverhead(\n owners: Array<{ signerKind: ShhhSignerKind }>\n): number {\n if (owners.length < MIN_THRESHOLD_OWNERS) {\n throw new Error(\n `THRESH: need at least ${MIN_THRESHOLD_OWNERS} owners to estimate ` +\n `threshold gas; got ${owners.length}`\n );\n }\n let total = 0;\n for (const { signerKind } of owners) {\n const overhead = SHHH_KIND_GAS_OVERHEAD[signerKind];\n if (overhead === undefined) {\n throw new Error(\n `THRESH: unknown signer kind ${signerKind} — missing from ` + `SHHH_KIND_GAS_OVERHEAD`\n );\n }\n total += overhead;\n }\n return total;\n}\n","/**\n * Outside-Execution construction for SHHH V8.4 ShhhAccount.\n *\n * Wraps the canonical SNIP-12 hash + envelope assembly so SDK callers\n * never touch the underlying felt-array shapes directly. Implements\n * the envelope routing per V8.4 handover §5:\n *\n * [ signature[0] ]\n * ├─ 'V2_SNIP12' → [owner_id, kind_tag, ...payload] (single-owner)\n * ├─ 'V2_THRESHOLD' → [n, env_1_len, ..., env_n_len, ...] (multisig)\n * └─ (else, len==4) → SNIP-163 session-key envelope\n *\n * Source of truth: `src/account.cairo::execute_from_outside_v2` at\n * shhh-wallet-cairo v8.4 commit 3975532.\n */\n\nimport { shortString, type Call } from \"starknet\";\nimport {\n computeSnip12Hash,\n SIG_VERSION_V2_SNIP12,\n SIG_VERSION_V2_THRESHOLD,\n type OutsideExecution,\n} from \"./snip12-hash\";\n\n/**\n * The `caller` sentinel used for paymaster-sponsored OE flows. Any\n * forwarder may submit the OE; binding `caller = ANY_CALLER` lifts the\n * usual caller-equality check inside the account.\n */\nexport const ANY_CALLER_FELT = BigInt(shortString.encodeShortString(\"ANY_CALLER\"));\n\n/**\n * Per audit M-2, OE validity windows must not exceed 2 hours. The\n * V8.4 account enforces this on-chain; we expose the constant so SDK\n * callers can default sensibly.\n */\nexport const MAX_OE_VALIDITY_WINDOW_SECONDS = 2 * 60 * 60;\n\n/**\n * Clock-skew buffer (seconds) subtracted from `now` when defaulting\n * the OE's `execute_after`. The wallet's check at\n * `sessions-smart-contract/src/account.cairo:377` (and the matching\n * V8.4 line in `shhh-wallet-cairo/src/account.cairo`) is strict\n * `assert(execute_after < now, 'SRC9: now <= execute_after')`. If\n * the OE is built and submitted within the same second the\n * sequencer mints the next block, `execute_after == now` and the\n * wallet panics with `'SRC9: too early'`, which CHIPI v29's\n * `_execute_calls` then swallows silently. 60s is well within the\n * audit-M-2 window (2h max) and well above any realistic SDK ↔\n * sequencer clock drift.\n */\nexport const OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER = 60;\n\n/**\n * Build a standard paymaster-sponsored OutsideExecution with sensible\n * defaults. Callers can override any field; the typical happy path is\n * to pass only `nonce` + `calls`.\n */\nexport function buildOutsideExecution(params: {\n /** Unique per-account replay nonce. The account stores these in `oe_nonces`. */\n nonce: bigint | string;\n /** The calls the account should execute on success. */\n calls: Call[];\n /** Override `caller`; defaults to ANY_CALLER for paymaster flows. */\n caller?: bigint | string;\n /**\n * UNIX seconds. Defaults to `now - OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER`\n * (60s in the past) so the OE is valid by the time the next block is\n * minted regardless of small clock drifts between the SDK host and\n * the sequencer. The wallet's check is strict `<` (`account.cairo:377`\n * `'SRC9: now <= execute_after'`) — without the buffer, a fast\n * SDK→paymaster round trip can land in the SAME block as the OE\n * was built in and revert with `'SRC9: too early'`. PR sdks#289\n * (this builder fix) made the buffer the default after the first\n * session smoke run reproduced the race.\n */\n executeAfter?: bigint;\n /** UNIX seconds; defaults to now + 2h (the maximum window per audit M-2). */\n executeBefore?: bigint;\n}): OutsideExecution {\n const nowSec = BigInt(Math.floor(Date.now() / 1000));\n const after = params.executeAfter ?? nowSec - BigInt(OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER);\n // When defaulting both endpoints, keep `before - after` exactly at\n // the audit-M-2 cap. Pulling `after` back by the clock-skew buffer\n // without pulling `before` back by the same amount would push the\n // span over MAX_OE_VALIDITY_WINDOW_SECONDS and trip the validator\n // below. The explicit-override branches keep their independent\n // values (caller still gets the M-2 check).\n const before =\n params.executeBefore ??\n nowSec + BigInt(MAX_OE_VALIDITY_WINDOW_SECONDS) - BigInt(OE_EXECUTE_AFTER_CLOCK_SKEW_BUFFER);\n if (before <= after) {\n // An inverted or zero-length window passes the max-span check but produces\n // an OE that can never be valid on-chain. Reject up front.\n throw new Error(\n `executeBefore (${before}) must be strictly greater than executeAfter (${after}).`\n );\n }\n if (before - after > BigInt(MAX_OE_VALIDITY_WINDOW_SECONDS)) {\n throw new Error(\n `OE validity window ${before - after}s exceeds max ${MAX_OE_VALIDITY_WINDOW_SECONDS}s (audit M-2). Adjust executeBefore.`\n );\n }\n return {\n caller: params.caller ?? ANY_CALLER_FELT,\n nonce: params.nonce,\n execute_after: after,\n execute_before: before,\n calls: params.calls,\n };\n}\n\n/**\n * Compute the SNIP-12 hash for the given OutsideExecution against\n * a specific account address and chain. This is what the signer\n * (STARK / Ed25519 / EIP-191 / WebAuthn / etc.) commits to.\n *\n * For Phantom UX where the user signs a string, hex-encode the\n * resulting hash via `snip12HashToHexAsciiBytes` from snip12-hash.\n */\nexport function computeOeHash(\n oe: OutsideExecution,\n accountAddress: bigint | string,\n chainId: bigint | string\n): bigint {\n return computeSnip12Hash(oe, accountAddress, chainId);\n}\n\n// ================================================================\n// Envelope assembly\n// ================================================================\n\n/**\n * Single-owner V2_SNIP12 envelope.\n *\n * The account strips signature[0..2] = ['V2_SNIP12', owner_id, kind_tag]\n * and library_calls the matching verifier with `payload`. Chipi\n * paymaster passes the entire envelope verbatim to\n * `execute_from_outside_v2`; it does NOT need to know the kind.\n *\n * **`ownerId` is a u32 INDEX into the wallet's owner_set**, NOT the\n * poseidon `owner_commitment`. Primary owner = `0n`; subsequent owners\n * added via `propose_add_owner` get `1n`, `2n`, etc. The V8.4\n * dispatcher (`src/account.cairo:480`) panics with `'SHHH: bad\n * owner_id'` if signature[1] does not fit in u32 — which happens\n * silently at the wallet level (outer paymaster tx still reports\n * SUCCEEDED). Discovered 2026-05-27 in a production trace audit.\n *\n * Returns the `signature: felt252[]` span you pass to\n * `paymaster_executeSponsoredRaw.calldata`.\n */\nexport function wrapV2Snip12Envelope(params: {\n /** u32 INDEX into owner_set (0n for primary). NOT the commitment. */\n ownerId: bigint;\n kindTag: string | bigint;\n payload: Array<bigint | string>;\n}): bigint[] {\n const tag =\n typeof params.kindTag === \"bigint\"\n ? params.kindTag\n : BigInt(shortString.encodeShortString(params.kindTag));\n return [\n BigInt(SIG_VERSION_V2_SNIP12),\n params.ownerId,\n tag,\n ...params.payload.map((v) => (typeof v === \"bigint\" ? v : BigInt(v))),\n ];\n}\n\n// Threshold envelope (V2_THRESHOLD): see `./threshold.ts:buildThresholdEnvelope`\n// for the canonical builder with validation + deduplication + Cairo-\n// matching interleaved layout.\n//\n// `wrapV2ThresholdEnvelope` below is kept as a deprecated compat shim\n// for v13.x integrators who imported it directly. Wave A shipped this\n// function with the WRONG layout (grouped lens before data, instead\n// of interleaved per Cairo dispatch); it was never called in\n// production. The shim now delegates to the correct builder so any\n// existing call sites get the fixed shape with no SDK API break.\n\nimport { buildThresholdEnvelope } from \"./threshold\";\n\n/**\n * @deprecated since Wave B B2 — use `buildThresholdEnvelope` from\n * `@chipi-stack/backend/shhh/threshold` instead. This wrapper exists\n * for backward compat with v13.x and will be removed one minor\n * release after `chipi-stack@14.5.0` ships.\n *\n * Forwards to the validated builder. Note that callers passing\n * fewer than 2 envelopes (which the old version silently accepted\n * with a bogus layout) will now correctly throw — Cairo rejects\n * `n < 2` thresholds.\n */\nexport function wrapV2ThresholdEnvelope(innerEnvelopes: bigint[][]): bigint[] {\n return buildThresholdEnvelope({ innerEnvelopes });\n}\n","/**\n * Ed25519 signer for SHHH V8.4 ShhhAccount.\n *\n * Used by Phantom (Solana) flows and any generic Ed25519 signer. The\n * V8.4 verifier (class `0x030a7dfc…949f74`) consumes a Cairo-ready\n * calldata array produced by Garaga's `eddsaCalldataBuilder` — it\n * contains the signature halves plus elliptic-curve hints the Cairo\n * verifier needs to keep gas bounded.\n *\n * Reference: shhh-wallet-cairo/scripts/ts/regen-ed25519-fixtures.mjs\n *\n * Verifier overhead: ~33M l2_gas (per SHHH_KIND_GAS_OVERHEAD).\n *\n * IMPORTANT — dynamic imports\n *\n * Garaga ships a ~1.2MB WASM bundle. We dynamic-import it so callers\n * who never touch Ed25519 don't pay the bundle cost. The first call\n * to `buildEd25519Envelope` will lazy-load + initialize Garaga; this\n * is irreversible and cached for the process lifetime. Cold-start\n * cost is ~200-400ms on Android, ~80-150ms on iPhone 13+.\n *\n * `@noble/ed25519` is also dynamic-imported for symmetry, though it\n * tree-shakes cleanly as a static import too.\n */\n\nimport { shortString } from \"starknet\";\nimport { ownerCommitment } from \"../compute-wallet-address\";\nimport { wrapV2Snip12Envelope } from \"../oe\";\nimport { snip12HashToHexAsciiBytes } from \"../snip12-hash\";\n\nconst ED25519_KIND_TAG = \"ED25519\";\nconst ED25519_KIND_TAG_FELT = BigInt(shortString.encodeShortString(ED25519_KIND_TAG));\n\nlet garagaInitPromise: Promise<typeof import(\"garaga\")> | null = null;\n\n/**\n * Lazy + memoized Garaga WASM init. Safe to call repeatedly.\n *\n * On failure (e.g. transient WASM fetch error) we clear the memoized\n * promise so subsequent calls retry; otherwise a single bad init would\n * brick the SDK for the process lifetime.\n */\nasync function ensureGaragaInit(): Promise<typeof import(\"garaga\")> {\n if (garagaInitPromise) return garagaInitPromise;\n const attempt = (async () => {\n const garaga = await import(\"garaga\");\n await garaga.init();\n return garaga;\n })();\n garagaInitPromise = attempt;\n try {\n return await attempt;\n } catch (err) {\n // Reset so the next caller gets a fresh attempt instead of the\n // cached rejection.\n if (garagaInitPromise === attempt) garagaInitPromise = null;\n throw err;\n }\n}\n\n/**\n * Split a 32-byte little-endian Ed25519 value into two felt252-sized\n * halves matching Cairo's `u256` storage layout (low_u128, high_u128).\n * bytes[0] is the LSB of `low`.\n */\nfunction bytesToLeU256(bytes: Uint8Array): { low: bigint; high: bigint } {\n if (bytes.length !== 32) {\n throw new Error(`Ed25519 value must be exactly 32 bytes; got ${bytes.length}.`);\n }\n let low = 0n;\n let high = 0n;\n for (let i = 0; i < 16; i++) low += BigInt(bytes[i]) << BigInt(8 * i);\n for (let i = 0; i < 16; i++) high += BigInt(bytes[16 + i]) << BigInt(8 * i);\n return { low, high };\n}\n\nfunction bytesToLeBigInt(bytes: Uint8Array): bigint {\n let x = 0n;\n for (let i = bytes.length - 1; i >= 0; i--) x = (x << 8n) | BigInt(bytes[i]);\n return x;\n}\n\n/**\n * The Cairo Ed25519 verifier reads the SNIP-12 hash via its hex-ASCII\n * 64-byte encoding so the signature commits to a string that Phantom\n * (and any \"sign-this-string\" UX) can display. This helper is exposed\n * here for parity with the Cairo-side decoder; it's the same function\n * as `snip12HashToHexAsciiBytes` in snip12-hash.ts, re-exported under\n * a kind-specific name for discoverability.\n */\nexport function ed25519SignedBytes(messageHash: bigint): Uint8Array {\n return snip12HashToHexAsciiBytes(messageHash);\n}\n\n/**\n * Compute the V8.4 owner_id for an Ed25519-kind owner from its public\n * key. owner_id = owner_commitment(ED25519_tag, [pub_low, pub_high])\n * — the same shape as `src/signer/ed25519/verifier.cairo` expects in\n * `validate_pubkey`.\n */\nexport function ed25519OwnerId(publicKey: Uint8Array): bigint {\n const { low, high } = bytesToLeU256(publicKey);\n return ownerCommitment(ED25519_KIND_TAG_FELT, [low, high]);\n}\n\n/**\n * Ed25519 pubkey felt layout for the V8.4 constructor / propose_add_owner.\n * Returns the low/high u128 halves of the 32-byte public key in that\n * order — what the Cairo `kind == ED25519` branch reads.\n */\nexport function ed25519PubkeyFelts(publicKey: Uint8Array): bigint[] {\n const { low, high } = bytesToLeU256(publicKey);\n return [low, high];\n}\n\n/**\n * Build a V2_SNIP12 envelope for the Ed25519 kind. Async because the\n * underlying Garaga WASM must be initialized; subsequent calls reuse\n * the cached init.\n *\n * Inputs:\n * privateKey — 32-byte Ed25519 secret (Uint8Array, NOT hex/bigint:\n * the noble lib refuses anything else).\n * messageHash — the SNIP-12 hash of the OE (see oe.ts::computeOeHash).\n *\n * Output: the `signature` span you pass to\n * `paymaster_executeSponsoredRaw.calldata`.\n */\nexport async function buildEd25519Envelope(params: {\n privateKey: Uint8Array;\n messageHash: bigint;\n /**\n * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**\n * Override for additional owners added via `propose_add_owner`.\n * MUST fit in u32 (i.e., < 2^32). Passing the poseidon\n * `owner_commitment` (e.g. via `ed25519OwnerId`) here will revert\n * on chain with `'SHHH: bad owner_id'` — the bug Shhh team caught\n * 2026-05-27 + audit-trail 2026-05-28.\n */\n ownerIndex?: bigint;\n}): Promise<bigint[]> {\n // Validate cheap params BEFORE Garaga init + signing. An out-of-range\n // ownerIndex should not pay for the ~80-400ms WASM warm-up or an ECDSA\n // sign that we will discard.\n const ownerIndex = params.ownerIndex ?? 0n;\n if (ownerIndex < 0n || ownerIndex >= 0x100000000n) {\n throw new Error(\n `buildEd25519Envelope: ownerIndex ${ownerIndex} is out of u32 range [0, 2^32). ` +\n \"owner_id is the u32 INDEX into the wallet's owner_set (0 for primary, \" +\n \"1 for second, etc.); do NOT pass an owner_commitment or pubkey here.\"\n );\n }\n if (params.privateKey.length !== 32) {\n throw new Error(\n `Ed25519 private key must be exactly 32 bytes; got ${params.privateKey.length}.`\n );\n }\n\n // @noble/ed25519 ≥ 3.x is async-by-default and ships as ESM.\n const ed = await import(\"@noble/ed25519\");\n\n const garaga = await ensureGaragaInit();\n\n const publicKey = await ed.getPublicKeyAsync(params.privateKey);\n const signedBytes = ed25519SignedBytes(params.messageHash);\n const signature = await ed.signAsync(signedBytes, params.privateKey);\n\n // Per RFC 8032: signature = R (32B) || s (32B).\n const R = signature.slice(0, 32);\n const s = signature.slice(32, 64);\n\n // Garaga's eddsaCalldataBuilder(Ry_le_bigint, s_le_bigint, Py_le_bigint, msg, prependPublickey=false)\n // returns the calldata array Cairo's Ed25519 verifier consumes:\n // [ Ry_low, Ry_high, s_low, s_high,\n // msg_len, msg_byte_0, ..., msg_byte_{msg_len-1},\n // ...msm_hint..., ...sqrt_Rx_hint..., ...sqrt_Px_hint... ]\n const payload = (garaga as any).eddsaCalldataBuilder(\n bytesToLeBigInt(R),\n bytesToLeBigInt(s),\n bytesToLeBigInt(publicKey),\n signedBytes,\n false\n );\n\n return wrapV2Snip12Envelope({\n ownerId: ownerIndex,\n kindTag: ED25519_KIND_TAG,\n payload: payload as Array<bigint | string>,\n });\n}\n","import CryptoES from \"crypto-es\";\n\nexport const encryptPrivateKey = (privateKey: string, password: string): string => {\n if (!privateKey || !password) {\n throw new Error(\"Private key and password are required\");\n }\n\n return CryptoES.AES.encrypt(privateKey, password).toString();\n};\n\nexport const decryptPrivateKey = (encryptedPrivateKey: string, password: string): string => {\n if (!encryptedPrivateKey || !password) {\n throw new Error(\"Encrypted private key and password are required\");\n }\n\n try {\n const bytes = CryptoES.AES.decrypt(encryptedPrivateKey, password);\n const decrypted = bytes.toString(CryptoES.enc.Utf8);\n if (!decrypted) {\n throw new Error(\"Decryption resulted in empty string\");\n }\n return decrypted;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n }\n throw new Error(`Decryption failed: ${error}`);\n }\n};\n","export { encryptPrivateKey, decryptPrivateKey } from \"./lib/encryption\";\n","/**\n * STARK ECDSA signer for SHHH V8.4 ShhhAccount.\n *\n * Cheapest verifier path (~2M l2_gas). Native Stark-curve ECDSA over\n * the SNIP-12 hash; the verifier class is a thin syscall wrapper.\n *\n * Reference: shhh-wallet-cairo/scripts/ts/gen-phase3-account-fixture.mjs\n * (same Stark signature shape, wrapped in V2_SNIP12).\n *\n * For non-STARK kinds (Ed25519, EIP-191, WebAuthn, JWT, BLS) the\n * envelope payload is per-kind — see sibling files in this directory.\n */\n\nimport { ec, shortString } from \"starknet\";\nimport { ownerCommitment } from \"../compute-wallet-address\";\nimport { wrapV2Snip12Envelope } from \"../oe\";\n\nconst STARK_KIND_TAG = \"STARK\";\nconst STARK_KIND_TAG_FELT = BigInt(shortString.encodeShortString(STARK_KIND_TAG));\n\n/**\n * Build a V2_SNIP12 envelope for the STARK kind from a private key +\n * the OE's SNIP-12 hash. The envelope is the `signature` span you\n * pass to `paymaster_executeSponsoredRaw.calldata`.\n *\n * Payload for STARK is exactly `[r, s]` — the verifier reads these\n * two felts and invokes the chain's `ec_op` syscall.\n *\n * **Envelope layout (V8.4):** `[V2_SNIP12, owner_id, kind_tag, ...payload]`\n *\n * `owner_id` at position [1] is a **u32 INDEX into the owner_set**,\n * NOT the poseidon `owner_commitment`. For the primary owner of a\n * fresh deploy that index is `0n`; for additional owners added via\n * `propose_add_owner` it's `1n`, `2n`, etc. The V8.4 dispatcher\n * (`src/account.cairo:480`) does `(*signature.at(1)).try_into::<u32>()`\n * and panics with `'SHHH: bad owner_id'` if the felt exceeds u32 MAX —\n * which is exactly what happens if a poseidon commitment is passed\n * here instead of the index. (Discovered 2026-05-27 trace audit on\n * tx 0x1d1ee02e0dd1ffdbc6f0dddb426098d0dc87800eee3d0d0201e74d97ddc0a55.)\n */\nexport function buildStarkEnvelope(params: {\n /** STARK private key as bigint or 0x-prefixed hex. */\n privateKey: bigint | string;\n /** SNIP-12 hash of the OutsideExecution — see oe.ts::computeOeHash. */\n messageHash: bigint;\n /**\n * Owner index in the wallet's owner_set. **Defaults to 0n (primary).**\n * Override for additional owners added via `propose_add_owner`.\n * MUST fit in u32 (i.e., < 2^32). Passing the poseidon\n * `owner_commitment` here will revert on chain with\n * `'SHHH: bad owner_id'`.\n */\n ownerIndex?: bigint;\n}): bigint[] {\n // starknet.js getStarkKey/sign accept hex strings, not raw bigints.\n // Normalize to 0x-prefixed 64-char hex regardless of input form.\n const pkHex =\n \"0x\" +\n (typeof params.privateKey === \"bigint\" ? params.privateKey : BigInt(params.privateKey))\n .toString(16)\n .padStart(64, \"0\");\n\n const sig = ec.starkCurve.sign(\"0x\" + params.messageHash.toString(16), pkHex);\n\n const r = BigInt(\"0x\" + sig.r.toString(16));\n const s = BigInt(\"0x\" + sig.s.toString(16));\n\n const ownerIndex = params.ownerIndex ?? 0n;\n if (ownerIndex < 0n || ownerIndex >= 0x100000000n) {\n // u32 range check matches the on-chain try_into bound; surface as\n // a loud SDK-side error instead of a silent on-chain revert.\n throw new Error(\n `buildStarkEnvelope: ownerIndex ${ownerIndex} is out of u32 range [0, 2^32). ` +\n \"owner_id is the u32 INDEX into the wallet's owner_set (0 for primary, \" +\n \"1 for second, etc.); do NOT pass an owner_commitment or pubkey here.\"\n );\n }\n\n return wrapV2Snip12Envelope({\n ownerId: ownerIndex,\n kindTag: STARK_KIND_TAG,\n payload: [r, s],\n });\n}\n\n/**\n * Compute the V8.4 owner_id for a STARK-kind owner from its public\n * key. owner_id = owner_commitment(kind_tag, [pubkey]) per\n * `src/signer/interface.cairo::owner_commitment`. The account uses\n * this to dedupe owners and select the verifier class.\n */\nexport function starkOwnerId(starkPubKey: bigint): bigint {\n return ownerCommitment(STARK_KIND_TAG_FELT, [starkPubKey]);\n}\n\n/**\n * STARK pubkey layout for the V8.4 constructor / propose_add_owner.\n * The Stark curve pubkey fits in one felt252, so this is a 1-element\n * array. Kept as a function for parity with kinds that split the\n * pubkey into multiple felts (Ed25519, secp256k1, P-256, BLS).\n */\nexport function starkPubkeyFelts(starkPubKey: bigint | string): bigint[] {\n return [typeof starkPubKey === \"bigint\" ? starkPubKey : BigInt(starkPubKey)];\n}\n","/**\n * High-level SHHH V8.4 OE execution path for the backend SDK.\n *\n * This module is the glue between the low-level OE builders in\n * `oe.ts` + `signers/*.ts` and chipi-back's\n * `POST /transactions/execute-sponsored-raw` endpoint.\n *\n * Why this exists separately from the legacy\n * `executePaymasterTransaction` flow:\n *\n * - The Chipi paymaster cannot synthesize SHHH envelopes server-side\n * (each of the 10 V8.4 signer kinds has a different envelope\n * shape; the paymaster's design — see chipi-pay/paymaster#16 — is\n * to pass the signature span verbatim to `execute_from_outside_v2`).\n * - Therefore SHHH clients MUST build + sign the OE locally and\n * submit it pre-signed to a forwarding endpoint\n * (`/transactions/execute-sponsored-raw`).\n * - The legacy flow (`prepare-typed-data` → sign → `execute-sponsored-\n * transaction`) is unchanged and continues to serve CHIPI / READY\n * wallets.\n *\n * Scope (Wave B, this PR): STARK + ED25519 signer kinds.\n * EIP-191 / WEBAUTHN_P256 / JWT_ES256_APPLE_SUB still throw the\n * deferred-feature error until the external-rooted wallet adapter\n * flow lands.\n */\n\nimport { hash, type Call } from \"starknet\";\nimport { decryptPrivateKey } from \"../encryption\";\nimport type { ChipiClient } from \"../client\";\nimport { buildOutsideExecution, computeOeHash, ANY_CALLER_FELT } from \"./oe\";\nimport { buildStarkEnvelope } from \"./signers/stark\";\nimport { buildEd25519Envelope } from \"./signers/ed25519\";\n\n// Default chain ID for SHHH OE signing. Chipi Pay operates exclusively\n// on Starknet mainnet per CLAUDE.md (\"ALWAYS mainnet — Chipi Pay\n// operates exclusively on Starknet mainnet. Staging, tests, and\n// production all use mainnet\"). Callers on non-mainnet environments\n// can override via the `chainId` param on `executeShhhSponsoredRaw`.\n// Drift here = SNIP-12 signature mismatch and on-chain rejection.\nconst SN_MAIN_CHAIN_ID = \"0x534e5f4d41494e\"; // shortString.encodeShortString(\"SN_MAIN\")\nconst SN_SEPOLIA_CHAIN_ID = \"0x534e5f5345504f4c4941\"; // shortString.encodeShortString(\"SN_SEPOLIA\")\n\n/**\n * Pick the chain ID for SNIP-12 hashing. If the caller did not provide\n * an explicit override, infer from the chipi-back base URL (sepolia\n * hostnames map to SN_SEPOLIA; everything else maps to SN_MAIN per\n * the project's mainnet-only invariant).\n *\n * Exported for unit-testing the resolver in isolation.\n */\nexport function resolveShhhChainId(params: {\n override?: bigint | string;\n baseUrl?: string;\n}): string {\n if (params.override !== undefined) {\n return typeof params.override === \"bigint\"\n ? \"0x\" + params.override.toString(16)\n : params.override;\n }\n const url = (params.baseUrl ?? \"\").toLowerCase();\n if (url.includes(\"sepolia\")) return SN_SEPOLIA_CHAIN_ID;\n return SN_MAIN_CHAIN_ID;\n}\n\n/**\n * Serialize an OutsideExecution struct + signature span into the\n * flat felt-array calldata that `execute_from_outside_v2` expects.\n *\n * Cairo's serde order (per shhh-wallet-cairo v8.4\n * `src/account.cairo::execute_from_outside_v2`):\n *\n * caller\n * nonce\n * execute_after (u64, 1 felt)\n * execute_before (u64, 1 felt)\n * calls.len()\n * for each Call:\n * to (felt)\n * selector (felt)\n * calldata.len()\n * ...calldata felts\n * signature.len()\n * ...signature felts\n *\n * Returns 0x-prefixed lowercase hex strings ready for direct\n * JSON-RPC submission via the chipi-back forwarder.\n */\nexport function serializeExecuteFromOutsideV2Calldata(params: {\n outsideExecution: {\n caller: bigint | string;\n nonce: bigint | string;\n execute_after: bigint;\n execute_before: bigint;\n calls: Call[];\n };\n signature: bigint[];\n}): string[] {\n const out: bigint[] = [];\n const oe = params.outsideExecution;\n\n const toBig = (v: bigint | string): bigint => (typeof v === \"bigint\" ? v : BigInt(v));\n\n out.push(toBig(oe.caller));\n out.push(toBig(oe.nonce));\n out.push(oe.execute_after);\n out.push(oe.execute_before);\n\n out.push(BigInt(oe.calls.length));\n for (const call of oe.calls) {\n out.push(BigInt(call.contractAddress));\n out.push(BigInt(hash.getSelectorFromName(call.entrypoint)));\n const callData = Array.isArray(call.calldata)\n ? (call.calldata as Array<string | number | bigint>)\n : [];\n out.push(BigInt(callData.length));\n for (const datum of callData) {\n out.push(typeof datum === \"bigint\" ? datum : BigInt(datum));\n }\n }\n\n out.push(BigInt(params.signature.length));\n for (const sigFelt of params.signature) {\n out.push(sigFelt);\n }\n\n return out.map((v) => \"0x\" + v.toString(16));\n}\n\n/**\n * Generate a unique OE nonce. The V8.4 account stores consumed nonces\n * in `oe_nonces` and rejects replays, so we need something with enough\n * entropy to avoid collisions across concurrent SDK calls. Combines\n * `Date.now()` (ms) with a 24-bit random suffix; both fit comfortably\n * in a felt252.\n */\nfunction newOeNonce(): bigint {\n const tsMs = BigInt(Date.now());\n const rand = BigInt(Math.floor(Math.random() * 0xffffff));\n // Shift the timestamp left by 24 bits and OR the random suffix to\n // produce a single felt-sized value. ~85 years of millisecond range\n // before the high bits push past 252 bits.\n return (tsMs << 24n) | rand;\n}\n\n/**\n * Decrypt the wallet's STARK private key from its\n * `encryptedPrivateKey` ciphertext + the per-wallet `encryptKey` (PIN\n * or passkey-derived secret), build a SHHH V8.4 OE around the given\n * inner calls, sign it with the wallet's STARK key, serialize the\n * calldata for `execute_from_outside_v2`, and forward to chipi-back's\n * `/transactions/execute-sponsored-raw` endpoint.\n *\n * Returns the on-chain transaction hash.\n *\n * Pre-conditions:\n * - `wallet.walletType === \"SHHH\"`.\n * - `wallet.publicKey` is the deployed account address (NOT the raw\n * STARK pubkey; V8.4 deployment derives address from kind-bound\n * salt + verifier).\n * - `wallet.encryptedPrivateKey` decrypts cleanly with `encryptKey`.\n * - The wallet is already deployed (no deploy_and_invoke variant\n * here; that flow stays inside `createWallet`).\n */\nexport async function executeShhhSponsoredRaw(params: {\n client: ChipiClient;\n bearerToken: string;\n wallet: {\n publicKey: string;\n encryptedPrivateKey: string;\n walletType?: string;\n };\n encryptKey: string;\n calls: Call[];\n /**\n * Defaults to `\"STARK\"`. Wave B ships STARK + ED25519. EIP-191 /\n * WEBAUTHN_P256 / JWT_ES256_APPLE_SUB are deferred to the\n * external-rooted wallet adapter PRs.\n */\n signerKind?: \"STARK\" | \"ED25519\";\n /** Override the OE nonce; default = `Date.now() << 24 | random24bit`. */\n nonce?: bigint;\n /** Optional externalUserId for the audit-log hint. */\n externalUserId?: string;\n /**\n * Override the chain ID used for SNIP-12 hashing. Defaults to\n * mainnet per CLAUDE.md's mainnet-only invariant; sepolia is auto-\n * inferred from the chipi-back base URL when present. Drift here\n * = the V8.4 account rejects the signature on chain.\n */\n chainId?: bigint | string;\n}): Promise<string> {\n const signerKind = params.signerKind ?? \"STARK\";\n const SDK_SHIPPED_OE_KINDS = [\"STARK\", \"ED25519\"] as const;\n if (!SDK_SHIPPED_OE_KINDS.includes(signerKind as (typeof SDK_SHIPPED_OE_KINDS)[number])) {\n throw new Error(\n `SHHH signerKind=\"${signerKind}\" is not yet shipped from the SDK ` +\n \"(Wave B ships STARK + ED25519). Wait for the follow-up PR that \" +\n \"adds external-rooted (MetaMask, Phantom, passkey, Apple) signing.\"\n );\n }\n\n const privateKeyHex = decryptPrivateKey(params.wallet.encryptedPrivateKey, params.encryptKey);\n if (!privateKeyHex) {\n throw new Error(\n \"Failed to decrypt private key for SHHH OE. Verify the encryptKey matches the one used at wallet creation.\"\n );\n }\n\n const oe = buildOutsideExecution({\n nonce: params.nonce ?? newOeNonce(),\n calls: params.calls,\n caller: ANY_CALLER_FELT,\n });\n\n const chainId = resolveShhhChainId({\n override: params.chainId,\n baseUrl: params.client.baseUrl,\n });\n const messageHash = computeOeHash(oe, params.wallet.publicKey, chainId);\n\n let signature: bigint[];\n if (signerKind === \"STARK\") {\n signature = buildStarkEnvelope({\n privateKey: privateKeyHex,\n messageHash,\n });\n } else {\n // ED25519 — decode the stored hex private key back to 32 raw bytes\n // and route through the Ed25519 envelope builder. The builder\n // generates RFC 8032 deterministic signatures + the Garaga\n // calldata hints the Cairo verifier requires.\n //\n // Defensive 0x prefix strip: today the wallet-creation path stores\n // bare hex (no prefix), but a future storage migration might add\n // one. Normalizing here keeps the validator's error message\n // accurate either way.\n const edKeyHex = privateKeyHex.startsWith(\"0x\") ? privateKeyHex.slice(2) : privateKeyHex;\n if (edKeyHex.length !== 64) {\n throw new Error(\n `SHHH ED25519 private key is ${edKeyHex.length} hex chars; expected 64 (32 raw bytes). ` +\n \"The stored ciphertext does not look like a Chipi-held Ed25519 key — check wallet provenance.\"\n );\n }\n const edPrivKey = Uint8Array.from(Buffer.from(edKeyHex, \"hex\"));\n signature = await buildEd25519Envelope({\n privateKey: edPrivKey,\n messageHash,\n });\n }\n\n const calldata = serializeExecuteFromOutsideV2Calldata({\n outsideExecution: oe,\n signature,\n });\n\n // Forward to chipi-back's /transactions/execute-sponsored-raw. The\n // endpoint owns the paymaster JSON-RPC call (paymaster cannot be\n // hit directly without the paymaster API key, which lives only on\n // chipi-back). chipi-back also records the resulting Transaction.\n const response = await params.client.post<{\n transactionHash?: string;\n id?: string;\n }>({\n endpoint: \"/transactions/execute-sponsored-raw\",\n bearerToken: params.bearerToken,\n body: {\n accountAddress: params.wallet.publicKey,\n calldata,\n signerKind,\n ...(params.externalUserId ? { externalUserId: params.externalUserId } : {}),\n },\n });\n\n // The endpoint returns either a saved Transaction row (with\n // `transactionHash`) or `{ transactionHash, trackingId }` when\n // saveToDatabase=false. Either way the hash is at the top level.\n if (!response || !response.transactionHash) {\n throw new Error(\n \"execute-sponsored-raw response missing transactionHash. Check the chipi-back logs for upstream paymaster errors.\"\n );\n }\n return response.transactionHash;\n}\n","import type {\n CreateWalletParams,\n CreateWalletResponse,\n PrepareWalletCreationResponse,\n CreateCustodialWalletParams,\n GetWalletParams,\n WalletData,\n GetWalletResponse,\n GetTokenBalanceParams,\n GetTokenBalanceResponse,\n WalletType,\n DeploymentData,\n UpdateWalletEncryptionParams,\n UpdateWalletEncryptionResponse,\n PrepareWalletUpgradeParams,\n PrepareWalletUpgradeResponse,\n ExecuteWalletUpgradeParams,\n ExecuteWalletUpgradeResponse,\n} from \"@chipi-stack/types\";\nimport {\n API_ENDPOINTS,\n SHHH_VERIFIER_CLASS_HASHES,\n WALLET_CLASS_HASHES,\n WALLET_RPC_ENDPOINTS,\n} from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport CryptoES from \"crypto-es\";\n\nimport { ChipiTransactionError, ChipiApiError } from \"@chipi-stack/shared\";\nimport { computeShhhAddress } from \"./shhh/compute-wallet-address\";\nimport { ed25519PubkeyFelts } from \"./shhh/signers/ed25519\";\nimport { shortString } from \"starknet\";\nimport { randomBytes } from \"crypto\";\nimport {\n Account,\n CairoCustomEnum,\n CairoOption,\n CairoOptionVariant,\n CallData,\n ec,\n hash,\n num,\n RpcProvider,\n stark,\n} from \"starknet\";\nimport { encryptPrivateKey } from \"./encryption\";\nimport {\n migrateChipiWalletToShhh,\n type MigrateChipiWalletToShhhParams,\n type MigrationOutcome,\n} from \"./migrate-to-shhh\";\n\n// Local DeploymentData type (replacing @avnu/gasless-sdk dependency)\n/**\n * Wallet management utilities\n */\nexport class ChipiWallets {\n constructor(private client: ChipiClient) {}\n\n /**\n * Create a new wallet on Starknet (gasless, paymaster-sponsored).\n *\n * **PIN is weak — not recommended for production.** `encryptKey` here\n * doubles as the only thing decrypting the wallet's private key. A\n * user-typed PIN is short, low-entropy, and trivially compromised\n * by shoulder-surfing or phishing. Production embedded-wallet apps\n * should derive `encryptKey` from a platform passkey\n * (`@chipi-stack/chipi-passkey` `getWalletEncryptKey` for the legacy\n * PRF flow, or — preferred — `createShhhPasskey` for new SHHH\n * V8.4 wallets where the P-256 key never leaves the platform\n * authenticator at all). PIN remains only as a last-resort\n * recovery surface for users without WebAuthn / biometrics.\n */\n async createWallet(\n params: CreateWalletParams & { bearerToken: string }\n ): Promise<CreateWalletResponse> {\n try {\n const {\n encryptKey,\n externalUserId,\n bearerToken,\n userId,\n // P2.2 default flip (re-applied 2026-05-29 after #270 revert and\n // the post-revert SHHH bug-fix run #271–#296). New wallets\n // default to SHHH V8.4 instead of legacy CHIPI v29 — pluggable-\n // signer Starknet account, guardian recovery, threshold support\n // out of the box. Existing wallets unaffected; this only\n // changes the default for callers that omit `walletType`.\n // Callers who explicitly pass `walletType: \"CHIPI\"` or\n // `\"READY\"` continue producing those.\n //\n // Server-side override: chipi-back's CHIPI_SHHH_DEFAULT env\n // var (chipi-back#267) lets ops force the legacy default back\n // for callers that bypass the SDK and POST to /chipi-wallets\n // directly without walletType.\n //\n // signerKind under the SHHH branch defaults to \"STARK\" further\n // down — same kind the original #267 P2.2 used and the same\n // kind every mainnet smoke since 2026-05-26 has been signing\n // under.\n walletType = \"SHHH\",\n classHash: customClassHash,\n usePasskey,\n chain,\n } = params;\n\n if (!encryptKey) {\n if (usePasskey) {\n throw new Error(\n \"encryptKey is required when using passkey. The passkey authentication should have provided the encryptKey.\"\n );\n }\n throw new Error(\"encryptKey is required for wallet creation\");\n }\n\n // SHHH V8.4 wallets use a different address derivation\n // (computeShhhAddress) and a different constructor calldata\n // shape ([kind, verifier, pubkey_len, *pubkey, label]). They\n // also use the SHHH paymaster lane on the same Chipi RPC.\n //\n // Wave B (this PR) ships ED25519 on top of the STARK default.\n // EIP-191 / WEBAUTHN_P256 / JWT_ES256_APPLE_SUB still throw the\n // deferred-feature error until their respective follow-up PRs\n // wire the wallet-adapter pubkey input (browser / OAuth context\n // the backend SDK doesn't have).\n const isShhh = walletType === \"SHHH\";\n const effectiveSignerKind = isShhh ? (params.signerKind ?? \"STARK\") : undefined;\n const SDK_SHIPPED_SHHH_KINDS = [\"STARK\", \"ED25519\"] as const;\n if (\n isShhh &&\n !SDK_SHIPPED_SHHH_KINDS.includes(\n effectiveSignerKind as (typeof SDK_SHIPPED_SHHH_KINDS)[number]\n )\n ) {\n throw new Error(\n `SHHH signerKind=${effectiveSignerKind} is not yet shipped from the SDK ` +\n `(Wave B ships STARK + ED25519). Use signerKind=\"STARK\" or ` +\n `\"ED25519\", or wait for the follow-up PRs that add external-rooted ` +\n \"(MetaMask, Phantom, passkey, Apple) deploy support.\"\n );\n }\n\n // Select RPC endpoint based on wallet type. SHHH uses the same\n // RPC pool as CHIPI today; if the V8.4 paymaster ever moves to\n // its own lane this becomes a third branch.\n const rpcUrl =\n walletType === \"READY\" ? WALLET_RPC_ENDPOINTS.READY : WALLET_RPC_ENDPOINTS.CHIPI;\n\n const provider = new RpcProvider({ nodeUrl: rpcUrl });\n\n // Generating the private key with Stark Curve. For SHHH STARK\n // this is exactly the same as CHIPI/READY: Chipi generates and\n // holds the STARK key, encrypts it with encryptKey, and the\n // user's passkey/PIN unwraps it at sign time.\n const privateKeyAX = this.getPrivateKeyAX();\n const starkKeyPubAX = ec.starkCurve.getStarkKey(privateKeyAX);\n\n let constructorCallData: string[];\n let accountClassHash: string;\n let publicKey: string;\n let deploymentSalt: string;\n // SHHH-only metadata sent through to chipi-back so the new\n // signerKind / ownerPublicKey columns get populated.\n let shhhOwnerPubkeyFelts: string[] | undefined;\n // For SHHH non-STARK kinds the Chipi-held key isn't the STARK\n // key. Default to STARK so CHIPI/READY/SHHH-STARK keep using\n // privateKeyAX (the existing behavior). ED25519 overrides this.\n // Empty string = external-rooted (no Chipi-held key to store).\n let shhhKeyForEncryption: string = privateKeyAX;\n\n if (isShhh) {\n // Resolve the pubkey felts + the Chipi-held private key (if any)\n // for the chosen signer kind. STARK and ED25519 both have\n // Chipi-held paths today; ED25519 also accepts caller-supplied\n // `ownerPubkeyFelts` (external-rooted, Phantom shape) which we\n // surface via the same code path.\n let pubkeyFelts: bigint[];\n let shhhPrivateKeyHex: string;\n if (effectiveSignerKind === \"STARK\") {\n // Chipi-held STARK: reuse the STARK key we already generated.\n pubkeyFelts = [BigInt(starkKeyPubAX)];\n shhhPrivateKeyHex = privateKeyAX;\n } else if (effectiveSignerKind === \"ED25519\") {\n if (params.ownerPubkeyFelts && params.ownerPubkeyFelts.length > 0) {\n // External-rooted ED25519 (Phantom). Caller supplies the\n // 2-felt [low, high] LE u128 split of the 32-byte pubkey.\n // Chipi does not hold a signing key in this branch — the\n // user signs OEs via their wallet adapter and the SDK just\n // wraps the signature in an envelope.\n if (params.ownerPubkeyFelts.length !== 2) {\n throw new Error(\n `SHHH ED25519 ownerPubkeyFelts must be [low_u128, high_u128] (2 felts); got ${params.ownerPubkeyFelts.length}.`\n );\n }\n pubkeyFelts = params.ownerPubkeyFelts.map((s) => BigInt(s));\n shhhPrivateKeyHex = \"\"; // No Chipi-held key for external-rooted.\n } else {\n // Chipi-held ED25519 (mirrors STARK pattern). Generate a\n // 32-byte random secret + derive the 32-byte public key\n // via @noble/ed25519. The private key is stored as hex\n // through the same encryption helper the STARK path uses.\n const edPrivKey: Uint8Array = randomBytes(32);\n const ed = await import(\"@noble/ed25519\");\n const edPubKey = await ed.getPublicKeyAsync(edPrivKey);\n pubkeyFelts = ed25519PubkeyFelts(edPubKey);\n shhhPrivateKeyHex = Buffer.from(edPrivKey).toString(\"hex\");\n }\n } else {\n // Defense-in-depth: SDK_SHIPPED_SHHH_KINDS already filtered\n // upstream, but assert here so a future kind addition doesn't\n // silently land with no implementation.\n throw new Error(\n `SHHH signerKind=${effectiveSignerKind} reached the address-derivation block without an implementation.`\n );\n }\n\n shhhOwnerPubkeyFelts = pubkeyFelts.map((f) => \"0x\" + f.toString(16));\n\n // Stash the per-kind private key on a closure variable so the\n // encryption step below knows what to encrypt (replaces the\n // hardcoded `privateKeyAX` for non-STARK kinds).\n shhhKeyForEncryption = shhhPrivateKeyHex;\n\n const verifierClassHash = BigInt(\n SHHH_VERIFIER_CLASS_HASHES[effectiveSignerKind as \"STARK\" | \"ED25519\"]\n );\n const shhhClassHash = customClassHash ?? WALLET_CLASS_HASHES.SHHH;\n\n // computeShhhAddress reproduces the on-chain constructor's\n // address derivation, including the kind-bound salt that\n // prevents the same raw pubkey from colliding across signer\n // kinds.\n const computedAddress = computeShhhAddress({\n classHash: BigInt(shhhClassHash),\n primaryKind: effectiveSignerKind as \"STARK\" | \"ED25519\",\n pubkey: pubkeyFelts,\n verifierClassHash,\n });\n publicKey = \"0x\" + computedAddress.toString(16);\n accountClassHash = shhhClassHash;\n\n // Constructor calldata shape: [kindTag, verifierClass,\n // pubkey_len, *pubkey, label]. Must match\n // computeShhhAddress's `ctor` shape byte-for-byte or the\n // deployment address mismatches.\n const labelFelt = BigInt(shortString.encodeShortString(\"primary\"));\n const kindTagFelt = BigInt(shortString.encodeShortString(effectiveSignerKind!));\n constructorCallData = [\n kindTagFelt.toString(),\n verifierClassHash.toString(),\n pubkeyFelts.length.toString(),\n ...pubkeyFelts.map((f) => f.toString()),\n labelFelt.toString(),\n ];\n\n // SHHH paymaster_deploy_and_invoke salt is poseidon(kind,\n // owner_commitment), not the raw pubkey. Recompute it the same\n // way computeShhhAddress did.\n const salt = (() => {\n const ownerCommit = BigInt(\n hash.computePoseidonHashOnElements([\n \"0x\" + kindTagFelt.toString(16),\n ...pubkeyFelts.map((f) => \"0x\" + f.toString(16)),\n ])\n );\n return BigInt(\n hash.computePoseidonHashOnElements([\n \"0x\" + kindTagFelt.toString(16),\n \"0x\" + ownerCommit.toString(16),\n ])\n );\n })();\n deploymentSalt = \"0x\" + salt.toString(16);\n } else {\n // CHIPI / READY path: unchanged from before this PR.\n constructorCallData = this.buildConstructorCallData(walletType, starkKeyPubAX);\n accountClassHash =\n customClassHash ??\n (walletType === \"READY\" ? WALLET_CLASS_HASHES.READY : WALLET_CLASS_HASHES.CHIPI);\n publicKey = hash.calculateContractAddressFromHash(\n starkKeyPubAX,\n accountClassHash,\n constructorCallData,\n 0\n );\n deploymentSalt = starkKeyPubAX;\n }\n\n // Initiating Account\n const account = new Account({ provider, address: publicKey, signer: privateKeyAX });\n\n const typedDataResponse = await this.client.post<PrepareWalletCreationResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/prepare-creation`,\n bearerToken,\n body: {\n chain,\n publicKey,\n walletType,\n starkKeyPubAX,\n ...(customClassHash && { classHash: customClassHash }),\n // SHHH: tell chipi-back which signer kind + pubkey felts\n // produced this deployment so it can validate against the\n // expected SHHH constructor shape and populate the new\n // wallet row columns.\n ...(isShhh && {\n signerKind: effectiveSignerKind,\n ownerPubkeyFelts: shhhOwnerPubkeyFelts,\n }),\n },\n });\n\n const { typedData, accountClassHash: accountClassHashResponse } = typedDataResponse;\n\n // SHHH V8.4 wallets use the chipi-back pure-deploy path\n // (paymaster_executeTransaction with type:\"deploy\"). No signature\n // is required — the paymaster signs its own forwarder tx and\n // calls UDC.deployContract directly. We skip account.signMessage\n // entirely; the userSignature + typedData fields in the create\n // body are kept only for DTO-shape compatibility and are ignored\n // server-side for SHHH.\n //\n // Why not deploy_and_invoke for SHHH: the paymaster's typedData\n // domain shape doesn't agree with V8.4's SNIP-12 V2 OE hashing,\n // so the bundled inner OE's STARK verifier always rejects the\n // signature with \"SHHH: signature invalid\" — silently, because\n // the outer paymaster tx still reports SUCCEEDED (2026-05-27\n // production trace audit, deploy tx 0x672fd5f6...).\n //\n // CHIPI / READY paths unchanged: signMessage(typedData) →\n // {r, s, recovery} → server-side formatOwnerSignature.\n let userSignaturePayload: { r: string; s: string; recovery: number };\n if (isShhh) {\n // Dummy placeholder. chipi-back's createWallet branches on\n // walletType=SHHH and never reads this field; the DTO still\n // requires `userSignature` for shape parity.\n userSignaturePayload = {\n r: \"0x0\",\n s: \"0x0\",\n recovery: 0,\n };\n } else {\n const userSignature = await account.signMessage(typedData);\n userSignaturePayload = {\n r: (userSignature as any).r.toString(),\n s: (userSignature as any).s.toString(),\n recovery: (userSignature as any).recovery,\n };\n }\n\n const deploymentData: DeploymentData = {\n class_hash: accountClassHashResponse,\n salt: deploymentSalt,\n unique: `${num.toHex(0)}`,\n calldata: constructorCallData.map((value) => num.toHex(value)),\n };\n\n // External-rooted SHHH wallets (e.g. Phantom ED25519 via\n // ownerPubkeyFelts) have no Chipi-held key. We omit\n // `encryptedPrivateKey` from the request body entirely in that\n // case so chipi-back stores `null` (per its DTO contract). All\n // other paths encrypt the per-kind private key.\n const encryptedPrivateKey =\n shhhKeyForEncryption === \"\"\n ? undefined\n : encryptPrivateKey(shhhKeyForEncryption, encryptKey);\n\n // Dual-key wrapping: if encryptKeyBackup provided (PIN), encrypt a second copy\n const encryptedPrivateKeyBackup =\n params.encryptKeyBackup && shhhKeyForEncryption !== \"\"\n ? encryptPrivateKey(shhhKeyForEncryption, params.encryptKeyBackup)\n : undefined;\n\n // Call the API to save the wallet in dashboard\n const executeTransactionResponse = await this.client.post<CreateWalletResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}`,\n bearerToken,\n body: {\n externalUserId,\n userId,\n publicKey,\n walletType,\n chain,\n userSignature: userSignaturePayload,\n typedData,\n ...(encryptedPrivateKey !== undefined && { encryptedPrivateKey }),\n // SHHH: write the signer kind to the new wallet row. P0.3's\n // STARK-only path uses a Chipi-held STARK key, so\n // ownerPublicKey stays unset (encryptedPrivateKey carries\n // the same key the verifier checks against). External-rooted\n // wallets will pass ownerPublicKey instead in a follow-up PR.\n ...(isShhh && { signerKind: effectiveSignerKind }),\n ...(encryptedPrivateKeyBackup && { encryptedPrivateKeyBackup }),\n ...(params.authMethod && { authMethod: params.authMethod }),\n ...(params.credentialId && { credentialId: params.credentialId }),\n ...(params.prfSupported != null && { prfSupported: params.prfSupported }),\n ...(params.deviceType && { deviceType: params.deviceType }),\n deploymentData: {\n ...deploymentData,\n salt: `${deploymentData.salt}`,\n calldata: deploymentData.calldata.map((data) => `${data}`),\n },\n },\n });\n\n return executeTransactionResponse;\n } catch (error: unknown) {\n console.error(\"Detailed error:\", error);\n\n if (error instanceof Error && error.message.includes(\"SSL\")) {\n throw new Error(\n \"SSL connection error. Try using NODE_TLS_REJECT_UNAUTHORIZED=0 or verify the RPC URL\"\n );\n }\n throw new ChipiTransactionError(\n `Failed to create wallet: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"WALLET_CREATION_FAILED\"\n );\n }\n }\n\n /**\n * Build constructor calldata based on wallet type\n * - CHIPI: Simple OpenZeppelin account with just public_key\n * - ARGENT: Argent X Account with owner/guardian structure\n */\n private buildConstructorCallData(walletType: WalletType, starkKeyPubAX: string): string[] {\n if (walletType === \"READY\") {\n // Argent X Account: owner (CairoCustomEnum) + guardian (CairoOption None)\n const axSigner = new CairoCustomEnum({\n Starknet: { pubkey: starkKeyPubAX },\n });\n const axGuardian = new CairoOption<unknown>(CairoOptionVariant.None);\n\n return CallData.compile({\n owner: axSigner,\n guardian: axGuardian,\n });\n }\n\n // ChipiWallet (default): Simple OpenZeppelin account with just public_key\n return CallData.compile({\n public_key: starkKeyPubAX,\n });\n }\n\n /**\n * Create a custodial merchant wallet\n */\n async createCustodialWallet({\n params,\n bearerToken,\n }: {\n params: Omit<CreateCustodialWalletParams, \"orgId\">;\n bearerToken: string;\n }): Promise<WalletData> {\n const response = await this.client.post<WalletData>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/custodial`,\n bearerToken,\n body: params,\n });\n\n return response!;\n }\n\n async getWallet(params: GetWalletParams, bearerToken: string): Promise<GetWalletResponse | null> {\n try {\n const { externalUserId } = params;\n const getWalletResponse = await this.client.get<GetWalletResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/by-user`,\n params: { externalUserId },\n bearerToken,\n });\n\n return getWalletResponse;\n } catch (err) {\n // Check if it's a 404 error (wallet not found)\n if (err instanceof ChipiApiError && err.status === 404) {\n return null;\n }\n throw err;\n }\n }\n\n /**\n * Recover passkey credential metadata for a wallet.\n * Used when localStorage is cleared — SDK recovers credentialId from backend.\n */\n async getCredentialRecovery(\n params: { externalUserId: string },\n bearerToken: string\n ): Promise<{\n credentialId: string | null;\n authMethod: string | null;\n prfSupported: boolean | null;\n deviceType: string | null;\n }> {\n return this.client.get({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/credential-recovery`,\n params: { externalUserId: params.externalUserId },\n bearerToken,\n });\n }\n\n async updateWalletEncryption(\n params: UpdateWalletEncryptionParams,\n bearerToken: string\n ): Promise<UpdateWalletEncryptionResponse> {\n const body: Record<string, string> = {\n externalUserId: params.externalUserId,\n newEncryptedPrivateKey: params.newEncryptedPrivateKey,\n };\n\n if (params.publicKey != null) {\n body.publicKey = params.publicKey;\n }\n\n return this.client.patch<UpdateWalletEncryptionResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/update-encryption-details`,\n bearerToken,\n body,\n });\n }\n\n getPrivateKeyAX = () => {\n // Generate 32 random bytes (256 bits)\n // const privateKeyBytes = Crypto.getRandomBytes(32); old implementation\n const privateKeyBytes = CryptoES.lib.WordArray.random(32);\n // Convert to hex string and ensure it's 64 characters (32 bytes)\n const privateKey = privateKeyBytes.toString(CryptoES.enc.Hex);\n\n // Add '0x' prefix\n const fullPrivateKey = `0x${privateKey}`;\n\n // Ensure the private key is within Starknet's valid range (0 to 2^251 - 1)\n // Convert to BigInt and take modulo 2^251\n const maxStarknetValue = BigInt(\n \"0x800000000000000000000000000000000000000000000000000000000000000\"\n );\n const privateKeyBigInt = BigInt(fullPrivateKey) % maxStarknetValue;\n\n // Convert back to hex string with '0x' prefix\n return `0x${privateKeyBigInt.toString(16)}`;\n };\n\n async getTokenBalance({\n params,\n bearerToken,\n }: {\n params: GetTokenBalanceParams;\n bearerToken: string;\n }): Promise<GetTokenBalanceResponse> {\n const getBalanceResponse = await this.client.get<GetTokenBalanceResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/token-balance`,\n params,\n bearerToken,\n });\n\n return getBalanceResponse;\n }\n\n /**\n * Prepare a wallet upgrade (e.g., READY → CHIPI or CHIPI v29 → v33).\n * Returns typed data that must be signed by the wallet owner.\n */\n async prepareWalletUpgrade(\n params: PrepareWalletUpgradeParams,\n bearerToken: string\n ): Promise<PrepareWalletUpgradeResponse> {\n return this.client.post<PrepareWalletUpgradeResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/prepare-upgrade`,\n bearerToken,\n body: params,\n });\n }\n\n /**\n * Execute a wallet upgrade after the owner has signed the typed data.\n */\n async executeWalletUpgrade(\n params: ExecuteWalletUpgradeParams,\n bearerToken: string\n ): Promise<ExecuteWalletUpgradeResponse> {\n return this.client.post<ExecuteWalletUpgradeResponse>({\n endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/upgrade`,\n bearerToken,\n body: params,\n });\n }\n\n /**\n * Migrate a CHIPI v29 wallet to SHHH V8.4 in-place at the same\n * on-chain address. Composes the two-phase chipi-back endpoint:\n * prepare returns the path decision + parameters, then this method\n * builds the [upgrade, bootstrap_from_sessions] multicall locally,\n * signs the V8.4 OE with the wallet's STARK key (decrypted via\n * the user's encryptKey), and sends the signed calldata to the\n * execute endpoint. Server forwards verbatim to the paymaster.\n *\n * Returns `{kind: \"ALREADY_MIGRATED\"}` if the wallet is already\n * SHHH on chain (DB row gets patched server-side; no on-chain tx\n * fires); otherwise `{kind: \"MIGRATED\", transactionHash, ...}`.\n *\n * Throws on STRANDED-state recovery, UNDEPLOYED wallets, and\n * other deferred-feature paths with actionable error messages.\n *\n * Endpoints contract shipped in chipi-back task #28 (PR #272).\n */\n async migrateToShhh(\n params: Omit<MigrateChipiWalletToShhhParams, \"client\" | \"bearerToken\">,\n bearerToken: string\n ): Promise<MigrationOutcome> {\n return migrateChipiWalletToShhh({\n client: this.client,\n bearerToken,\n ...params,\n });\n }\n}\n","/**\n * SDK-side orchestrator for the CHIPI v29 → SHHH V8.4 migration flow.\n *\n * 1. POST /chipi-wallets/prepare-migrate-to-shhh\n * Returns the path decision (HAPPY / STRANDED / ALREADY_MIGRATED)\n * plus the parameters the SDK uses to build the [upgrade(SHHH),\n * bootstrap_from_sessions] multicall locally.\n *\n * 2. Build the multicall via `buildMigrationCalls`. Targets the\n * wallet itself (CHIPI v29 sessions account → V8.4 ShhhAccount).\n *\n * 3. Broadcast the multicall through the **existing CHIPI v29\n * transaction flow** (`executePaymasterTransaction` →\n * `/transactions/prepare-typed-data` → SDK signs `(r, s)` locally\n * → `/transactions/execute-sponsored` → paymaster relay). The\n * wallet is still CHIPI v29 at sign time, so the signature must\n * use CHIPI v29's `(r, s)` SNIP-12 format — not V8.4's V2_SNIP12\n * envelope.\n *\n * This is the historical bug fixed by this refactor. The\n * original orchestrator (PR #282) used V8.4's\n * `buildStarkEnvelope` + `serializeExecuteFromOutsideV2Calldata`\n * and sent a 5-felt envelope `[V2_SNIP12, KIND_TAG, len, r, s]`\n * to the wallet. CHIPI v29's\n * `sessions-smart-contract/src/account.cairo::execute_from_outside_v2`\n * read `sig[0:2]` as `(r, s)`, got bogus values, panicked with\n * `SRC9: invalid signature` (line 430). Because CHIPI v29's\n * `_execute_calls` swallows `Result::Err` at line 684, the outer\n * tx still SUCCEEDED but the upgrade(SHHH) call never ran. The\n * class hash stayed CHIPI v29. The wallet panic was confirmed by\n * reading the inner-call `result` field: ASCII-decoded to\n * literal `SRC9: invalid signature`. Reference receipt:\n * `0x0535ed9ca8628f1c82ee2005d84017e59a02436597df371d516b97c2c93a516a`\n * (staging wallet `0x1434aadce…`).\n *\n * 4. POST /chipi-wallets/execute-migrate-to-shhh with the tx hash.\n * chipi-back verifies the on-chain class hash flipped to SHHH\n * V8.4 and patches the DB row.\n *\n * Stranded-state recovery (STRANDED path) is NOT shipped here. The\n * SDK has the builders (`buildBootstrapSignedCall`,\n * `computeBootstrapMessage`, `signBootstrapMessage`) but the flow is\n * sufficiently different from HAPPY that surfacing it cleanly needs\n * its own UI affordance. Throws a clear deferred-feature error so\n * the calling dashboard can route to a stranded-recovery UI.\n */\n\nimport { ec } from \"starknet\";\n\nimport type { ChipiClient } from \"./client\";\nimport { decryptPrivateKey } from \"./encryption\";\nimport { executePaymasterTransaction } from \"./execute-paymaster-transaction\";\nimport { buildMigrationCalls } from \"./shhh/migrate\";\n\n/**\n * Response shape from chipi-back's `prepare-migrate-to-shhh`. Mirrors\n * the union the server returns; the SDK orchestrator switches on\n * `migrationPath` to decide what to do next.\n */\nexport type PrepareMigrateResponse =\n | {\n migrationPath: \"HAPPY\";\n walletAddress: string;\n onChainClassHash: string;\n shhhClassHash: string;\n shhhVerifierClassHash: string;\n label: string;\n auditLogId: string;\n }\n | {\n migrationPath: \"STRANDED\";\n walletAddress: string;\n onChainClassHash: string;\n shhhVerifierClassHash: string;\n label: string;\n auditLogId: string;\n }\n | {\n migrationPath: \"ALREADY_MIGRATED\";\n walletAddress: string;\n onChainClassHash: string;\n primaryKind: string;\n };\n\n/**\n * Response shape from chipi-back's `execute-migrate-to-shhh`.\n *\n * On terminal success, the server returns the patched wallet shape.\n * On PARTIAL failure (on-chain succeeded + DB patch failed) the\n * server still returns 200 with `partial: true` — the migration tx\n * landed but the calling dashboard should surface \"we'll catch up on\n * our end shortly\" until the reconciler cron (task #30) patches the\n * row.\n */\nexport interface ExecuteMigrateResponse {\n walletId: string;\n walletType: \"SHHH\";\n signerKind: \"STARK\";\n classHash: string;\n migratedFromClassHash: string;\n transactionHash: string;\n partial?: boolean;\n}\n\n/**\n * Final result returned by `migrateChipiWalletToShhh`. Either a\n * full migration completed end-to-end, OR an ALREADY_MIGRATED\n * short-circuit where the server patched the DB row but no on-chain\n * tx was needed. Both are terminal; both leave the wallet in SHHH\n * state for subsequent SDK operations.\n */\nexport type MigrationOutcome =\n | (ExecuteMigrateResponse & { kind: \"MIGRATED\" })\n | {\n kind: \"ALREADY_MIGRATED\";\n walletAddress: string;\n onChainClassHash: string;\n primaryKind: string;\n };\n\nexport interface MigrateChipiWalletToShhhParams {\n client: ChipiClient;\n bearerToken: string;\n /** Chipi-back wallet row id + the wallet's tenant scope. */\n walletId: string;\n externalUserId: string;\n /** Wallet shape the SDK passes through from its `wallets.getWallet` cache. */\n wallet: {\n publicKey: string;\n encryptedPrivateKey: string;\n };\n /** PIN or passkey-derived secret that decrypts `encryptedPrivateKey`. */\n encryptKey: string;\n /**\n * @deprecated The pre-refactor orchestrator (sdks #282) used this to\n * override the V8.4 SNIP-12 chain id. The new flow inherits chain\n * config from `executePaymasterTransaction`'s RPC endpoint\n * resolution, so this field is ignored. Kept for source-compat with\n * any v14.5.0-rc callers that may have copied the older signature\n * into their codebase before publish.\n */\n chainId?: bigint | string;\n}\n\nexport async function migrateChipiWalletToShhh(\n params: MigrateChipiWalletToShhhParams\n): Promise<MigrationOutcome> {\n const prepare = await params.client.post<PrepareMigrateResponse>({\n endpoint: \"/chipi-wallets/prepare-migrate-to-shhh\",\n bearerToken: params.bearerToken,\n body: {\n walletId: params.walletId,\n externalUserId: params.externalUserId,\n },\n });\n\n if (prepare.migrationPath === \"ALREADY_MIGRATED\") {\n return {\n kind: \"ALREADY_MIGRATED\",\n walletAddress: prepare.walletAddress,\n onChainClassHash: prepare.onChainClassHash,\n primaryKind: prepare.primaryKind,\n };\n }\n\n if (prepare.migrationPath === \"STRANDED\") {\n throw new Error(\n \"SHHH stranded-state recovery is not yet shipped in the SDK client. \" +\n \"The wallet is at V8.4 class hash but primary_kind == 0, which \" +\n \"means a prior migration completed `upgrade(SHHH)` but the \" +\n \"`bootstrap_from_sessions` call silently reverted. Recovery \" +\n \"requires `bootstrap_from_sessions_signed`; see \" +\n \"`buildBootstrapSignedCall` in @chipi-stack/backend/src/shhh/migrate.ts.\"\n );\n }\n\n // Defensive guard: if chipi-back ever folds UNDEPLOYED / DEFERRED /\n // WRONG_CLASS / RPC_ERROR into the 200 union (today they're HTTP\n // 4xx), the HAPPY-shaped field reads below would NaN/undefined\n // their way into a malformed multicall. Fail fast with a clear\n // \"upgrade your SDK\" message instead.\n if (prepare.migrationPath !== \"HAPPY\") {\n throw new Error(\n \"SHHH migration: unexpected migrationPath \" +\n JSON.stringify((prepare as { migrationPath: unknown }).migrationPath) +\n \" returned by chipi-back. This SDK build only handles HAPPY / \" +\n \"STRANDED / ALREADY_MIGRATED; upgrade @chipi-stack/backend to a \" +\n \"version that knows this path.\"\n );\n }\n\n // Derive the STARK pubkey from the decrypted private key — V8.4's\n // `bootstrap_from_sessions` call needs the pubkey as its first\n // argument so the contract can stamp the owner_commitment.\n //\n // Decryption happens here in the orchestrator AND a second time\n // inside executePaymasterTransaction. Both decryptions stay local\n // — the cleartext key never crosses the wire. The double-decrypt\n // is the cost of using `executePaymasterTransaction` as a black\n // box; a future refactor could expose a \"derive STARK pubkey from\n // wallet\" helper on ChipiWallets to avoid it.\n const privateKeyHex = decryptPrivateKey(params.wallet.encryptedPrivateKey, params.encryptKey);\n if (!privateKeyHex) {\n throw new Error(\n \"Failed to decrypt STARK private key for migration. Verify the \" +\n \"encryptKey matches the one used at wallet creation.\"\n );\n }\n const starkKeyPub = BigInt(ec.starkCurve.getStarkKey(privateKeyHex));\n\n const calls = buildMigrationCalls({\n legacyAccountAddress: params.wallet.publicKey,\n legacyPublicKey: starkKeyPub,\n shhhAccountClassHash: BigInt(prepare.shhhClassHash),\n starkVerifierClassHash: BigInt(prepare.shhhVerifierClassHash),\n label: prepare.label,\n });\n\n // Broadcast through the existing CHIPI v29 transaction flow. The\n // wallet is still CHIPI v29 at sign time, so the OE signature must\n // use CHIPI v29's `(r, s)` SNIP-12 format. executePaymasterTransaction\n // routes to `/transactions/prepare-typed-data` → SDK signs locally\n // → `/transactions/execute-sponsored` for CHIPI walletType, which\n // is exactly what the wallet's `execute_from_outside_v2` validates.\n const transactionHash = await executePaymasterTransaction({\n params: {\n encryptKey: params.encryptKey,\n wallet: {\n publicKey: params.wallet.publicKey,\n encryptedPrivateKey: params.wallet.encryptedPrivateKey,\n // Explicit walletType: the wallet IS still CHIPI v29 at sign\n // time. The upgrade(SHHH) inside the multicall changes the\n // class hash mid-tx; that's after signature verification.\n walletType: \"CHIPI\",\n },\n calls,\n // Persist the migration tx so it shows up in the user's tx\n // history. The on-chain receipt is the load-bearing artifact;\n // the DB row is convenience surfacing.\n saveToDatabase: true,\n },\n bearerToken: params.bearerToken,\n client: params.client,\n });\n\n // Notify chipi-back to verify on chain + patch the DB row. The\n // execute endpoint reads the live class hash and asserts it\n // flipped to SHHH V8.4 — if the migration tx silently reverted at\n // the wallet sub-call (the original bug), this surfaces a clear\n // BadRequestException rather than letting the DB drift.\n const execute = await params.client.post<ExecuteMigrateResponse>({\n endpoint: \"/chipi-wallets/execute-migrate-to-shhh\",\n bearerToken: params.bearerToken,\n body: {\n walletId: params.walletId,\n externalUserId: params.externalUserId,\n transactionHash,\n },\n });\n\n return { kind: \"MIGRATED\", ...execute };\n}\n","import type {\n ExecuteSponsoredTransactionResponse,\n WalletData,\n WalletType,\n SessionKeyData,\n PrepareTypedDataResponse,\n} from \"@chipi-stack/types\";\nimport type { Call } from \"starknet\";\nimport { Account, RpcProvider, TypedData, ec, typedData, num } from \"starknet\";\nimport { decryptPrivateKey } from \"./encryption\";\nimport {\n WALLET_CLASS_HASHES,\n WALLET_RPC_ENDPOINTS,\n SESSION_ERRORS,\n ChipiSessionError,\n} from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport { normalizeSignature } from \"./paymaster-adapter\";\n\n// Extended WalletData with optional wallet type for transactions\n\n// Internal type - not exported to users\ninterface ExecutePaymasterTransactionParams {\n encryptKey?: string;\n wallet: WalletData;\n calls: Call[];\n saveToDatabase: boolean; // Internal only - always set by SDK, users cannot control this\n usePasskey?: boolean;\n}\n\n/**\n * Execute a paymaster transaction (gasless) via Chipi's paymaster\n * Supports both CHIPI and ARGENT wallet types\n */\nexport const executePaymasterTransaction = async ({\n params,\n bearerToken,\n client,\n}: {\n params: ExecutePaymasterTransactionParams;\n bearerToken: string;\n client: ChipiClient;\n}): Promise<string> => {\n try {\n const { encryptKey, wallet, calls, saveToDatabase, usePasskey } = params;\n\n // Validate encryptKey is provided\n if (!encryptKey) {\n throw new Error(\n usePasskey\n ? \"encryptKey is required when using passkey. The passkey authentication should have provided the encryptKey.\"\n : \"encryptKey is required for transaction execution\"\n );\n }\n\n const walletType = wallet.walletType ?? \"READY\";\n\n // SHHH V8.4 ShhhAccount wallets cannot use the legacy\n // prepare-typed-data → sign → execute-sponsored-transaction flow\n // because the Chipi paymaster cannot synthesize SHHH envelopes\n // server-side (each V8.4 signer kind has a different envelope\n // shape — see chipi-pay/paymaster#16). Route SHHH OEs through the\n // execute-sponsored-raw forwarder which expects pre-signed\n // calldata. The legacy flow below is unchanged for CHIPI/READY.\n if (walletType === \"SHHH\") {\n const { executeShhhSponsoredRaw } = await import(\"./shhh/execute-paymaster-raw\");\n // Auto-route to the right envelope builder. Default to STARK to\n // preserve pre-Wave-B behavior when the caller's WalletData was\n // populated from an older response (or from a non-SHHH row where\n // chipi-back returns null for signerKind).\n const shhhSignerKind = ((wallet.signerKind ?? \"STARK\") || \"STARK\") as \"STARK\" | \"ED25519\";\n return await executeShhhSponsoredRaw({\n client,\n bearerToken,\n wallet: {\n publicKey: wallet.publicKey,\n encryptedPrivateKey: wallet.encryptedPrivateKey,\n walletType: wallet.walletType,\n },\n encryptKey,\n calls,\n signerKind: shhhSignerKind,\n });\n }\n\n // For known types, use mapped RPC and class hash.\n // For custom types, fall back to default CHIPI RPC (all StarkNet accounts use the same RPC)\n // and use the wallet's classHash if available.\n const isKnownType =\n Object.prototype.hasOwnProperty.call(WALLET_RPC_ENDPOINTS, walletType) &&\n Object.prototype.hasOwnProperty.call(WALLET_CLASS_HASHES, walletType);\n\n const rpcUrl = isKnownType ? WALLET_RPC_ENDPOINTS[walletType] : WALLET_RPC_ENDPOINTS[\"CHIPI\"];\n const accountClassHash = isKnownType\n ? WALLET_CLASS_HASHES[walletType]\n : WALLET_CLASS_HASHES[\"CHIPI\"];\n\n // Decrypt the private key\n let privateKeyDecrypted: string;\n try {\n privateKeyDecrypted = decryptPrivateKey(wallet.encryptedPrivateKey, encryptKey);\n } catch {\n throw new Error(\"Failed to decrypt private key. Verify the encryptKey is correct.\");\n }\n\n const provider = new RpcProvider({ nodeUrl: rpcUrl });\n\n const account = new Account({\n provider,\n address: wallet.publicKey,\n signer: privateKeyDecrypted,\n });\n\n // Build the type data via Chipi's backend\n const prepareResponse = await client.post<PrepareTypedDataResponse>({\n endpoint: \"/transactions/prepare-typed-data\",\n bearerToken,\n body: {\n publicKey: wallet.publicKey,\n calls: calls,\n accountClassHash,\n },\n });\n\n // Validate response structure\n if (!prepareResponse || typeof prepareResponse !== \"object\") {\n throw new Error(\n \"Invalid response from /transactions/prepare-typed-data: response is not an object\"\n );\n }\n\n // Support both current and legacy API response shapes:\n // - Current: { typedData: TypedData, walletType: WalletType }\n // - Legacy: TypedData directly (domain/types/primaryType/message)\n let typedData: TypedData | undefined;\n let resolvedWalletType: WalletType | undefined;\n\n if (\"typedData\" in prepareResponse) {\n const resp = prepareResponse as PrepareTypedDataResponse;\n typedData = resp.typedData;\n // Only accept known wallet types from the backend\n if (\n resp.walletType &&\n Object.prototype.hasOwnProperty.call(WALLET_CLASS_HASHES, resp.walletType)\n ) {\n resolvedWalletType = resp.walletType;\n }\n } else if (prepareResponse && \"types\" in prepareResponse && \"message\" in prepareResponse) {\n // Legacy shape: the response itself is the TypedData\n typedData = prepareResponse as TypedData;\n } else {\n // Likely an API error shape like { message: string }\n const apiMessage =\n prepareResponse && typeof (prepareResponse as any).message === \"string\"\n ? (prepareResponse as any).message\n : undefined;\n\n throw new Error(\n apiMessage ??\n \"Invalid response from /transactions/prepare-typed-data: typedData is missing. \" +\n \"This may occur for older STARKNET wallets that don't support paymaster transactions. \" +\n \"Please ensure the wallet is a CHIPI wallet type.\"\n );\n }\n\n // Validate typedData structure before signing\n if (\n !typedData ||\n typeof typedData !== \"object\" ||\n !(\"types\" in typedData) ||\n !(\"message\" in typedData)\n ) {\n throw new Error(\n \"Invalid typedData received from backend: missing 'types' or 'message' field. \" +\n \"This may occur for older STARKNET wallets that don't support paymaster transactions.\"\n );\n }\n\n // Sign the message\n const userSignature = await account.signMessage(typedData);\n\n // Normalize signature format (handles both Weierstrass and Array types)\n const normalizedSig = normalizeSignature(userSignature);\n\n // Execute the sponsored transaction via Chipi's paymaster\n const result = await client.post<ExecuteSponsoredTransactionResponse>({\n endpoint: \"/transactions/execute-sponsored-transaction\",\n bearerToken,\n body: {\n publicKey: wallet.publicKey,\n typedData,\n userSignature: normalizedSig,\n saveToDatabase,\n walletType: resolvedWalletType ?? walletType,\n },\n });\n\n if (!result || typeof result !== \"object\") {\n throw new Error(\n \"Invalid response from /transactions/execute-sponsored-transaction: response is not an object\"\n );\n }\n\n const apiMessage =\n typeof (result as any).message === \"string\" ? (result as any).message : undefined;\n\n if (typeof result.transactionHash !== \"string\" || result.transactionHash.trim().length === 0) {\n throw new Error(apiMessage ?? \"Response does not contain transaction hash\");\n }\n\n return result.transactionHash;\n } catch (error) {\n console.error(\"Error sending transaction with paymaster\", error);\n throw error;\n }\n};\n\n// Internal type for session-based paymaster transactions\ninterface ExecutePaymasterTransactionWithSessionParams {\n encryptKey: string;\n wallet: WalletData;\n session: SessionKeyData;\n calls: Call[];\n saveToDatabase: boolean;\n}\n\n/**\n * Execute a paymaster transaction (gasless) using a session key.\n *\n * This function uses the 4-element session signature format:\n * [sessionPubKey, r, s, validUntil]\n *\n * The session key must be registered on the contract before use via\n * `addSessionKeyToContract()`.\n *\n * CHIPI wallets only - will throw if wallet type is not CHIPI.\n *\n * @internal\n */\nexport const executePaymasterTransactionWithSession = async ({\n params,\n bearerToken,\n client,\n}: {\n params: ExecutePaymasterTransactionWithSessionParams;\n bearerToken: string;\n client: ChipiClient;\n}): Promise<string> => {\n const { encryptKey, wallet, session, calls, saveToDatabase } = params;\n\n // Accept CHIPI v29 + SHHH V8.4 (both embed SNIP-163 sessions; the\n // 4-felt session sig fast path at account.cairo:389 runs the same\n // on either class).\n if (wallet.walletType !== \"CHIPI\" && wallet.walletType !== \"SHHH\") {\n console.error(\"[ChipiSDK:Session:Execute] Invalid wallet type for session execution\", {\n provided: wallet.walletType,\n required: \"CHIPI or SHHH\",\n walletAddress: wallet.publicKey?.slice(0, 15) + \"...\",\n hint: \"Session keys require a SNIP-163 sessions component (CHIPI v29 or SHHH V8.4).\",\n });\n throw new ChipiSessionError(\n `Session execution requires CHIPI or SHHH wallet type. Got: \"${wallet.walletType || \"undefined\"}\"`,\n SESSION_ERRORS.INVALID_WALLET_TYPE_FOR_SESSION\n );\n }\n\n // Check if session has expired\n const nowSeconds = Math.floor(Date.now() / 1000);\n if (session.validUntil < nowSeconds) {\n console.error(\"[ChipiSDK:Session:Execute] Session has expired\", {\n sessionExpiry: new Date(session.validUntil * 1000).toISOString(),\n currentTime: new Date(nowSeconds * 1000).toISOString(),\n expiredAgo: `${nowSeconds - session.validUntil} seconds`,\n });\n throw new ChipiSessionError(\n `Session expired at ${new Date(session.validUntil * 1000).toISOString()}. ` +\n `Create a new session key.`,\n SESSION_ERRORS.SESSION_EXPIRED\n );\n }\n\n try {\n // console.log(\"[ChipiSDK:Session:Execute] Starting session transaction\", {\n // walletAddress: wallet.publicKey.slice(0, 15) + \"...\",\n // sessionPubKey: session.publicKey.slice(0, 15) + \"...\",\n // sessionExpiry: new Date(session.validUntil * 1000).toISOString(),\n // callCount: calls.length,\n // });\n\n // Decrypt the session private key\n let sessionPrivateKey: string;\n try {\n sessionPrivateKey = decryptPrivateKey(session.encryptedPrivateKey, encryptKey);\n } catch {\n console.error(\"[ChipiSDK:Session:Execute] Failed to decrypt session private key\", {\n sessionPubKey: session.publicKey?.slice(0, 15) + \"...\",\n hint: \"Ensure the encryptKey matches the one used when creating the session\",\n });\n throw new ChipiSessionError(\n \"Failed to decrypt session private key. Verify the encryptKey is correct.\",\n SESSION_ERRORS.SESSION_DECRYPTION_FAILED\n );\n }\n\n // Verify the session private key matches the public key\n const derivedPubKey = ec.starkCurve.getStarkKey(sessionPrivateKey);\n if (derivedPubKey.toLowerCase() !== session.publicKey.toLowerCase()) {\n console.error(\"[ChipiSDK:Session:Execute] Session key mismatch\", {\n expected: session.publicKey.slice(0, 15) + \"...\",\n derived: derivedPubKey.slice(0, 15) + \"...\",\n hint: \"The encrypted private key does not match the stored public key\",\n });\n throw new ChipiSessionError(\n \"Session key mismatch: decrypted private key does not match the public key\",\n SESSION_ERRORS.SESSION_DECRYPTION_FAILED\n );\n }\n\n if (wallet.walletType === \"SHHH\") {\n // V8.4 session OE path. Bypass chipi-back's prepare-typed-data +\n // execute-sponsored-transaction (which are shaped for CHIPI v29\n // wallets — they hardcode the CHIPI typedData domain + the\n // `isSessionSignature` flag the CHIPI controller reads). Route\n // the session OE through `/transactions/execute-sponsored-raw`\n // with the 4-felt session sig as the signature span.\n //\n // V8.4's `execute_from_outside_v2` checks `signature.len() == 4`\n // at `account.cairo:389` BEFORE the V2_SNIP12 envelope dispatch,\n // so the session-sig path runs the same way as on CHIPI v29 —\n // session whitelist + spending-policy enforcement + signature\n // verification against the SESSION pubkey (NOT the primary\n // owner), all inside the SNIP-163 component.\n const { buildOutsideExecution, computeOeHash, ANY_CALLER_FELT } = await import(\"./shhh/oe\");\n const { resolveShhhChainId, serializeExecuteFromOutsideV2Calldata } = await import(\n \"./shhh/execute-paymaster-raw\"\n );\n\n const oe = buildOutsideExecution({\n nonce: (BigInt(Date.now()) << 24n) | BigInt(Math.floor(Math.random() * 0xffffff)),\n calls,\n caller: ANY_CALLER_FELT,\n });\n\n const chainId = resolveShhhChainId({ baseUrl: client.baseUrl });\n const messageHash = computeOeHash(oe, wallet.publicKey, chainId);\n\n // ec.starkCurve.sign expects a Hex string (0x-prefixed). The\n // V8.4 OE message hash from computeOeHash is a bigint; convert.\n const messageHashHex = \"0x\" + messageHash.toString(16);\n const { r, s } = ec.starkCurve.sign(messageHashHex, sessionPrivateKey);\n\n // 4-felt session signature span: [session_pubkey, r, s, valid_until].\n // The wallet reads sig[0] as session_pubkey, enforces the whitelist\n // + spending policy, and validates (r, s) against session_pubkey\n // via the SNIP-163 component (NOT the primary signer's verifier\n // class). See `sessions-smart-contract/src/account.cairo:387-433`\n // and `shhh-wallet-cairo/src/account.cairo:389-405`.\n const sessionSignature: bigint[] = [\n BigInt(session.publicKey),\n BigInt(r),\n BigInt(s),\n BigInt(session.validUntil),\n ];\n\n const calldata = serializeExecuteFromOutsideV2Calldata({\n outsideExecution: oe,\n signature: sessionSignature,\n });\n\n const response = await client.post<{\n transactionHash?: string;\n id?: string;\n }>({\n endpoint: \"/transactions/execute-sponsored-raw\",\n bearerToken,\n body: {\n accountAddress: wallet.publicKey,\n calldata,\n // Gas budget for the session-sig path is the same as STARK\n // primary (~2M l2_gas). The paymaster's kind-detection\n // branch reads signature[0] == 'V2_SNIP12' to look up the\n // per-kind gas overhead; signature[0] here is the\n // session_pubkey (not the V2_SNIP12 prefix), so the\n // detection falls through to the STARK default — which is\n // correct, since the session verifier is STARK ECDSA.\n signerKind: \"STARK\" as const,\n },\n });\n\n if (!response?.transactionHash) {\n throw new ChipiSessionError(\n \"execute-sponsored-raw response missing transactionHash. Check chipi-back logs for upstream paymaster errors.\",\n SESSION_ERRORS.SESSION_INVALID_RESPONSE\n );\n }\n return response.transactionHash;\n }\n\n // CHIPI v29 path (unchanged).\n const accountClassHash = WALLET_CLASS_HASHES.CHIPI;\n\n // Build the type data via Chipi's backend\n const prepareResponse = await client.post<PrepareTypedDataResponse>({\n endpoint: \"/transactions/prepare-typed-data\",\n bearerToken,\n body: {\n publicKey: wallet.publicKey,\n calls: calls,\n accountClassHash,\n walletType: \"CHIPI\",\n },\n });\n\n // Unwrap the response — backend returns { typedData, walletType } for CHIPI wallets\n if (!prepareResponse || typeof prepareResponse !== \"object\") {\n throw new ChipiSessionError(\n \"Invalid response from /transactions/prepare-typed-data: response is not an object\",\n SESSION_ERRORS.SESSION_INVALID_RESPONSE\n );\n }\n\n let sessionTypedData: TypedData;\n if (\"typedData\" in prepareResponse && (prepareResponse as PrepareTypedDataResponse).typedData) {\n const candidate = (prepareResponse as PrepareTypedDataResponse).typedData;\n if (\n !candidate ||\n typeof candidate !== \"object\" ||\n !(\"types\" in candidate) ||\n !(\"message\" in candidate)\n ) {\n throw new ChipiSessionError(\n \"Invalid response from /transactions/prepare-typed-data: typedData structure is invalid\",\n SESSION_ERRORS.SESSION_INVALID_RESPONSE\n );\n }\n sessionTypedData = candidate;\n } else if (\"types\" in prepareResponse && \"message\" in prepareResponse) {\n sessionTypedData = prepareResponse as unknown as TypedData;\n } else {\n throw new ChipiSessionError(\n \"Invalid response from /transactions/prepare-typed-data: typedData is missing\",\n SESSION_ERRORS.SESSION_INVALID_RESPONSE\n );\n }\n\n // Compute the message hash from typed data\n const msgHash = typedData.getMessageHash(sessionTypedData, wallet.publicKey);\n\n // Sign with session private key using ECDSA\n const { r, s } = ec.starkCurve.sign(msgHash, sessionPrivateKey);\n\n // Build 4-element session signature: [sessionPubKey, r, s, validUntil]\n const sessionSignature = [\n session.publicKey,\n num.toHex(r),\n num.toHex(s),\n num.toHex(session.validUntil),\n ];\n\n // Execute the sponsored transaction via Chipi's paymaster\n const result = await client.post<ExecuteSponsoredTransactionResponse>({\n endpoint: \"/transactions/execute-sponsored-transaction\",\n bearerToken,\n body: {\n publicKey: wallet.publicKey,\n typedData: sessionTypedData,\n // Session signature format - array of 4 hex strings\n userSignature: sessionSignature,\n saveToDatabase: saveToDatabase,\n walletType: \"CHIPI\",\n isSessionSignature: true, // Flag to indicate session signature format\n },\n });\n\n if (!result || typeof result !== \"object\") {\n throw new ChipiSessionError(\n \"Invalid response from /transactions/execute-sponsored-transaction: response is not an object\",\n SESSION_ERRORS.SESSION_INVALID_RESPONSE\n );\n }\n\n if (typeof result.transactionHash !== \"string\" || result.transactionHash.trim().length === 0) {\n const apiMessage =\n typeof (result as any).message === \"string\" ? (result as any).message : undefined;\n console.error(\"[ChipiSDK:Session:Execute] No transaction hash in response\", {\n result,\n });\n throw new ChipiSessionError(\n apiMessage ?? \"Response does not contain transaction hash\",\n SESSION_ERRORS.SESSION_INVALID_RESPONSE\n );\n }\n\n return result.transactionHash;\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n\n // Re-throw ChipiSessionError as-is\n if (error instanceof ChipiSessionError) {\n throw error;\n }\n\n console.error(\"[ChipiSDK:Session:Execute] Unexpected error\", {\n error: err.message,\n walletAddress: wallet.publicKey?.slice(0, 15) + \"...\",\n sessionPubKey: session.publicKey?.slice(0, 15) + \"...\",\n });\n throw error;\n }\n};\n","import type {\n PaymasterAdapter,\n PaymasterAccount,\n PrepareTypedDataResponse,\n ExecuteSponsoredTransactionResponse,\n WalletType,\n} from \"@chipi-stack/types\";\nimport type { Call, TypedData } from \"starknet\";\nimport { WALLET_CLASS_HASHES, getWalletTypeFromClassHash } from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\n\n/**\n * Unwrap the /prepare-typed-data response.\n * Supports both current shape ({ typedData, walletType }) and legacy (raw TypedData).\n */\nfunction unwrapPrepareResponse(response: unknown): {\n typedData: TypedData;\n walletType?: WalletType;\n} {\n if (!response || typeof response !== \"object\") {\n throw new Error(\n \"Invalid response from /transactions/prepare-typed-data: response is not an object\"\n );\n }\n\n // Current shape: { typedData: TypedData, walletType: WalletType }\n if (\"typedData\" in response) {\n const resp = response as PrepareTypedDataResponse;\n if (\n !resp.typedData ||\n typeof resp.typedData !== \"object\" ||\n !(\"types\" in resp.typedData) ||\n !(\"message\" in resp.typedData)\n ) {\n throw new Error(\n \"Invalid response from /transactions/prepare-typed-data: typedData structure is invalid\"\n );\n }\n return { typedData: resp.typedData, walletType: resp.walletType };\n }\n\n // Legacy shape: the response itself is the TypedData\n if (\"types\" in response && \"message\" in response) {\n return { typedData: response as TypedData };\n }\n\n // Possibly an API error shape\n const apiMessage =\n typeof (response as any).message === \"string\" ? (response as any).message : undefined;\n\n throw new Error(\n apiMessage ?? \"Invalid response from /transactions/prepare-typed-data: typedData is missing\"\n );\n}\n\n/**\n * Normalize signature to { r: string, s: string } format.\n * Handles WeierstrassSignatureType ({r, s} as bigint) and ArraySignatureType ([r, s]).\n */\nexport function normalizeSignature(signature: unknown): { r: string; s: string } {\n // ArraySignatureType: string[]\n if (Array.isArray(signature)) {\n if (signature.length < 2) {\n throw new Error(`Array signature must have at least 2 elements, got ${signature.length}`);\n }\n const r = signature[0];\n const s = signature[1];\n if (r == null || s == null) {\n throw new Error(`Signature elements must not be null/undefined. Got r=${r}, s=${s}`);\n }\n return { r: String(r), s: String(s) };\n }\n\n // WeierstrassSignatureType: { r: bigint, s: bigint }\n if (signature && typeof signature === \"object\" && \"r\" in signature && \"s\" in signature) {\n const sig = signature as { r: unknown; s: unknown; recovery?: unknown };\n if (sig.r == null || sig.s == null) {\n throw new Error(`Object signature has null/undefined fields. Got r=${sig.r}, s=${sig.s}`);\n }\n return {\n r: String(sig.r),\n s: String(sig.s),\n };\n }\n\n throw new Error(\"Unrecognized signature format from account.signMessage()\");\n}\n\n/**\n * PaymasterAdapter implementation that uses Chipi's backend API for gasless transactions.\n *\n * Flow:\n * 1. Detect wallet type from on-chain class hash\n * 2. Call /transactions/prepare-typed-data to get TypedData\n * 3. Call account.signMessage(typedData) — triggers wallet popup for external wallets\n * 4. Call /transactions/execute-sponsored-transaction with signature\n * 5. Return transaction hash\n */\nexport class ChipiPaymasterAdapter implements PaymasterAdapter {\n private readonly client: ChipiClient;\n private readonly bearerToken: string;\n\n constructor(client: ChipiClient, bearerToken: string) {\n this.client = client;\n this.bearerToken = bearerToken;\n }\n\n async execute({ account, calls }: { account: PaymasterAccount; calls: Call[] }): Promise<string> {\n // 1. Detect wallet type from on-chain class hash\n const classHash = await account.getClassHashAt(account.address);\n const walletType = getWalletTypeFromClassHash(classHash);\n\n // Use mapped class hash for known types, on-chain hash for custom accounts.\n // The Rust paymaster auto-detects the interface version (V1/V2) from the class,\n // so custom SNIP-9 accounts work without an allowlist.\n const accountClassHash = walletType ? WALLET_CLASS_HASHES[walletType] : classHash;\n\n // 2. Prepare typed data via Chipi's backend\n const prepareResponse = await this.client.post<PrepareTypedDataResponse>({\n endpoint: \"/transactions/prepare-typed-data\",\n bearerToken: this.bearerToken,\n body: {\n publicKey: account.address,\n calls,\n accountClassHash,\n },\n });\n\n // 3. Unwrap response\n const { typedData, walletType: resolvedWalletType } = unwrapPrepareResponse(prepareResponse);\n\n // 4. Sign — triggers wallet popup for Argent X / Braavos\n const signature = await account.signMessage(typedData);\n\n // 5. Normalize signature format\n const normalizedSig = normalizeSignature(signature);\n\n // 6. Execute sponsored transaction\n const result = await this.client.post<ExecuteSponsoredTransactionResponse>({\n endpoint: \"/transactions/execute-sponsored-transaction\",\n bearerToken: this.bearerToken,\n body: {\n publicKey: account.address,\n typedData,\n userSignature: normalizedSig,\n walletType: resolvedWalletType ?? walletType,\n saveToDatabase: true,\n },\n });\n\n if (!result || typeof result !== \"object\") {\n throw new Error(\n \"Invalid response from /transactions/execute-sponsored-transaction: response is not an object\"\n );\n }\n\n const apiMessage =\n typeof (result as any).message === \"string\" ? (result as any).message : undefined;\n\n if (!result.transactionHash) {\n throw new Error(apiMessage ?? \"Response does not contain transaction hash\");\n }\n\n return result.transactionHash;\n }\n}\n","/**\n * SHHH V8.4 migration builders — sessions / OZ → V8.4.\n *\n * Wave B PR B4. Covers two paths:\n *\n * 1. Happy-path atomic multicall (`buildMigrationCalls`)\n * Used when the legacy wallet is still operational and can sign\n * an OutsideExecution. The wallet emits one OE containing two\n * calls in order:\n *\n * upgrade(SHHH_ACCOUNT_CLASS_HASH)\n * bootstrap_from_sessions(public_key, STARK_VERIFIER_CLASS_HASH, label)\n *\n * Atomicity is the security property — between `upgrade` and\n * `bootstrap_from_sessions` an attacker watching the mempool\n * could call `bootstrap_from_sessions(attacker_pk, ...)` first\n * and seize the account. The OE bundles them so that race\n * window doesn't exist.\n *\n * 2. Stranded-state recovery (`buildBootstrapSignedCall` +\n * `computeBootstrapMessage` + `signBootstrapMessage`)\n * Used when the legacy multicall ran but `bootstrap_from_sessions`\n * silently reverted (the old chipi-pay/sessions-smart-contract's\n * `_execute_calls` swallowed subcall errors at a\n * `Result::Err(_) => res.append(array![].span())` site). The\n * account is at V8.4 class but `primary_kind == 0`.\n *\n * Recovery: anyone calls\n *\n * bootstrap_from_sessions_signed(\n * public_key, stark_verifier_class, label, signature_r, signature_s\n * )\n *\n * where (r, s) is a STARK ECDSA signature over\n *\n * bootstrap_msg = poseidon([\n * 'SHHH_BOOTSTRAP_V8_4',\n * account_address,\n * public_key,\n * stark_verifier_class,\n * label,\n * ])\n *\n * Authorization comes from two independent checks on-chain:\n * - the preserved `LEGACY_OZ_ACCOUNT_PUBKEY_SLOT` storage slot\n * must equal `public_key` (audit C-1 binding to the original\n * owner)\n * - the signature must verify under `public_key`\n *\n * So this entrypoint is callable from any sponsor / relayer\n * without a self-call channel, but is non-transferable: only the\n * original owner who controls the STARK private key can produce\n * a valid signature.\n *\n * Cairo source of truth:\n * shhh-wallet-cairo/src/account.cairo:1024 bootstrap_from_sessions\n * shhh-wallet-cairo/src/account.cairo:1113 bootstrap_from_sessions_signed\n * shhh-wallet-cairo/scripts/ts/migrate-sessions-wallet.ts (happy-path ref)\n *\n * What this module does NOT cover (out of scope for B4):\n * - The upstream OE-build + sign step. Caller wraps these `Call`s\n * with `buildOutsideExecution` + `buildStarkEnvelope` and submits\n * via the paymaster. Same flow as B1's governance builders.\n * - Verifying on-chain whether a wallet is stranded. Done by the\n * dashboard / UI; here we just produce the recovery call.\n */\n\nimport { ec, hash, shortString, type Call } from \"starknet\";\n\n// ================================================================\n// Constants — MUST match shhh-wallet-cairo/src/account.cairo exactly\n// ================================================================\n\n/**\n * Domain separator the on-chain signed-bootstrap message uses as its\n * first felt. Mirrors `'SHHH_BOOTSTRAP_V8_4'` literal at\n * `src/account.cairo:1170`.\n */\nexport const BOOTSTRAP_DOMAIN_SEPARATOR = BigInt(\n shortString.encodeShortString(\"SHHH_BOOTSTRAP_V8_4\")\n);\n\n/** Cairo Account-component `upgrade` entrypoint name (OpenZeppelin v3). */\nconst UPGRADE_ENTRYPOINT = \"upgrade\";\n\n/** `src/account.cairo:1026` */\nconst BOOTSTRAP_ENTRYPOINT = \"bootstrap_from_sessions\";\n\n/** `src/account.cairo:1114` */\nconst BOOTSTRAP_SIGNED_ENTRYPOINT = \"bootstrap_from_sessions_signed\";\n\n// ================================================================\n// Helpers\n// ================================================================\n\nfunction _asHex(x: string | bigint): string {\n return typeof x === \"bigint\" ? \"0x\" + x.toString(16) : x;\n}\n\nfunction _felt(x: bigint | string | number): bigint {\n if (typeof x === \"bigint\") return x;\n return BigInt(x);\n}\n\n/**\n * Encode a label string as a Starknet short-string felt252.\n *\n * starknet.js `shortString.encodeShortString` accepts only ASCII\n * characters and rejects strings longer than 31 characters (it\n * stuffs one character per byte, and felt252 has 251 useful bits).\n * We pre-validate here so callers get a clear error instead of the\n * generic encoder revert; the previous \"≤31 bytes\" message was\n * misleading because non-ASCII strings would silently slip past a\n * UTF-16 `.length` check.\n */\nexport function labelToFelt(label: string): bigint {\n if (label.length === 0) return 0n;\n // ASCII check via charCodeAt rather than `/^[\\x00-\\x7F]*$/` to keep\n // ESLint's `no-control-regex` happy. Equivalent semantics: every\n // UTF-16 code unit must fit in 7 bits.\n for (let i = 0; i < label.length; i++) {\n if (label.charCodeAt(i) > 0x7f) {\n throw new RangeError(\n `migrate label must be ASCII-only (starknet.js short-string encoder rejects non-ASCII)`\n );\n }\n }\n if (label.length > 31) {\n throw new RangeError(\n `migrate label must fit in felt252 short-string (≤31 ASCII characters); got ${label.length}`\n );\n }\n return BigInt(shortString.encodeShortString(label));\n}\n\n// ================================================================\n// Happy path: atomic multicall\n// ================================================================\n\nexport interface BuildMigrationCallsParams {\n /** Address of the legacy CHIPI v29 / sessions wallet being migrated. */\n legacyAccountAddress: bigint | string;\n /** STARK public key currently stored in the legacy wallet's OZ slot. */\n legacyPublicKey: bigint | string;\n /** Target SHHH ShhhAccount class hash (e.g. `WALLET_CLASS_HASHES.SHHH`). */\n shhhAccountClassHash: bigint | string;\n /** STARK verifier class hash (`SHHH_VERIFIER_CLASS_HASHES.STARK`). */\n starkVerifierClassHash: bigint | string;\n /** Optional user-facing tag bound into the V8.4 owner record (≤31 ASCII characters). */\n label?: string;\n}\n\n/**\n * Build the two-call atomic multicall that migrates a legacy\n * CHIPI v29 (chipi-pay/sessions-smart-contract) wallet to SHHH V8.4.\n *\n * Caller's responsibility:\n * 1. Wrap the returned `Call[]` in a single `OutsideExecution`\n * against the legacy wallet's address.\n * 2. Sign the OE with the legacy wallet's STARK key (the SDK's\n * legacy CHIPI flow already does this; use that signer here).\n * 3. Submit via the paymaster.\n *\n * The multicall MUST be atomic — if any call between the two could\n * land independently on chain (e.g. via mempool front-running), the\n * resulting `upgrade`-but-not-`bootstrap` state is exploitable.\n * Bundling them in a single OE makes both calls land atomically.\n *\n * Returns the calls in order: [upgrade, bootstrap_from_sessions].\n */\nexport function buildMigrationCalls(params: BuildMigrationCallsParams): Call[] {\n const upgradeCall: Call = {\n contractAddress: _asHex(params.legacyAccountAddress),\n entrypoint: UPGRADE_ENTRYPOINT,\n calldata: [_asHex(params.shhhAccountClassHash)],\n };\n\n const labelFelt = labelToFelt(params.label ?? \"migrated\");\n\n const bootstrapCall: Call = {\n contractAddress: _asHex(params.legacyAccountAddress),\n entrypoint: BOOTSTRAP_ENTRYPOINT,\n calldata: [\n _asHex(params.legacyPublicKey),\n _asHex(params.starkVerifierClassHash),\n \"0x\" + labelFelt.toString(16),\n ],\n };\n\n return [upgradeCall, bootstrapCall];\n}\n\n// ================================================================\n// Stranded-state recovery: signed bootstrap\n// ================================================================\n\nexport interface BootstrapMessageParams {\n /** The (already-upgraded) SHHH wallet's contract address. */\n accountAddress: bigint | string;\n /** STARK public key originally stored in the legacy OZ slot. */\n publicKey: bigint | string;\n /** STARK verifier class hash to set as primary. */\n starkVerifierClassHash: bigint | string;\n /** User-facing tag bound into the V8.4 owner record (≤31 ASCII characters). */\n label?: string;\n}\n\n/**\n * Compute the canonical bootstrap message the on-chain\n * `bootstrap_from_sessions_signed` verifier will check the STARK\n * ECDSA signature against:\n *\n * poseidon([\n * 'SHHH_BOOTSTRAP_V8_4',\n * account_address,\n * public_key,\n * stark_verifier_class,\n * label,\n * ])\n *\n * Domain separation via the literal prefix means a signature produced\n * for this message can never be replayed as a different intent\n * (audit point — covered in `audit_poc_attacker_can_seize_any_stranded_wallet`).\n */\nexport function computeBootstrapMessage(params: BootstrapMessageParams): bigint {\n const accountFelt = _felt(params.accountAddress);\n const pubkeyFelt = _felt(params.publicKey);\n const verifierFelt = _felt(params.starkVerifierClassHash);\n const labelFelt = labelToFelt(params.label ?? \"migrated\");\n\n return BigInt(\n hash.computePoseidonHashOnElements([\n \"0x\" + BOOTSTRAP_DOMAIN_SEPARATOR.toString(16),\n \"0x\" + accountFelt.toString(16),\n \"0x\" + pubkeyFelt.toString(16),\n \"0x\" + verifierFelt.toString(16),\n \"0x\" + labelFelt.toString(16),\n ])\n );\n}\n\n/**\n * Sign the canonical bootstrap message with a STARK private key.\n *\n * Used in two situations:\n * - Server-side test fixtures + the Wave B B9 mainnet smoke\n * - \"Recovery as a service\" flows where the SDK is allowed to hold\n * a temporary STARK key custodied for the user\n *\n * Most production flows will instead sign via the user's wallet\n * (e.g. by exposing the message hash to the legacy wallet's STARK\n * signing endpoint) and pass `(r, s)` directly to\n * `buildBootstrapSignedCall`.\n */\nexport function signBootstrapMessage(params: {\n privateKey: bigint | string;\n message: BootstrapMessageParams;\n}): { r: bigint; s: bigint } {\n const messageHash = computeBootstrapMessage(params.message);\n const pkHex = _asHex(_felt(params.privateKey));\n const sig = ec.starkCurve.sign(\"0x\" + messageHash.toString(16), pkHex);\n return {\n r: BigInt(\"0x\" + sig.r.toString(16)),\n s: BigInt(\"0x\" + sig.s.toString(16)),\n };\n}\n\nexport interface BuildBootstrapSignedCallParams {\n /** The stranded SHHH wallet's contract address. */\n accountAddress: bigint | string;\n /** STARK public key originally stored in the legacy OZ slot. */\n publicKey: bigint | string;\n /** STARK verifier class hash. Must match the value the original migration intended. */\n starkVerifierClassHash: bigint | string;\n /** User-facing tag (≤31 ASCII characters). Must match the original migration intent. */\n label?: string;\n /** STARK ECDSA signature r component over `computeBootstrapMessage(...)`. */\n signatureR: bigint | string;\n /** STARK ECDSA signature s component. */\n signatureS: bigint | string;\n}\n\n/**\n * Build a `bootstrap_from_sessions_signed(public_key, stark_verifier_class,\n * label, signature_r, signature_s)` call against a stranded SHHH wallet.\n *\n * Permissionless on-chain — anyone (typically a sponsor / relayer)\n * can dispatch this call. The signature is the auth. So the integrator\n * does NOT need to wrap it in an OE from the wallet itself; it goes\n * out as a regular invoke from any STARK account.\n *\n * Returns the `Call` for the integrator's account-abstraction layer\n * (e.g. a chipi-back service account, or a one-off operator wallet).\n */\nexport function buildBootstrapSignedCall(params: BuildBootstrapSignedCallParams): Call {\n const labelFelt = labelToFelt(params.label ?? \"migrated\");\n return {\n contractAddress: _asHex(params.accountAddress),\n entrypoint: BOOTSTRAP_SIGNED_ENTRYPOINT,\n calldata: [\n _asHex(params.publicKey),\n _asHex(params.starkVerifierClassHash),\n \"0x\" + labelFelt.toString(16),\n _asHex(params.signatureR),\n _asHex(params.signatureS),\n ],\n };\n}\n"]}