@chipi-stack/backend 14.2.1 → 14.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -0
- package/dist/index.mjs.map +1 -1
- package/dist/skuPurchases.js +12 -0
- package/dist/skuPurchases.js.map +1 -1
- package/dist/skuPurchases.mjs +12 -0
- package/dist/skuPurchases.mjs.map +1 -1
- package/dist/transactions.d.mts +7 -1
- package/dist/transactions.d.ts +7 -1
- package/dist/transactions.js +12 -0
- package/dist/transactions.js.map +1 -1
- package/dist/transactions.mjs +12 -0
- package/dist/transactions.mjs.map +1 -1
- package/dist/wallets.d.mts +12 -0
- package/dist/wallets.d.ts +12 -0
- package/dist/wallets.js +17 -0
- package/dist/wallets.js.map +1 -1
- package/dist/wallets.mjs +17 -0
- package/dist/wallets.mjs.map +1 -1
- package/package.json +3 -3
package/dist/transactions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/encryption.ts","../src/paymaster-adapter.ts","../src/execute-paymaster-transaction.ts","../src/transactions.ts"],"names":["CryptoES","WALLET_RPC_ENDPOINTS","WALLET_CLASS_HASHES","RpcProvider","Account","typedData","apiMessage","STARKNET_CONTRACTS","formatAmount","API_ENDPOINTS"],"mappings":"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","file":"transactions.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 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","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 // 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 // Validate CHIPI wallet - session keys only work with CHIPI wallets\n if (wallet.walletType !== \"CHIPI\") {\n console.error(\"[ChipiSDK:Session:Execute] Invalid wallet type for session execution\", {\n provided: wallet.walletType,\n required: \"CHIPI\",\n expectedClassHash: WALLET_CLASS_HASHES.CHIPI,\n walletAddress: wallet.publicKey?.slice(0, 15) + \"...\",\n hint: \"Session keys only work with CHIPI wallets (SNIP-9 compatible)\",\n });\n throw new ChipiSessionError(\n `Session execution requires CHIPI 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 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 {\n CallAnyContractParams,\n STARKNET_CONTRACTS,\n WalletData,\n type ExecuteTransactionParams,\n type RecordSendTransactionParams,\n type Transaction,\n type TransferParams,\n type GetTransactionListQuery,\n type PaginatedResponse,\n type TransactionStatusResponse,\n} from \"@chipi-stack/types\";\nimport { API_ENDPOINTS, formatAmount } from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport { executePaymasterTransaction } from \"./execute-paymaster-transaction\";\n\n/**\n * Transaction management utilities\n */\nexport class ChipiTransactions {\n constructor(private client: ChipiClient) {}\n /**\n * Execute a gasless transaction using paymaster\n * @internal saveToDatabase - Internal only, not exposed to clients\n */\n async executeTransaction({\n params,\n bearerToken,\n saveToDatabase = true, // Internal only - defaults to true, can be overridden internally\n }: {\n params: ExecuteTransactionParams;\n bearerToken: string;\n saveToDatabase?: boolean; // Internal only - not part of public API\n }): Promise<string> {\n return executePaymasterTransaction({\n params: {\n ...params,\n saveToDatabase,\n },\n bearerToken,\n client: this.client,\n });\n }\n\n /**\n * Transfer tokens\n */\n async transfer({\n params,\n bearerToken,\n }: {\n params: TransferParams;\n bearerToken: string;\n }): Promise<string> {\n const { encryptKey, wallet, token, otherToken, recipient, amount } = params;\n const contract = STARKNET_CONTRACTS[token];\n let contractAddress = contract.contractAddress;\n let decimals = contract.decimals;\n if (token === \"OTHER\") {\n if (!otherToken) {\n throw new Error(\"Other token is required when token is OTHER\");\n }\n contractAddress = otherToken.contractAddress;\n decimals = otherToken.decimals;\n }\n const formattedAmount = formatAmount(amount, decimals);\n return this.executeTransaction({\n params: {\n encryptKey,\n wallet,\n calls: [\n {\n contractAddress,\n entrypoint: \"transfer\",\n calldata: [recipient, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken,\n });\n }\n\n /**\n * Approve token spending\n */\n async approve(params: {\n encryptKey: string;\n wallet: WalletData;\n contractAddress: string;\n spender: string;\n amount: string;\n decimals?: number;\n bearerToken: string;\n }): Promise<string> {\n const formattedAmount = formatAmount(params.amount, params.decimals);\n\n return this.executeTransaction({\n params: {\n encryptKey: params.encryptKey,\n wallet: params.wallet,\n calls: [\n {\n contractAddress: params.contractAddress,\n entrypoint: \"approve\",\n calldata: [params.spender, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken: params.bearerToken,\n });\n }\n\n /**\n * Call any contract method\n */\n // {\n // encryptKey: string;\n // wallet: any;\n // calls: any[];\n // bearerToken: string;\n // }\n\n async callAnyContract({\n params,\n bearerToken,\n }: {\n params: CallAnyContractParams;\n bearerToken: string;\n }): Promise<string> {\n return this.executeTransaction({\n params,\n bearerToken,\n });\n }\n\n /**\n * Record a send transaction\n */\n async recordSendTransaction({\n params,\n bearerToken,\n }: {\n params: RecordSendTransactionParams;\n bearerToken: string;\n }): Promise<Transaction> {\n const response = await this.client.post<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/record-send`,\n bearerToken,\n body: params,\n });\n return response;\n }\n\n /**\n * Get paginated transaction history\n */\n async getTransactionList(\n query: GetTransactionListQuery,\n bearerToken: string\n ): Promise<PaginatedResponse<Transaction>> {\n const response = await this.client.get<PaginatedResponse<Transaction>>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/transaction-list`,\n params: query,\n bearerToken,\n });\n return response;\n }\n\n /**\n * Get a single transaction by hash or internal ID\n */\n async getTransaction(hashOrId: string, bearerToken: string): Promise<Transaction> {\n return this.client.get<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hashOrId}`,\n bearerToken,\n });\n }\n\n /**\n * Get on-chain transaction status\n */\n async getTransactionStatus(hash: string, bearerToken: string): Promise<TransactionStatusResponse> {\n return this.client.get<TransactionStatusResponse>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hash}/status`,\n bearerToken,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/encryption.ts","../src/paymaster-adapter.ts","../src/execute-paymaster-transaction.ts","../src/transactions.ts"],"names":["CryptoES","WALLET_RPC_ENDPOINTS","WALLET_CLASS_HASHES","RpcProvider","Account","typedData","apiMessage","STARKNET_CONTRACTS","formatAmount","API_ENDPOINTS"],"mappings":"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","file":"transactions.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 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","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 // 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 // Validate CHIPI wallet - session keys only work with CHIPI wallets\n if (wallet.walletType !== \"CHIPI\") {\n console.error(\"[ChipiSDK:Session:Execute] Invalid wallet type for session execution\", {\n provided: wallet.walletType,\n required: \"CHIPI\",\n expectedClassHash: WALLET_CLASS_HASHES.CHIPI,\n walletAddress: wallet.publicKey?.slice(0, 15) + \"...\",\n hint: \"Session keys only work with CHIPI wallets (SNIP-9 compatible)\",\n });\n throw new ChipiSessionError(\n `Session execution requires CHIPI 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 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 {\n CallAnyContractParams,\n STARKNET_CONTRACTS,\n WalletData,\n type ExecuteTransactionParams,\n type RecordSendTransactionParams,\n type Transaction,\n type TransferParams,\n type GetTransactionListQuery,\n type PaginatedResponse,\n type TransactionStatusResponse,\n type SyncOnChainTransfersParams,\n type SyncOnChainTransfersResponse,\n} from \"@chipi-stack/types\";\nimport { API_ENDPOINTS, formatAmount } from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport { executePaymasterTransaction } from \"./execute-paymaster-transaction\";\n\n/**\n * Transaction management utilities\n */\nexport class ChipiTransactions {\n constructor(private client: ChipiClient) {}\n /**\n * Execute a gasless transaction using paymaster\n * @internal saveToDatabase - Internal only, not exposed to clients\n */\n async executeTransaction({\n params,\n bearerToken,\n saveToDatabase = true, // Internal only - defaults to true, can be overridden internally\n }: {\n params: ExecuteTransactionParams;\n bearerToken: string;\n saveToDatabase?: boolean; // Internal only - not part of public API\n }): Promise<string> {\n return executePaymasterTransaction({\n params: {\n ...params,\n saveToDatabase,\n },\n bearerToken,\n client: this.client,\n });\n }\n\n /**\n * Transfer tokens\n */\n async transfer({\n params,\n bearerToken,\n }: {\n params: TransferParams;\n bearerToken: string;\n }): Promise<string> {\n const { encryptKey, wallet, token, otherToken, recipient, amount } = params;\n const contract = STARKNET_CONTRACTS[token];\n let contractAddress = contract.contractAddress;\n let decimals = contract.decimals;\n if (token === \"OTHER\") {\n if (!otherToken) {\n throw new Error(\"Other token is required when token is OTHER\");\n }\n contractAddress = otherToken.contractAddress;\n decimals = otherToken.decimals;\n }\n const formattedAmount = formatAmount(amount, decimals);\n return this.executeTransaction({\n params: {\n encryptKey,\n wallet,\n calls: [\n {\n contractAddress,\n entrypoint: \"transfer\",\n calldata: [recipient, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken,\n });\n }\n\n /**\n * Approve token spending\n */\n async approve(params: {\n encryptKey: string;\n wallet: WalletData;\n contractAddress: string;\n spender: string;\n amount: string;\n decimals?: number;\n bearerToken: string;\n }): Promise<string> {\n const formattedAmount = formatAmount(params.amount, params.decimals);\n\n return this.executeTransaction({\n params: {\n encryptKey: params.encryptKey,\n wallet: params.wallet,\n calls: [\n {\n contractAddress: params.contractAddress,\n entrypoint: \"approve\",\n calldata: [params.spender, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken: params.bearerToken,\n });\n }\n\n /**\n * Call any contract method\n */\n // {\n // encryptKey: string;\n // wallet: any;\n // calls: any[];\n // bearerToken: string;\n // }\n\n async callAnyContract({\n params,\n bearerToken,\n }: {\n params: CallAnyContractParams;\n bearerToken: string;\n }): Promise<string> {\n return this.executeTransaction({\n params,\n bearerToken,\n });\n }\n\n /**\n * Record a send transaction\n */\n async recordSendTransaction({\n params,\n bearerToken,\n }: {\n params: RecordSendTransactionParams;\n bearerToken: string;\n }): Promise<Transaction> {\n const response = await this.client.post<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/record-send`,\n bearerToken,\n body: params,\n });\n return response;\n }\n\n /**\n * Get paginated transaction history\n */\n async getTransactionList(\n query: GetTransactionListQuery,\n bearerToken: string\n ): Promise<PaginatedResponse<Transaction>> {\n const response = await this.client.get<PaginatedResponse<Transaction>>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/transaction-list`,\n params: query,\n bearerToken,\n });\n return response;\n }\n\n /**\n * Get a single transaction by hash or internal ID\n */\n async getTransaction(hashOrId: string, bearerToken: string): Promise<Transaction> {\n return this.client.get<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hashOrId}`,\n bearerToken,\n });\n }\n\n /**\n * Get on-chain transaction status\n */\n async getTransactionStatus(hash: string, bearerToken: string): Promise<TransactionStatusResponse> {\n return this.client.get<TransactionStatusResponse>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hash}/status`,\n bearerToken,\n });\n }\n\n /**\n * Sync on-chain transfers from Voyager into the DB (read-through cache).\n * Discovers transactions not made through the SDK (e.g., external USDC receives).\n * After sync, getTransactionList returns the newly discovered transactions.\n */\n async syncOnChainTransfers(\n params: SyncOnChainTransfersParams,\n bearerToken: string,\n ): Promise<SyncOnChainTransfersResponse> {\n return this.client.post<SyncOnChainTransfersResponse>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/sync-on-chain`,\n bearerToken,\n body: params,\n });\n }\n}\n"]}
|
package/dist/transactions.mjs
CHANGED
|
@@ -279,6 +279,18 @@ var ChipiTransactions = class {
|
|
|
279
279
|
bearerToken
|
|
280
280
|
});
|
|
281
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Sync on-chain transfers from Voyager into the DB (read-through cache).
|
|
284
|
+
* Discovers transactions not made through the SDK (e.g., external USDC receives).
|
|
285
|
+
* After sync, getTransactionList returns the newly discovered transactions.
|
|
286
|
+
*/
|
|
287
|
+
async syncOnChainTransfers(params, bearerToken) {
|
|
288
|
+
return this.client.post({
|
|
289
|
+
endpoint: `${API_ENDPOINTS.TRANSACTIONS}/sync-on-chain`,
|
|
290
|
+
bearerToken,
|
|
291
|
+
body: params
|
|
292
|
+
});
|
|
293
|
+
}
|
|
282
294
|
};
|
|
283
295
|
|
|
284
296
|
export { ChipiTransactions };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/encryption.ts","../src/paymaster-adapter.ts","../src/execute-paymaster-transaction.ts","../src/transactions.ts"],"names":["WALLET_CLASS_HASHES","typedData","apiMessage"],"mappings":"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","file":"transactions.mjs","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 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","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 // 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 // Validate CHIPI wallet - session keys only work with CHIPI wallets\n if (wallet.walletType !== \"CHIPI\") {\n console.error(\"[ChipiSDK:Session:Execute] Invalid wallet type for session execution\", {\n provided: wallet.walletType,\n required: \"CHIPI\",\n expectedClassHash: WALLET_CLASS_HASHES.CHIPI,\n walletAddress: wallet.publicKey?.slice(0, 15) + \"...\",\n hint: \"Session keys only work with CHIPI wallets (SNIP-9 compatible)\",\n });\n throw new ChipiSessionError(\n `Session execution requires CHIPI 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 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 {\n CallAnyContractParams,\n STARKNET_CONTRACTS,\n WalletData,\n type ExecuteTransactionParams,\n type RecordSendTransactionParams,\n type Transaction,\n type TransferParams,\n type GetTransactionListQuery,\n type PaginatedResponse,\n type TransactionStatusResponse,\n} from \"@chipi-stack/types\";\nimport { API_ENDPOINTS, formatAmount } from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport { executePaymasterTransaction } from \"./execute-paymaster-transaction\";\n\n/**\n * Transaction management utilities\n */\nexport class ChipiTransactions {\n constructor(private client: ChipiClient) {}\n /**\n * Execute a gasless transaction using paymaster\n * @internal saveToDatabase - Internal only, not exposed to clients\n */\n async executeTransaction({\n params,\n bearerToken,\n saveToDatabase = true, // Internal only - defaults to true, can be overridden internally\n }: {\n params: ExecuteTransactionParams;\n bearerToken: string;\n saveToDatabase?: boolean; // Internal only - not part of public API\n }): Promise<string> {\n return executePaymasterTransaction({\n params: {\n ...params,\n saveToDatabase,\n },\n bearerToken,\n client: this.client,\n });\n }\n\n /**\n * Transfer tokens\n */\n async transfer({\n params,\n bearerToken,\n }: {\n params: TransferParams;\n bearerToken: string;\n }): Promise<string> {\n const { encryptKey, wallet, token, otherToken, recipient, amount } = params;\n const contract = STARKNET_CONTRACTS[token];\n let contractAddress = contract.contractAddress;\n let decimals = contract.decimals;\n if (token === \"OTHER\") {\n if (!otherToken) {\n throw new Error(\"Other token is required when token is OTHER\");\n }\n contractAddress = otherToken.contractAddress;\n decimals = otherToken.decimals;\n }\n const formattedAmount = formatAmount(amount, decimals);\n return this.executeTransaction({\n params: {\n encryptKey,\n wallet,\n calls: [\n {\n contractAddress,\n entrypoint: \"transfer\",\n calldata: [recipient, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken,\n });\n }\n\n /**\n * Approve token spending\n */\n async approve(params: {\n encryptKey: string;\n wallet: WalletData;\n contractAddress: string;\n spender: string;\n amount: string;\n decimals?: number;\n bearerToken: string;\n }): Promise<string> {\n const formattedAmount = formatAmount(params.amount, params.decimals);\n\n return this.executeTransaction({\n params: {\n encryptKey: params.encryptKey,\n wallet: params.wallet,\n calls: [\n {\n contractAddress: params.contractAddress,\n entrypoint: \"approve\",\n calldata: [params.spender, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken: params.bearerToken,\n });\n }\n\n /**\n * Call any contract method\n */\n // {\n // encryptKey: string;\n // wallet: any;\n // calls: any[];\n // bearerToken: string;\n // }\n\n async callAnyContract({\n params,\n bearerToken,\n }: {\n params: CallAnyContractParams;\n bearerToken: string;\n }): Promise<string> {\n return this.executeTransaction({\n params,\n bearerToken,\n });\n }\n\n /**\n * Record a send transaction\n */\n async recordSendTransaction({\n params,\n bearerToken,\n }: {\n params: RecordSendTransactionParams;\n bearerToken: string;\n }): Promise<Transaction> {\n const response = await this.client.post<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/record-send`,\n bearerToken,\n body: params,\n });\n return response;\n }\n\n /**\n * Get paginated transaction history\n */\n async getTransactionList(\n query: GetTransactionListQuery,\n bearerToken: string\n ): Promise<PaginatedResponse<Transaction>> {\n const response = await this.client.get<PaginatedResponse<Transaction>>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/transaction-list`,\n params: query,\n bearerToken,\n });\n return response;\n }\n\n /**\n * Get a single transaction by hash or internal ID\n */\n async getTransaction(hashOrId: string, bearerToken: string): Promise<Transaction> {\n return this.client.get<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hashOrId}`,\n bearerToken,\n });\n }\n\n /**\n * Get on-chain transaction status\n */\n async getTransactionStatus(hash: string, bearerToken: string): Promise<TransactionStatusResponse> {\n return this.client.get<TransactionStatusResponse>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hash}/status`,\n bearerToken,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/encryption.ts","../src/paymaster-adapter.ts","../src/execute-paymaster-transaction.ts","../src/transactions.ts"],"names":["WALLET_CLASS_HASHES","typedData","apiMessage"],"mappings":"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","file":"transactions.mjs","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 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","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 // 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 // Validate CHIPI wallet - session keys only work with CHIPI wallets\n if (wallet.walletType !== \"CHIPI\") {\n console.error(\"[ChipiSDK:Session:Execute] Invalid wallet type for session execution\", {\n provided: wallet.walletType,\n required: \"CHIPI\",\n expectedClassHash: WALLET_CLASS_HASHES.CHIPI,\n walletAddress: wallet.publicKey?.slice(0, 15) + \"...\",\n hint: \"Session keys only work with CHIPI wallets (SNIP-9 compatible)\",\n });\n throw new ChipiSessionError(\n `Session execution requires CHIPI 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 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 {\n CallAnyContractParams,\n STARKNET_CONTRACTS,\n WalletData,\n type ExecuteTransactionParams,\n type RecordSendTransactionParams,\n type Transaction,\n type TransferParams,\n type GetTransactionListQuery,\n type PaginatedResponse,\n type TransactionStatusResponse,\n type SyncOnChainTransfersParams,\n type SyncOnChainTransfersResponse,\n} from \"@chipi-stack/types\";\nimport { API_ENDPOINTS, formatAmount } from \"@chipi-stack/shared\";\nimport { ChipiClient } from \"./client\";\nimport { executePaymasterTransaction } from \"./execute-paymaster-transaction\";\n\n/**\n * Transaction management utilities\n */\nexport class ChipiTransactions {\n constructor(private client: ChipiClient) {}\n /**\n * Execute a gasless transaction using paymaster\n * @internal saveToDatabase - Internal only, not exposed to clients\n */\n async executeTransaction({\n params,\n bearerToken,\n saveToDatabase = true, // Internal only - defaults to true, can be overridden internally\n }: {\n params: ExecuteTransactionParams;\n bearerToken: string;\n saveToDatabase?: boolean; // Internal only - not part of public API\n }): Promise<string> {\n return executePaymasterTransaction({\n params: {\n ...params,\n saveToDatabase,\n },\n bearerToken,\n client: this.client,\n });\n }\n\n /**\n * Transfer tokens\n */\n async transfer({\n params,\n bearerToken,\n }: {\n params: TransferParams;\n bearerToken: string;\n }): Promise<string> {\n const { encryptKey, wallet, token, otherToken, recipient, amount } = params;\n const contract = STARKNET_CONTRACTS[token];\n let contractAddress = contract.contractAddress;\n let decimals = contract.decimals;\n if (token === \"OTHER\") {\n if (!otherToken) {\n throw new Error(\"Other token is required when token is OTHER\");\n }\n contractAddress = otherToken.contractAddress;\n decimals = otherToken.decimals;\n }\n const formattedAmount = formatAmount(amount, decimals);\n return this.executeTransaction({\n params: {\n encryptKey,\n wallet,\n calls: [\n {\n contractAddress,\n entrypoint: \"transfer\",\n calldata: [recipient, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken,\n });\n }\n\n /**\n * Approve token spending\n */\n async approve(params: {\n encryptKey: string;\n wallet: WalletData;\n contractAddress: string;\n spender: string;\n amount: string;\n decimals?: number;\n bearerToken: string;\n }): Promise<string> {\n const formattedAmount = formatAmount(params.amount, params.decimals);\n\n return this.executeTransaction({\n params: {\n encryptKey: params.encryptKey,\n wallet: params.wallet,\n calls: [\n {\n contractAddress: params.contractAddress,\n entrypoint: \"approve\",\n calldata: [params.spender, formattedAmount, \"0x0\"],\n },\n ],\n },\n bearerToken: params.bearerToken,\n });\n }\n\n /**\n * Call any contract method\n */\n // {\n // encryptKey: string;\n // wallet: any;\n // calls: any[];\n // bearerToken: string;\n // }\n\n async callAnyContract({\n params,\n bearerToken,\n }: {\n params: CallAnyContractParams;\n bearerToken: string;\n }): Promise<string> {\n return this.executeTransaction({\n params,\n bearerToken,\n });\n }\n\n /**\n * Record a send transaction\n */\n async recordSendTransaction({\n params,\n bearerToken,\n }: {\n params: RecordSendTransactionParams;\n bearerToken: string;\n }): Promise<Transaction> {\n const response = await this.client.post<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/record-send`,\n bearerToken,\n body: params,\n });\n return response;\n }\n\n /**\n * Get paginated transaction history\n */\n async getTransactionList(\n query: GetTransactionListQuery,\n bearerToken: string\n ): Promise<PaginatedResponse<Transaction>> {\n const response = await this.client.get<PaginatedResponse<Transaction>>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/transaction-list`,\n params: query,\n bearerToken,\n });\n return response;\n }\n\n /**\n * Get a single transaction by hash or internal ID\n */\n async getTransaction(hashOrId: string, bearerToken: string): Promise<Transaction> {\n return this.client.get<Transaction>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hashOrId}`,\n bearerToken,\n });\n }\n\n /**\n * Get on-chain transaction status\n */\n async getTransactionStatus(hash: string, bearerToken: string): Promise<TransactionStatusResponse> {\n return this.client.get<TransactionStatusResponse>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/${hash}/status`,\n bearerToken,\n });\n }\n\n /**\n * Sync on-chain transfers from Voyager into the DB (read-through cache).\n * Discovers transactions not made through the SDK (e.g., external USDC receives).\n * After sync, getTransactionList returns the newly discovered transactions.\n */\n async syncOnChainTransfers(\n params: SyncOnChainTransfersParams,\n bearerToken: string,\n ): Promise<SyncOnChainTransfersResponse> {\n return this.client.post<SyncOnChainTransfersResponse>({\n endpoint: `${API_ENDPOINTS.TRANSACTIONS}/sync-on-chain`,\n bearerToken,\n body: params,\n });\n }\n}\n"]}
|
package/dist/wallets.d.mts
CHANGED
|
@@ -24,6 +24,18 @@ declare class ChipiWallets {
|
|
|
24
24
|
bearerToken: string;
|
|
25
25
|
}): Promise<WalletData>;
|
|
26
26
|
getWallet(params: GetWalletParams, bearerToken: string): Promise<GetWalletResponse | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Recover passkey credential metadata for a wallet.
|
|
29
|
+
* Used when localStorage is cleared — SDK recovers credentialId from backend.
|
|
30
|
+
*/
|
|
31
|
+
getCredentialRecovery(params: {
|
|
32
|
+
externalUserId: string;
|
|
33
|
+
}, bearerToken: string): Promise<{
|
|
34
|
+
credentialId: string | null;
|
|
35
|
+
authMethod: string | null;
|
|
36
|
+
prfSupported: boolean | null;
|
|
37
|
+
deviceType: string | null;
|
|
38
|
+
}>;
|
|
27
39
|
updateWalletEncryption(params: UpdateWalletEncryptionParams, bearerToken: string): Promise<UpdateWalletEncryptionResponse>;
|
|
28
40
|
getPrivateKeyAX: () => string;
|
|
29
41
|
getTokenBalance({ params, bearerToken, }: {
|
package/dist/wallets.d.ts
CHANGED
|
@@ -24,6 +24,18 @@ declare class ChipiWallets {
|
|
|
24
24
|
bearerToken: string;
|
|
25
25
|
}): Promise<WalletData>;
|
|
26
26
|
getWallet(params: GetWalletParams, bearerToken: string): Promise<GetWalletResponse | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Recover passkey credential metadata for a wallet.
|
|
29
|
+
* Used when localStorage is cleared — SDK recovers credentialId from backend.
|
|
30
|
+
*/
|
|
31
|
+
getCredentialRecovery(params: {
|
|
32
|
+
externalUserId: string;
|
|
33
|
+
}, bearerToken: string): Promise<{
|
|
34
|
+
credentialId: string | null;
|
|
35
|
+
authMethod: string | null;
|
|
36
|
+
prfSupported: boolean | null;
|
|
37
|
+
deviceType: string | null;
|
|
38
|
+
}>;
|
|
27
39
|
updateWalletEncryption(params: UpdateWalletEncryptionParams, bearerToken: string): Promise<UpdateWalletEncryptionResponse>;
|
|
28
40
|
getPrivateKeyAX: () => string;
|
|
29
41
|
getTokenBalance({ params, bearerToken, }: {
|
package/dist/wallets.js
CHANGED
|
@@ -85,6 +85,7 @@ var ChipiWallets = class {
|
|
|
85
85
|
calldata: constructorCallData.map((value) => starknet.num.toHex(value))
|
|
86
86
|
};
|
|
87
87
|
const encryptedPrivateKey = encryptPrivateKey(privateKeyAX, encryptKey);
|
|
88
|
+
const encryptedPrivateKeyBackup = params.encryptKeyBackup ? encryptPrivateKey(privateKeyAX, params.encryptKeyBackup) : void 0;
|
|
88
89
|
const executeTransactionResponse = await this.client.post({
|
|
89
90
|
endpoint: `${shared.API_ENDPOINTS.CHIPI_WALLETS}`,
|
|
90
91
|
bearerToken,
|
|
@@ -101,6 +102,11 @@ var ChipiWallets = class {
|
|
|
101
102
|
},
|
|
102
103
|
typedData,
|
|
103
104
|
encryptedPrivateKey,
|
|
105
|
+
...encryptedPrivateKeyBackup && { encryptedPrivateKeyBackup },
|
|
106
|
+
...params.authMethod && { authMethod: params.authMethod },
|
|
107
|
+
...params.credentialId && { credentialId: params.credentialId },
|
|
108
|
+
...params.prfSupported != null && { prfSupported: params.prfSupported },
|
|
109
|
+
...params.deviceType && { deviceType: params.deviceType },
|
|
104
110
|
deploymentData: {
|
|
105
111
|
...deploymentData,
|
|
106
112
|
salt: `${deploymentData.salt}`,
|
|
@@ -172,6 +178,17 @@ var ChipiWallets = class {
|
|
|
172
178
|
throw err;
|
|
173
179
|
}
|
|
174
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* Recover passkey credential metadata for a wallet.
|
|
183
|
+
* Used when localStorage is cleared — SDK recovers credentialId from backend.
|
|
184
|
+
*/
|
|
185
|
+
async getCredentialRecovery(params, bearerToken) {
|
|
186
|
+
return this.client.get({
|
|
187
|
+
endpoint: `${shared.API_ENDPOINTS.CHIPI_WALLETS}/credential-recovery`,
|
|
188
|
+
params: { externalUserId: params.externalUserId },
|
|
189
|
+
bearerToken
|
|
190
|
+
});
|
|
191
|
+
}
|
|
175
192
|
async updateWalletEncryption(params, bearerToken) {
|
|
176
193
|
const body = {
|
|
177
194
|
externalUserId: params.externalUserId,
|
package/dist/wallets.js.map
CHANGED
|
@@ -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":"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","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 // 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 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 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/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":"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","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"]}
|
package/dist/wallets.mjs
CHANGED
|
@@ -79,6 +79,7 @@ var ChipiWallets = class {
|
|
|
79
79
|
calldata: constructorCallData.map((value) => num.toHex(value))
|
|
80
80
|
};
|
|
81
81
|
const encryptedPrivateKey = encryptPrivateKey(privateKeyAX, encryptKey);
|
|
82
|
+
const encryptedPrivateKeyBackup = params.encryptKeyBackup ? encryptPrivateKey(privateKeyAX, params.encryptKeyBackup) : void 0;
|
|
82
83
|
const executeTransactionResponse = await this.client.post({
|
|
83
84
|
endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}`,
|
|
84
85
|
bearerToken,
|
|
@@ -95,6 +96,11 @@ var ChipiWallets = class {
|
|
|
95
96
|
},
|
|
96
97
|
typedData,
|
|
97
98
|
encryptedPrivateKey,
|
|
99
|
+
...encryptedPrivateKeyBackup && { encryptedPrivateKeyBackup },
|
|
100
|
+
...params.authMethod && { authMethod: params.authMethod },
|
|
101
|
+
...params.credentialId && { credentialId: params.credentialId },
|
|
102
|
+
...params.prfSupported != null && { prfSupported: params.prfSupported },
|
|
103
|
+
...params.deviceType && { deviceType: params.deviceType },
|
|
98
104
|
deploymentData: {
|
|
99
105
|
...deploymentData,
|
|
100
106
|
salt: `${deploymentData.salt}`,
|
|
@@ -166,6 +172,17 @@ var ChipiWallets = class {
|
|
|
166
172
|
throw err;
|
|
167
173
|
}
|
|
168
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Recover passkey credential metadata for a wallet.
|
|
177
|
+
* Used when localStorage is cleared — SDK recovers credentialId from backend.
|
|
178
|
+
*/
|
|
179
|
+
async getCredentialRecovery(params, bearerToken) {
|
|
180
|
+
return this.client.get({
|
|
181
|
+
endpoint: `${API_ENDPOINTS.CHIPI_WALLETS}/credential-recovery`,
|
|
182
|
+
params: { externalUserId: params.externalUserId },
|
|
183
|
+
bearerToken
|
|
184
|
+
});
|
|
185
|
+
}
|
|
169
186
|
async updateWalletEncryption(params, bearerToken) {
|
|
170
187
|
const body = {
|
|
171
188
|
externalUserId: params.externalUserId,
|