UNPKG

@turnkey/core

Version:

A core JavaScript web and React Native package for interfacing with Turnkey's infrastructure.

649 lines (646 loc) 27.2 kB
import { TurnkeyError, TurnkeyErrorCodes } from '@turnkey/sdk-types'; import { Chain, TurnkeyRequestError } from './__types__/base.mjs'; import { DEFAULT_XRP_ACCOUNTS, DEFAULT_TON_V4R2_ACCOUNTS, DEFAULT_TON_V3R2_ACCOUNTS, DEFAULT_DOGE_TESTNET_ACCOUNTS, DEFAULT_DOGE_MAINNET_ACCOUNTS, DEFAULT_XLM_ACCOUNTS, DEFAULT_SEI_ACCOUNTS, DEFAULT_BITCOIN_REGTEST_P2TR_ACCOUNTS, DEFAULT_BITCOIN_REGTEST_P2WSH_ACCOUNTS, DEFAULT_BITCOIN_REGTEST_P2WPKH_ACCOUNTS, DEFAULT_BITCOIN_REGTEST_P2SH_ACCOUNTS, DEFAULT_BITCOIN_REGTEST_P2PKH_ACCOUNTS, DEFAULT_BITCOIN_SIGNET_P2TR_ACCOUNTS, DEFAULT_BITCOIN_SIGNET_P2WSH_ACCOUNTS, DEFAULT_BITCOIN_SIGNET_P2WPKH_ACCOUNTS, DEFAULT_BITCOIN_SIGNET_P2SH_ACCOUNTS, DEFAULT_BITCOIN_SIGNET_P2PKH_ACCOUNTS, DEFAULT_BITCOIN_TESTNET_P2TR_ACCOUNTS, DEFAULT_BITCOIN_TESTNET_P2WSH_ACCOUNTS, DEFAULT_BITCOIN_TESTNET_P2WPKH_ACCOUNTS, DEFAULT_BITCOIN_TESTNET_P2SH_ACCOUNTS, DEFAULT_BITCOIN_TESTNET_P2PKH_ACCOUNTS, DEFAULT_BITCOIN_MAINNET_P2TR_ACCOUNTS, DEFAULT_BITCOIN_MAINNET_P2WSH_ACCOUNTS, DEFAULT_BITCOIN_MAINNET_P2WPKH_ACCOUNTS, DEFAULT_BITCOIN_MAINNET_P2SH_ACCOUNTS, DEFAULT_BITCOIN_MAINNET_P2PKH_ACCOUNTS, DEFAULT_APTOS_ACCOUNTS, DEFAULT_SUI_ACCOUNTS, DEFAULT_TRON_ACCOUNTS, DEFAULT_COSMOS_ACCOUNTS, DEFAULT_SOLANA_ACCOUNTS, DEFAULT_ETHEREUM_ACCOUNTS } from './turnkey-helpers.mjs'; import { fromDerSignature } from '@turnkey/crypto'; import { decodeBase64urlToString, uint8ArrayToHexString } from '@turnkey/encoding'; /** * Configuration for all supported address formats. * * Includes: * - encoding type * - hash function * - default accounts for the address format * - display name for the address format * * ```ts * // Example usage: * import { addressFormatConfig } from "@turnkey/sdk-core"; * * const config = addressFormatConfig["ADDRESS_FORMAT_ETHEREUM"]; * ``` */ const addressFormatConfig = { ADDRESS_FORMAT_UNCOMPRESSED: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: null, displayName: "Uncompressed", }, ADDRESS_FORMAT_COMPRESSED: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: null, displayName: "Compressed", }, ADDRESS_FORMAT_ETHEREUM: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_KECCAK256", defaultAccounts: DEFAULT_ETHEREUM_ACCOUNTS, displayName: "Ethereum", }, ADDRESS_FORMAT_SOLANA: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: DEFAULT_SOLANA_ACCOUNTS, displayName: "Solana", }, ADDRESS_FORMAT_COSMOS: { encoding: "PAYLOAD_ENCODING_TEXT_UTF8", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_COSMOS_ACCOUNTS, displayName: "Cosmos", }, ADDRESS_FORMAT_TRON: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_TRON_ACCOUNTS, displayName: "Tron", }, ADDRESS_FORMAT_SUI: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: DEFAULT_SUI_ACCOUNTS, displayName: "Sui", }, ADDRESS_FORMAT_APTOS: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: DEFAULT_APTOS_ACCOUNTS, displayName: "Aptos", }, ADDRESS_FORMAT_BITCOIN_MAINNET_P2PKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_MAINNET_P2PKH_ACCOUNTS, displayName: "Bitcoin Mainnet P2PKH", }, ADDRESS_FORMAT_BITCOIN_MAINNET_P2SH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_MAINNET_P2SH_ACCOUNTS, displayName: "Bitcoin Mainnet P2SH", }, ADDRESS_FORMAT_BITCOIN_MAINNET_P2WPKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_MAINNET_P2WPKH_ACCOUNTS, displayName: "Bitcoin Mainnet P2WPKH", }, ADDRESS_FORMAT_BITCOIN_MAINNET_P2WSH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_MAINNET_P2WSH_ACCOUNTS, displayName: "Bitcoin Mainnet P2WSH", }, ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_MAINNET_P2TR_ACCOUNTS, displayName: "Bitcoin Mainnet P2TR", }, ADDRESS_FORMAT_BITCOIN_TESTNET_P2PKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_TESTNET_P2PKH_ACCOUNTS, displayName: "Bitcoin Testnet P2PKH", }, ADDRESS_FORMAT_BITCOIN_TESTNET_P2SH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_TESTNET_P2SH_ACCOUNTS, displayName: "Bitcoin Testnet P2SH", }, ADDRESS_FORMAT_BITCOIN_TESTNET_P2WPKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_TESTNET_P2WPKH_ACCOUNTS, displayName: "Bitcoin Testnet P2WPKH", }, ADDRESS_FORMAT_BITCOIN_TESTNET_P2WSH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_TESTNET_P2WSH_ACCOUNTS, displayName: "Bitcoin Testnet P2WSH", }, ADDRESS_FORMAT_BITCOIN_TESTNET_P2TR: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_TESTNET_P2TR_ACCOUNTS, displayName: "Bitcoin Testnet P2TR", }, ADDRESS_FORMAT_BITCOIN_SIGNET_P2PKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_SIGNET_P2PKH_ACCOUNTS, displayName: "Bitcoin Signet P2PKH", }, ADDRESS_FORMAT_BITCOIN_SIGNET_P2SH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_SIGNET_P2SH_ACCOUNTS, displayName: "Bitcoin Signet P2SH", }, ADDRESS_FORMAT_BITCOIN_SIGNET_P2WPKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_SIGNET_P2WPKH_ACCOUNTS, displayName: "Bitcoin Signet P2WPKH", }, ADDRESS_FORMAT_BITCOIN_SIGNET_P2WSH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_SIGNET_P2WSH_ACCOUNTS, displayName: "Bitcoin Signet P2WSH", }, ADDRESS_FORMAT_BITCOIN_SIGNET_P2TR: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_SIGNET_P2TR_ACCOUNTS, displayName: "Bitcoin Signet P2TR", }, ADDRESS_FORMAT_BITCOIN_REGTEST_P2PKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_REGTEST_P2PKH_ACCOUNTS, displayName: "Bitcoin Regtest P2PKH", }, ADDRESS_FORMAT_BITCOIN_REGTEST_P2SH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_REGTEST_P2SH_ACCOUNTS, displayName: "Bitcoin Regtest P2SH", }, ADDRESS_FORMAT_BITCOIN_REGTEST_P2WPKH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_REGTEST_P2WPKH_ACCOUNTS, displayName: "Bitcoin Regtest P2WPKH", }, ADDRESS_FORMAT_BITCOIN_REGTEST_P2WSH: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_REGTEST_P2WSH_ACCOUNTS, displayName: "Bitcoin Regtest P2WSH", }, ADDRESS_FORMAT_BITCOIN_REGTEST_P2TR: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_BITCOIN_REGTEST_P2TR_ACCOUNTS, displayName: "Bitcoin Regtest P2TR", }, ADDRESS_FORMAT_SEI: { encoding: "PAYLOAD_ENCODING_TEXT_UTF8", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_SEI_ACCOUNTS, displayName: "Sei", }, ADDRESS_FORMAT_XLM: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: DEFAULT_XLM_ACCOUNTS, displayName: "Xlm", }, ADDRESS_FORMAT_DOGE_MAINNET: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_DOGE_MAINNET_ACCOUNTS, displayName: "Doge Mainnet", }, ADDRESS_FORMAT_DOGE_TESTNET: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_DOGE_TESTNET_ACCOUNTS, displayName: "Doge Testnet", }, ADDRESS_FORMAT_TON_V3R2: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: DEFAULT_TON_V3R2_ACCOUNTS, displayName: "Ton V3R2", }, ADDRESS_FORMAT_TON_V4R2: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: DEFAULT_TON_V4R2_ACCOUNTS, displayName: "Ton V4R2", }, ADDRESS_FORMAT_TON_V5R1: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_NOT_APPLICABLE", defaultAccounts: null, displayName: "Ton V5R1", }, ADDRESS_FORMAT_XRP: { encoding: "PAYLOAD_ENCODING_HEXADECIMAL", hashFunction: "HASH_FUNCTION_SHA256", defaultAccounts: DEFAULT_XRP_ACCOUNTS, displayName: "XRP", }, }; const googleISS = "https://accounts.google.com"; const isReactNative = () => { return (typeof navigator !== "undefined" && navigator.product === "ReactNative"); }; const isWeb = () => { return typeof window !== "undefined" && typeof document !== "undefined"; }; const generateRandomBuffer = () => { const arr = new Uint8Array(32); crypto.getRandomValues(arr); return arr.buffer; }; Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0")); const toExternalTimestamp = (date = new Date()) => { const millis = date.getTime(); const seconds = Math.floor(millis / 1000); const nanos = (millis % 1000) * 1_000_000; return { seconds: seconds.toString(), nanos: nanos.toString(), }; }; function parseSession(token) { if (typeof token !== "string") { return token; } const [, payload] = token.split("."); if (!payload) { throw new Error("Invalid JWT: Missing payload"); } const decoded = JSON.parse(atob(payload)); const { exp, public_key: publicKey, session_type: sessionType, user_id: userId, organization_id: organizationId, } = decoded; if (!exp || !publicKey || !sessionType || !userId || !organizationId) { throw new Error("JWT payload missing required fields"); } const expSeconds = Math.ceil((exp * 1000 - Date.now()) / 1000); return { sessionType, userId, organizationId, expiry: exp, expirationSeconds: expSeconds.toString(), publicKey, token, }; } function getHashFunction(addressFormat) { const config = addressFormatConfig[addressFormat]; if (!config) { throw new TurnkeyError(`Unsupported address format: ${addressFormat}`, TurnkeyErrorCodes.INVALID_REQUEST); } return config.hashFunction; } function getEncodingType(addressFormat) { const config = addressFormatConfig[addressFormat]; if (!config) { throw new TurnkeyError(`Unsupported address format: ${addressFormat}`, TurnkeyErrorCodes.INVALID_REQUEST); } return config.encoding; } function getEncodedMessage(addressFormat, rawMessage) { const config = addressFormatConfig[addressFormat]; if (!config) { throw new TurnkeyError(`Unsupported address format: ${addressFormat}`, TurnkeyErrorCodes.INVALID_REQUEST); } if (config.encoding === "PAYLOAD_ENCODING_HEXADECIMAL") { return ("0x" + Array.from(new TextEncoder().encode(rawMessage)) .map((b) => b.toString(16).padStart(2, "0")) .join("")); } return rawMessage; } const broadcastTransaction = async (params) => { const { signedTransaction, rpcUrl, transactionType } = params; switch (transactionType) { case "TRANSACTION_TYPE_SOLANA": { const response = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "sendTransaction", params: [signedTransaction], }), }); const json = await response.json(); if (json.error) { throw new TurnkeyError(`Solana RPC Error: ${json.error.message}`, TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR); } return json.result; } case "TRANSACTION_TYPE_ETHEREUM": { const response = await fetch(rpcUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_sendRawTransaction", params: [signedTransaction], }), }); const json = await response.json(); if (json.error) { throw new TurnkeyError(`Ethereum RPC Error: ${json.error.message}`, TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR); } return json.result; } case "TRANSACTION_TYPE_TRON": { const response = await fetch(`${rpcUrl}/wallet/broadcasthex`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction: signedTransaction }), }); const json = await response.json(); if (!json.result) { throw new TurnkeyError(`Tron RPC Error: ${json.message}`, TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR); } return json.txid; } default: throw new TurnkeyError(`Unsupported transaction type for broadcasting: ${transactionType}`, TurnkeyErrorCodes.SIGN_AND_SEND_TRANSACTION_ERROR); } }; function splitSignature(signature, addressFormat) { const hex = signature.replace(/^0x/, ""); if (addressFormat === "ADDRESS_FORMAT_ETHEREUM") { // this is a ECDSA signature if (hex.length === 130) { const r = hex.slice(0, 64); const s = hex.slice(64, 128); const v = hex.slice(128, 130); return { r, s, v }; } // this is a DER-encoded signatures (e.g., Ledger) const raw = fromDerSignature(hex); const r = uint8ArrayToHexString(raw.slice(0, 32)); const s = uint8ArrayToHexString(raw.slice(32, 64)); // DER signatures do not have a v component // so we return 00 to match what Turnkey does const v = "00"; return { r, s, v }; } if (addressFormat === "ADDRESS_FORMAT_SOLANA") { if (hex.length !== 128) { throw new Error(`Invalid Solana signature length: expected 64 bytes (128 hex), got ${hex.length}`); } // this is a Ed25519 signature const r = hex.slice(0, 64); const s = hex.slice(64, 128); // solana signatures do not have a v component // so we return 00 to match what Turnkey does return { r, s, v: "00" }; } throw new Error(`Unsupported address format or invalid signature length: ${hex.length}`); } // Type guard to check if accounts is WalletAccount[] function isWalletAccountArray(arr) { return (arr.length === 0 || (typeof arr[0] === "object" && "addressFormat" in arr[0] && "curve" in arr[0] && "path" in arr[0] && "pathFormat" in arr[0])); } function createWalletAccountFromAddressFormat(addressFormat) { const walletAccount = addressFormatConfig[addressFormat].defaultAccounts; if (!walletAccount) { throw new Error(`Unsupported address format: ${addressFormat}`); } if (walletAccount[0]) { return walletAccount[0]; } throw new Error(`No default accounts defined for address format: ${addressFormat}`); } /**@internal */ function generateWalletAccountsFromAddressFormat(params) { const { addresses, existingWalletAccounts } = params; const pathMap = new Map(); // Build a lookup for max index per (addressFormat, basePath) const maxIndexMap = new Map(); if (existingWalletAccounts && existingWalletAccounts.length > 0) { for (const acc of existingWalletAccounts) { // Normalize base path (remove account index) const basePath = acc.path.replace(/^((?:[^\/]+\/){3})[^\/]+/, "$1"); const key = `${acc.addressFormat}:${basePath}`; const idxSegment = acc.path.split("/")[3]; const idx = idxSegment ? parseInt(idxSegment.replace(/'/, ""), 10) : -1; if (!isNaN(idx)) { maxIndexMap.set(key, Math.max(maxIndexMap.get(key) ?? -1, idx)); } } } return addresses.map((addressFormat) => { const account = createWalletAccountFromAddressFormat(addressFormat); const basePath = account.path.replace(/^((?:[^\/]+\/){3})[^\/]+/, "$1"); const key = `${addressFormat}:${basePath}`; let nextIndex = 0; if (maxIndexMap.has(key)) { nextIndex = maxIndexMap.get(key) + 1; } else if (pathMap.has(account.path)) { nextIndex = pathMap.get(account.path); } const pathWithIndex = account.path.replace(/^((?:[^\/]*\/){3})(\d+)/, (_, prefix) => `${prefix}${nextIndex}`); pathMap.set(account.path, nextIndex + 1); return { ...account, path: pathWithIndex, }; }); } function buildSignUpBody(params) { const { createSubOrgParams } = params; const websiteName = window.location.hostname; let authenticators = []; if (createSubOrgParams?.authenticators?.length) { authenticators = createSubOrgParams?.authenticators?.map((authenticator) => ({ authenticatorName: authenticator.authenticatorName || `${websiteName}-${Date.now()}`, challenge: authenticator.challenge, attestation: authenticator.attestation, })) || []; } let apiKeys = []; if (createSubOrgParams?.apiKeys?.length) { apiKeys = createSubOrgParams.apiKeys .filter((apiKey) => apiKey.curveType !== undefined) .map((apiKey) => ({ apiKeyName: apiKey.apiKeyName || `api-key-${Date.now()}`, publicKey: apiKey.publicKey, curveType: apiKey.curveType, ...(apiKey?.expirationSeconds && { expirationSeconds: apiKey.expirationSeconds, }), })); } return { userName: createSubOrgParams?.userName || createSubOrgParams?.userEmail || `user-${Date.now()}`, ...(createSubOrgParams?.userEmail && { userEmail: createSubOrgParams?.userEmail, }), ...(createSubOrgParams?.authenticators?.length ? { authenticators, } : { authenticators: [] }), ...(createSubOrgParams?.userPhoneNumber && { userPhoneNumber: createSubOrgParams.userPhoneNumber, }), ...(createSubOrgParams?.userTag && { userTag: createSubOrgParams?.userTag, }), organizationName: createSubOrgParams?.subOrgName || `sub-org-${Date.now()}`, ...(createSubOrgParams?.verificationToken && { verificationToken: createSubOrgParams?.verificationToken, }), ...(createSubOrgParams?.apiKeys?.length ? { apiKeys, } : { apiKeys: [] }), ...(createSubOrgParams?.oauthProviders?.length ? { oauthProviders: createSubOrgParams.oauthProviders, } : { oauthProviders: [] }), ...(createSubOrgParams?.customWallet && { wallet: { walletName: createSubOrgParams.customWallet.walletName, accounts: createSubOrgParams.customWallet.walletAccounts, }, }), }; } /** * Extracts the public key from a Turnkey stamp header value. * @param stampHeaderValue - The base64url encoded stamp header value * @returns The public key as a hex string */ function getPublicKeyFromStampHeader(stampHeaderValue) { try { // we decode the base64url string to get the JSON stamp const stampJson = decodeBase64urlToString(stampHeaderValue); // we parse the JSON to get the stamp object const stamp = JSON.parse(stampJson); return stamp.publicKey; } catch (error) { throw new Error(`Failed to extract public key from stamp header: ${error instanceof Error ? error.message : String(error)}`); } } /**@internal */ function isEthereumProvider(provider) { return provider.chainInfo.namespace === Chain.Ethereum; } /**@internal */ function isSolanaProvider(provider) { return provider.chainInfo.namespace === Chain.Solana; } /** @internal */ function findWalletProviderFromAddress(address, providers) { for (const provider of providers) { if (provider.connectedAddresses.includes(address)) { return provider; } } // no provider found for that address return undefined; } /**@internal */ async function getAuthProxyConfig(authProxyConfigId, authProxyUrl) { const fullUrl = (authProxyUrl ?? "https://authproxy.turnkey.com") + "/v1/wallet_kit_config"; var headers = { "Content-Type": "application/json", "X-Auth-Proxy-Config-ID": authProxyConfigId, }; const response = await fetch(fullUrl, { method: "POST", headers: headers, }); if (!response.ok) { let res; try { res = await response.json(); } catch (_) { throw new Error(`${response.status} ${response.statusText}`); } throw new TurnkeyRequestError(res); } const data = await response.json(); return data; } /** * @internal * Executes an async function with error handling. * * @param fn The async function to execute with error handling * @param errorOptions Options for customizing error handling * @param errorOptions.catchFn Optional function to execute in the catch block * @param errorOptions.errorMessage The default error message to use if no custom message is found * @param errorOptions.errorCode The default error code to use if no custom message is found * @param errorOptions.customMessageByCodes Optional mapping of error codes to custom messages, if you're trying to target a specific error code and surface a custom message, use this * @param errorOptions.customMessageByMessages Optional mapping of error messages to custom messages, if you're trying to target a specific error message and surface a custom message, use this * @param finallyFn Optional function to execute in the finally block * @returns The result of the async function or throws an error */ async function withTurnkeyErrorHandling(fn, catchOptions, finallyOptions) { const { errorMessage, errorCode, customMessageByCodes, customMessageByMessages, catchFn, } = catchOptions; const finallyFn = finallyOptions?.finallyFn; try { return await fn(); } catch (error) { await catchFn?.(); if (error instanceof TurnkeyError) { const customCodeMessage = customMessageByCodes?.[error.code]; if (customCodeMessage) { throw new TurnkeyError(customCodeMessage.message, customCodeMessage.code, error); } throwMatchingMessage(error.message, customMessageByMessages, error); throw error; } else if (error instanceof TurnkeyRequestError) { throwMatchingMessage(error.message, customMessageByMessages, error); throw new TurnkeyError(errorMessage, errorCode, error); } else if (error instanceof Error) { throwMatchingMessage(error.message, customMessageByMessages, error); // Wrap other errors in a TurnkeyError throw new TurnkeyError(errorMessage, errorCode, error); } else { throwMatchingMessage(String(error), customMessageByMessages, error); // Handle non-Error exceptions throw new TurnkeyError(String(error), errorCode, error); } } finally { await finallyFn?.(); } } /** * Throws a TurnkeyError with a custom message if the error message matches any key in customMessageByMessages. * If no match is found, it does nothing. * * @param errorMessage The error message to check against the custom messages. * @param customMessageByMessages An object mapping error messages to custom messages and codes. * @param error The original error that triggered this function. */ const throwMatchingMessage = (errorMessage, customMessageByMessages, error) => { if (customMessageByMessages && Object.keys(customMessageByMessages).length > 0) { Object.keys(customMessageByMessages).forEach((key) => { if (errorMessage.includes(key)) { throw new TurnkeyError(customMessageByMessages[key].message, customMessageByMessages[key].code, error); } }); } }; export { addressFormatConfig, broadcastTransaction, buildSignUpBody, createWalletAccountFromAddressFormat, findWalletProviderFromAddress, generateRandomBuffer, generateWalletAccountsFromAddressFormat, getAuthProxyConfig, getEncodedMessage, getEncodingType, getHashFunction, getPublicKeyFromStampHeader, googleISS, isEthereumProvider, isReactNative, isSolanaProvider, isWalletAccountArray, isWeb, parseSession, splitSignature, toExternalTimestamp, withTurnkeyErrorHandling }; //# sourceMappingURL=utils.mjs.map