@brahmafi/cards-sdk
Version:
Official SDK for integrating Swype payment and credit services
1 lines • 538 kB
Source Map (JSON)
{"version":3,"sources":["../src/widgets/kyc/KycModal.tsx","../src/components/Modal.tsx","../src/components/SDKWrapper.tsx","../src/stores/cardStatusStore.ts","../src/stores/sdkConfigStore.ts","../src/api/api.ts","../src/api/httpClient.ts","../src/api/auth.ts","../src/api/utils.ts","../src/stores/creditStore.ts","../src/stores/transactionsStore.ts","../src/core/dataFetcher.ts","../src/stores/useKycStore.ts","../src/components/icons/PendingSvg.tsx","../src/widgets/kyc/KycPendingViaRain.tsx","../src/widgets/kyc/PreRainTerms.tsx","../src/components/shared/Checkbox.tsx","../src/components/SumSubWidget.tsx","../src/hooks/use-sumsub.ts","../src/utils/getUserIpAddress.ts","../src/widgets/dashboard/ManageCardModal.tsx","../src/components/shared/Typography.tsx","../src/components/icons/BrahmaLogoWithNameIcon.tsx","../src/components/icons/USDCIcon.tsx","../src/components/icons/VisaIcon.tsx","../src/components/icons/USDT0Icon.tsx","../src/components/shared/CreditCard.tsx","../src/hooks/useCardData.ts","../src/hooks/useCreditData.ts","../src/hooks/useCardDetails.ts","../src/core/secureCardMethods.ts","../src/components/icons/ChevronRightIcon.tsx","../src/widgets/dashboard/components/InfoIcon.tsx","../src/widgets/dashboard/components/ContentWrapper.tsx","../src/widgets/dashboard/components/WalletTag.tsx","../src/utils/textUtils.ts","../src/components/icons/CopyIcon.tsx","../src/components/icons/OpenInNewTabIcon.tsx","../src/widgets/dashboard/components/FreezeCardTransactions.tsx","../src/widgets/dashboard/components/CustomButton.tsx","../src/widgets/dashboard/components/CustomInput.tsx","../src/core/cardActivation.ts","../src/core/createCard.ts","../src/core/delegateAmount.ts","../src/core/updateCardProvider.ts","../src/api/sdk.ts","../src/hooks/useGetTransactions.ts"],"sourcesContent":["import React, { useMemo, useEffect, JSX } from \"react\";\nimport { Modal } from \"../../components/Modal\";\nimport { useCardStatusStore } from \"../../stores/cardStatusStore\";\nimport { useKycStore } from \"../../stores/useKycStore\";\nimport KycPendingViaRain from \"./KycPendingViaRain\";\nimport PreRainTerms from \"./PreRainTerms\";\nimport { SumSubWidget } from \"../../components/SumSubWidget\";\n\ninterface KycModalProps {\n eoaAddress: string;\n onClose?: () => void;\n onComplete?: () => void;\n}\n\nexport function KycModal({\n eoaAddress,\n onClose,\n onComplete,\n}: KycModalProps): JSX.Element | null {\n const { status } = useCardStatusStore();\n const { acceptedRainTerms } = useKycStore();\n\n // Call onComplete when status becomes approved\n useEffect(() => {\n if (status === \"approved\" && onComplete) {\n onComplete();\n }\n }, [status, onComplete]);\n\n const getModalView = useMemo(() => {\n switch (status) {\n case \"userNotExisting\":\n case \"feePending\":\n case \"initiated\": {\n return {\n title: \"User Verification\",\n component: <SumSubWidget eoaAddress={eoaAddress} />,\n };\n }\n case \"review\": {\n return {\n title: \"User Verification is in Process\",\n component: <KycPendingViaRain />,\n };\n }\n case \"pending\": {\n if (acceptedRainTerms) {\n return {\n title: \"User Verification is in Process\",\n component: <KycPendingViaRain />,\n };\n } else {\n return {\n title: \"User Verification is in Process\",\n component: <PreRainTerms />,\n };\n }\n }\n case \"approved\": {\n // For approved status, we just return null as this SDK doesn't handle card activation\n return {\n title: \"\",\n component: <></>,\n };\n }\n case \"rejected\": {\n // For approved status, we just return null as this SDK doesn't handle card activation\n return {\n title: \"\",\n component: <></>,\n };\n }\n default:\n return {\n title: \"Apply for Card\",\n component: <></>,\n };\n }\n }, [status, acceptedRainTerms]);\n\n const handleClose = onClose || (() => {});\n const { title, component } = getModalView;\n\n // Don't render modal if status is approved or no component\n if (status === \"approved\" || !component) {\n return null;\n }\n\n return (\n <Modal title={title} onClose={handleClose}>\n {component}\n </Modal>\n );\n}\n","import { motion, AnimatePresence } from \"framer-motion\";\nimport React, { ReactNode } from \"react\";\nimport { SDKWrapper } from \"./SDKWrapper\";\n\ninterface ModalProps {\n title: string;\n children: ReactNode;\n onClose: () => void;\n onOutsideClick?: () => void;\n overlayOpacity?: string;\n topDistance?: string;\n open?: boolean;\n padding?: string; // New prop for custom padding\n}\n\nexport function Modal({\n title,\n children,\n onClose,\n onOutsideClick,\n overlayOpacity = \"bg-black bg-opacity-100\",\n topDistance = \"pt-[100px]\",\n open = true,\n padding, // Accept the padding prop\n}: ModalProps) {\n // Default padding classes if not provided\n const childrenPadding = padding ?? \"px-4 sm:px-6 py-4 sm:py-6\";\n return (\n <SDKWrapper>\n <AnimatePresence>\n {open && (\n <motion.div\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n className={`fixed inset-0 z-50 flex items-start justify-center ${topDistance} ${overlayOpacity} px-4 sm:px-0`}\n onClick={onOutsideClick}\n aria-modal=\"true\"\n role=\"dialog\"\n >\n <motion.div\n initial={{ scale: 0.95, opacity: 0 }}\n animate={{ scale: 1, opacity: 1 }}\n exit={{ scale: 0.95, opacity: 0 }}\n transition={{ duration: 0.2 }}\n className=\"relative max-h-[90vh] w-full max-w-lg p-0 flex flex-col\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"flex items-center justify-between pb-[14px]\">\n <h2 className=\"text-base sm:text-lg font-semibold text-[#E6E8ED]\">\n {title}\n </h2>\n <button\n onClick={onClose}\n aria-label=\"Close modal\"\n className=\"text-gray-400 hover:text-white text-xl sm:text-2xl px-2\"\n >\n ×\n </button>\n </div>\n <div\n style={{ maxHeight: \"70vh\" }}\n className=\"border border-[#292930] rounded-[9px] bg-[#0D0D10] shadow-lg flex flex-col overflow-y-auto [&::-webkit-scrollbar]:w-1.5 sm:[&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-[#292930] [&::-webkit-scrollbar-track]:border-0 [&::-webkit-scrollbar-thumb]:bg-[#808080] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:border-0\"\n >\n <div className={`${childrenPadding} relative`}>{children}</div>\n </div>\n </motion.div>\n </motion.div>\n )}\n </AnimatePresence>\n </SDKWrapper>\n );\n}\n","import React, { ReactNode } from \"react\";\n\ninterface SDKWrapperProps {\n children: ReactNode;\n className?: string;\n}\n\n/**\n * SDKWrapper component that wraps all SDK components with a root class\n * This ensures all Tailwind styles are scoped and don't affect the host application\n */\nexport function SDKWrapper({ children, className = \"\" }: SDKWrapperProps) {\n return <div className={`swype-sdk-root ${className}`}>{children}</div>;\n}\n","import { create } from \"zustand\";\nimport { CardStatus, UserCard } from \"../api/types\";\nimport { useSDKConfigStore } from \"./sdkConfigStore\";\nimport { getCardStatus } from \"../api/api\";\n\ninterface CardStatusState {\n status: CardStatus | null;\n userCard: UserCard | null;\n isUserCardDelegated: boolean;\n isLoading: boolean;\n error: string | null;\n // Internal methods for SDK use only\n _setCardStatus: (status: CardStatus, userCard: UserCard | null) => void;\n _setUserStatus: (status: CardStatus) => void;\n _setLoading: (loading: boolean) => void;\n _setError: (error: string | null) => void;\n _reset: () => void;\n _setUserCard: (userCard: UserCard | null) => void;\n _refreshCardStatus: () => Promise<void>;\n}\n\n// Helper function to check if card is delegated to the current EOA\nconst isCardDelegatedToCurrentEOA = (userCard: UserCard | null): boolean => {\n if (!userCard) return false;\n\n // Get current EOA from global config store\n const { eoaAddress } = useSDKConfigStore.getState();\n if (!eoaAddress) return false;\n\n // Card is delegated to this EOA if creditProviderAddress matches the current EOA\n return (\n userCard.creditProviderAddress.toLowerCase() === eoaAddress.toLowerCase()\n );\n};\n\nexport const useCardStatusStore = create<CardStatusState>((set) => ({\n status: null,\n userCard: null,\n isLoading: false,\n error: null,\n isUserCardDelegated: false,\n\n // Internal methods for SDK use only (prefixed with _)\n _setCardStatus: (status, userCard) => {\n const isCardDelegated = isCardDelegatedToCurrentEOA(userCard);\n set({\n status,\n userCard,\n isUserCardDelegated: isCardDelegated,\n });\n },\n\n _setLoading: (loading) => set({ isLoading: loading }),\n _setUserStatus: (status) => set({ status }),\n\n _setError: (error) => set({ error }),\n\n _reset: () =>\n set({\n status: null,\n userCard: null,\n isUserCardDelegated: false,\n isLoading: false,\n error: null,\n }),\n\n _setUserCard: (userCard) => {\n const isCardDelegated = isCardDelegatedToCurrentEOA(userCard);\n set({\n userCard,\n isUserCardDelegated: isCardDelegated,\n });\n },\n\n _refreshCardStatus: async () => {\n const { eoaAddress } = useSDKConfigStore.getState();\n if (!eoaAddress) {\n console.error(\"[_refreshCardStatus]: No EOA address found\");\n return;\n }\n const statusResponse = await getCardStatus(eoaAddress);\n if (statusResponse && statusResponse.data) {\n set({ status: statusResponse.data });\n }\n },\n}));\n","import { create } from \"zustand\";\nimport { dataFetcher } from \"../core/dataFetcher\";\n\ninterface SDKConfigState {\n eoaAddress: string | null;\n chainID: number | null;\n apiKey: string | null;\n baseUrl: string | null;\n apiPath: string | null;\n isInitialized: boolean;\n setConfig: (config: {\n eoaAddress: string;\n apiKey: string;\n chainID: number;\n baseUrl?: string;\n apiPath?: string;\n }) => void;\n reset: () => void;\n}\n\nexport const useSDKConfigStore = create<SDKConfigState>((set, get) => ({\n eoaAddress: null,\n chainID: null,\n apiKey: null,\n baseUrl: null,\n apiPath: null,\n isInitialized: false,\n\n setConfig: (config) => {\n // Reset previous state\n get().reset();\n\n // Set new config\n set({\n eoaAddress: config.eoaAddress,\n apiKey: config.apiKey,\n chainID: config.chainID,\n baseUrl: config.baseUrl || \"https://dev.console.fi\",\n apiPath: config.apiPath || \"/v1/vendor\",\n isInitialized: true,\n });\n\n // Note: Data fetching will be triggered after authentication is complete\n },\n\n reset: () => {\n set({\n eoaAddress: null,\n apiKey: null,\n baseUrl: null,\n apiPath: null,\n isInitialized: false,\n });\n\n // Reset data stores\n dataFetcher.reset();\n },\n}));\n","import axios from \"axios\";\nimport {\n CardStatusResponse,\n SumSubTokenResponse,\n CreditDelegationRequest,\n CreditDelegationResponse,\n LinkCardRequest,\n UserCardsResponse,\n CreditInformationResponse,\n CreateCardResponse,\n CardSecretResponse,\n TransactionsResponse,\n OnchainTransactionDetailsResponse,\n CardActionType,\n UpdateCardRequest,\n CreditType,\n CardLimitFrequency,\n EulerTotalAssetResponse,\n} from \"./types\";\nimport { getApiClient } from \"./httpClient\";\nimport {\n formatAvailableLimit,\n generateUUID,\n getCardSolverConfig,\n mapEulerMetadataToSuppliedAssets,\n waitFor,\n} from \"./utils\";\nimport {\n getMockCardStatus,\n getMockUserCards,\n getMockAaveCreditInformation,\n getMockEulerCreditInformation,\n getMockSumSubToken,\n getMockCreditDelegationTxData,\n getMockTransactions,\n transactionsMockData,\n} from \"./mockData\";\nimport { useSDKConfigStore } from \"../stores/sdkConfigStore\";\nimport { getAuthState } from \"./auth\";\n\nexport const getCardStatus = async (\n eoaAddress: string\n): Promise<CardStatusResponse> => {\n // Return mock data\n // return getMockCardStatus();\n\n // Real API call (commented out - uncomment to use real API)\n try {\n const client = getApiClient();\n const response = await client.get<CardStatusResponse>(`/cards/user/status`);\n return response.data;\n } catch (error) {\n if (axios.isAxiosError(error) && error.response?.status === 404) {\n return { data: \"userNotExisting\" };\n }\n throw error;\n }\n};\n\nexport const getUserCards = async (\n eoaAddress: string\n): Promise<UserCardsResponse> => {\n // Return mock data\n // return getMockUserCards();\n\n // Real API call (commented out - uncomment to use real API)\n try {\n const client = getApiClient();\n const response = await client.get<UserCardsResponse>(`/cards/user`);\n return response.data;\n } catch (err) {\n console.error(\"[getUserCards]\", err);\n return { data: null };\n }\n};\n\nexport const getCreditInformation = async (\n creditAddress: string,\n solverType: \"aave-v3\" | \"euler\" | \"hypurrFi\" | string\n): Promise<CreditInformationResponse> => {\n // Return mock data based on credit type\n // if (creditType === \"aave-v3\") {\n // return getMockAaveCreditInformation();\n // } else {\n // return getMockEulerCreditInformation();\n // }\n\n // Real API call (commented out - uncomment to use real API)\n try {\n const client = getApiClient();\n const response = await client.post<CreditInformationResponse>(\n `/cards/credit`,\n {\n solverType,\n config:\n solverType === \"aave-v3\"\n ? {\n borrowTokenAddress:\n \"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\",\n }\n : solverType === \"hypurrFi\"\n ? {\n borrowTokenAddress:\n \"0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb\",\n }\n : {\n borrowVaultAddress:\n \"0x0A1a3b5f2041F33522C4efc754a7D096f880eE16\",\n },\n }\n );\n\n // Transform availableLimit if it exists\n if (\n response.data?.data &&\n typeof response.data.data.availableLimit === \"number\"\n ) {\n response.data.data.availableLimit = formatAvailableLimit(\n response.data.data.availableLimit,\n solverType\n );\n response.data.data.amountDelegated = formatAvailableLimit(\n response.data.data.amountDelegated,\n solverType\n );\n }\n\n // If Euler, map vaults to suppliedAssets for compatibility (without mutating original)\n if (solverType === \"euler\" && response.data?.data?.metadata) {\n response.data.data.metadata = mapEulerMetadataToSuppliedAssets(\n response.data.data.metadata\n );\n }\n\n return response.data;\n } catch (error) {\n console.error(\"[getCreditInformation]\", error);\n return { data: null };\n }\n};\n\nexport const getSumSubAccessToken = async (\n userId: string,\n ipAddress?: string\n): Promise<SumSubTokenResponse[\"data\"] | undefined> => {\n // Return mock data\n // return getMockSumSubToken();\n\n // Real API call (commented out - uncomment to use real API)\n try {\n const client = getApiClient();\n const response = await client.post<SumSubTokenResponse>(\n \"/cards/sumsub/token\",\n { userId, ipAddress }\n );\n return response.data.data;\n } catch (error) {\n console.error(\"[error at getSumSubAccessToken]\", error);\n return undefined;\n }\n};\n\nexport const getCreditDelegationTxData = async (\n requestData: CreditDelegationRequest\n): Promise<{ data: CreditDelegationResponse }> => {\n // Return mock data\n // return getMockCreditDelegationTxData();\n\n // Real API call (commented out - uncomment to use real API\n const eoa = requestData.from;\n const payload = {\n creditType: requestData.creditType,\n creditLimit: requestData.creditLimit,\n userParams: requestData?.userParams,\n };\n const client = getApiClient();\n const response = await client.post<{ data: CreditDelegationResponse }>(\n `/cards/credit/delegate`,\n payload\n );\n return response.data;\n};\n\nexport const linkCardToCreditAccount = async (\n requestData: LinkCardRequest\n): Promise<any> => {\n const client = getApiClient();\n const response = await client.patch(\"/cards/\", requestData);\n return response.data;\n};\n\n/**\n * Updates a user's card by linking it to a credit provider using signed authorization.\n *\n * @param requestData - Request payload including credit provider details and EIP-712 auth data.\n * @returns An object indicating success or failure, with an optional message.\n */\nexport const updateCard = async (\n requestData: UpdateCardRequest\n): Promise<{ success: boolean; message?: string }> => {\n const { apiKey, baseUrl, apiPath } = useSDKConfigStore.getState();\n\n try {\n const client = axios.create({\n baseURL: `${baseUrl}${apiPath}`,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${requestData.auth}`,\n sessionID: generateUUID(),\n },\n });\n\n const response = await client.patch(`/cards/creditprovider`, {\n creditProviderAddress: requestData.creditProviderAddress,\n chainId: requestData.chainId,\n creditType: requestData.creditType,\n cardSolverConfig: getCardSolverConfig(requestData.creditType),\n });\n\n if (response.status === 200) {\n return { success: true };\n }\n\n return {\n success: false,\n message: response.data?.message || \"Failed to update card\",\n };\n } catch (error: any) {\n console.error(\"[updateCard]\", error);\n return {\n success: false,\n message:\n error?.response?.data?.data?.error ||\n error?.message ||\n \"Failed to update card\",\n };\n }\n};\n\nexport const createCard = async (\n userEoa: string,\n chainID: number,\n creditType: CreditType,\n auth: string\n): Promise<CreateCardResponse> => {\n const { apiKey, baseUrl, apiPath } = useSDKConfigStore.getState();\n\n const client = axios.create({\n baseURL: `${baseUrl}${apiPath}`,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${auth}`,\n sessionID: generateUUID(),\n },\n });\n\n const response = await client.post<CreateCardResponse>(`/cards/create`, {\n creditProviderAddress: userEoa,\n chainID: chainID,\n creditType: creditType,\n cardSolverConfig: getCardSolverConfig(creditType),\n });\n\n return response.data;\n};\n\n/**\n * Freezes or unfreezes a user's card using a signed request.\n *\n * @param eoa - The Ethereum address of the user.\n * @param cardID - The UUID of the card to be frozen/unfrozen.\n * @param chainId - The blockchain network's chain ID.\n * @param signature - EIP-712 signature for the freeze/unfreeze action.\n * @param timestamp - UNIX timestamp (in seconds) of the request.\n * @param freeze - Boolean flag indicating whether to freeze (true) or unfreeze (false) the card.\n * @param action - Action type string, e.g., \"freeze\" or \"unfreeze\", used in request headers.\n * @returns An object indicating success or failure with an optional error message.\n */\nexport const freezeCard = async (\n eoa: string,\n cardID: string,\n chainID: number,\n signature: string,\n timestamp: number,\n freeze: boolean,\n action: CardActionType\n): Promise<{ success: boolean; message?: string }> => {\n const { apiKey, baseUrl, apiPath } = useSDKConfigStore.getState();\n\n const authJson = {\n cardID,\n action,\n version: \"1\",\n timestamp,\n chainID,\n sig: signature,\n userAddr: eoa,\n };\n\n const encodedAuth = btoa(JSON.stringify(authJson));\n\n const client = axios.create({\n baseURL: `${baseUrl}${apiPath}`,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${encodedAuth}`,\n sessionID: generateUUID(),\n },\n });\n\n try {\n const response = await client.patch(`/cards/status`, {\n cardID,\n freeze,\n });\n // if 200 then return success\n if (response.status === 200) {\n return { success: true };\n }\n // if not 200, return failure with message\n return {\n success: false,\n message: response.data?.message || \"Failed to freeze card\",\n };\n } catch (error: any) {\n console.error(\"[freezeCard]\", error);\n return {\n success: false,\n message:\n error?.response?.data?.data?.error ||\n error?.message ||\n \"Failed to freeze card\",\n };\n }\n};\n\n/**\n * Updates the PIN of a card by sending encrypted PIN data using a signed request.\n *\n * @param eoa - The Ethereum address of the user.\n * @param cardID - The UUID of the card whose PIN is to be updated.\n * @param chainId - The blockchain network's chain ID.\n * @param sessionID - The session ID generated by the client (e.g., via Rain).\n * @param signature - EIP-712 signature for the PIN update action.\n * @param timestamp - UNIX timestamp (in seconds) of the request.\n * @param iv - Base64-encoded Initialization Vector (IV) used to encrypt the PIN.\n * @param data - Base64-encoded encrypted PIN data.\n * @param action - Action type string used in request headers.\n * @returns An object indicating success or failure with an optional error message.\n */\nexport const updatePin = async (\n eoa: string,\n cardID: string,\n chainID: number,\n sessionID: string,\n signature: string,\n timestamp: number,\n iv: string,\n data: string,\n action: CardActionType\n): Promise<{ success: boolean; message?: string }> => {\n const { apiKey, baseUrl, apiPath } = useSDKConfigStore.getState();\n\n const authJson = {\n cardID,\n action,\n version: \"1\",\n timestamp,\n chainID,\n sig: signature,\n userAddr: eoa,\n };\n\n const encodedAuth = btoa(JSON.stringify(authJson));\n\n const client = axios.create({\n baseURL: `${baseUrl}${apiPath}`,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${encodedAuth}`,\n sessionID,\n },\n });\n\n try {\n const response = await client.patch<{ success: boolean; message?: string }>(\n `/cards/pin`,\n { pin: { encryptedPin: { data, iv } } }\n );\n\n if (response.status === 200) {\n return { success: true };\n }\n\n return {\n success: false,\n message: response.data?.message || \"Failed to update PIN\",\n };\n } catch (error: any) {\n console.error(\"[updatePin]\", error);\n return {\n success: false,\n message:\n error?.response?.data?.data?.error ||\n error?.message ||\n \"Failed to update PIN\",\n };\n }\n};\n\n/**\n * Get encrypted card details for a user\n * @param eoa Ethereum address of the user\n * @param cardID UUID of the card\n * @param chainId Blockchain chain ID\n * @param sessionID Session identifier\n * @param signature EIP-712 signature\n * @param timestamp UNIX timestamp (in seconds)\n * @returns Encrypted PAN and CVC data\n */\nexport const getCardSecrets = async (\n eoa: string,\n cardID: string,\n chainID: number,\n sessionID: string,\n signature: string,\n timestamp: number,\n action: CardActionType\n): Promise<{\n encryptedPan: { iv: string; data: string };\n encryptedCvc: { iv: string; data: string };\n ExpirationMonth: string;\n ExpirationYear: string;\n}> => {\n const { apiKey, baseUrl, apiPath } = useSDKConfigStore.getState();\n\n const authJson = {\n cardID,\n action,\n version: \"1\",\n timestamp,\n chainID,\n sig: signature,\n userAddr: eoa,\n };\n\n const encodedAuth = btoa(JSON.stringify(authJson));\n\n const client = axios.create({\n baseURL: `${baseUrl}${apiPath}`,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${encodedAuth}`,\n sessionID,\n },\n });\n\n try {\n const response = await client.get(`/cards/secrets`);\n\n // If successful\n if (response.status === 200 && response.data?.data) {\n return response.data.data;\n }\n\n // Fallback if 200 but no data\n throw new Error(\"Unexpected response format while fetching card secrets.\");\n } catch (error: any) {\n console.error(\"[getCardSecrets] Error fetching card secrets:\", error);\n\n const errorMessage =\n error?.response?.data?.data?.error ||\n error?.response?.data?.message ||\n error?.message ||\n \"Failed to fetch card secrets\";\n\n throw new Error(errorMessage);\n }\n};\n\nexport const getCardLimit = async (\n cardID: string\n): Promise<{\n amount: number; // in cents\n frequency: CardLimitFrequency;\n}> => {\n const client = getApiClient();\n\n try {\n const response = await client.get(`/cards/limit/${cardID}`);\n\n // If successful\n if (response.status === 200 && response.data?.data) {\n return response.data.data;\n }\n\n // Fallback if 200 but no data\n throw new Error(\"Unexpected response format while fetching card limits.\");\n } catch (error: any) {\n console.error(\"[getCardLimit] Error fetching card limits:\", error);\n\n const errorMessage =\n error?.response?.data?.data?.error ||\n error?.response?.data?.message ||\n error?.message ||\n \"Failed to fetch card limits\";\n\n throw new Error(errorMessage);\n }\n};\n\nexport const updateLimit = async (\n eoa: string,\n cardID: string,\n chainID: number,\n signature: string,\n timestamp: number,\n limit: number,\n action: CardActionType\n): Promise<{ success: boolean; message?: string }> => {\n const { apiKey, baseUrl, apiPath } = useSDKConfigStore.getState();\n\n const authJson = {\n cardID,\n action,\n version: \"1\",\n timestamp,\n chainID,\n sig: signature,\n userAddr: eoa,\n };\n\n const encodedAuth = btoa(JSON.stringify(authJson));\n\n const client = axios.create({\n baseURL: `${baseUrl}${apiPath}`,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${encodedAuth}`,\n },\n });\n\n try {\n const response = await client.patch<{ success: boolean; message?: string }>(\n `/cards/limits`,\n { limit, frequency: \"per24HourPeriod\" }\n );\n\n if (response.status === 200) {\n return { success: true };\n }\n\n return {\n success: false,\n message: response.data?.message || \"Failed to update limit\",\n };\n } catch (error: any) {\n console.error(\"[updateLimit]\", error);\n return {\n success: false,\n message:\n error?.response?.data?.data?.error ||\n error?.message ||\n \"Failed to update limit\",\n };\n }\n};\n\n/**\n * Get the public key for card encryption\n * @returns Public key in PEM format\n */\nexport const getCardPublicKey = async (): Promise<{ data: string }> => {\n const client = getApiClient();\n const response = await client.get<{ data: string }>(\"/cards/pubkey\");\n return response.data;\n};\n\nexport const getTransactions = async (\n userAddress: string,\n cardID: string,\n limit?: number,\n cursor?: string\n): Promise<TransactionsResponse> => {\n // Return mock data\n\n // await waitFor(2000);\n // return transactionsMockData;\n // Real API call\n try {\n const client = getApiClient();\n const response = await client.get<import(\"./types\").TransactionsResponse>(\n `cards/transactions/${cardID}`,\n {\n params: {\n limit,\n cursor,\n },\n }\n );\n return response.data;\n } catch (error) {\n console.error(\"[getTransactions]\", error);\n // Return a default structure on error to prevent crashes\n return {\n data: {\n transactions: [],\n pagination: {\n limit: limit || 50,\n has_next: false,\n },\n },\n };\n }\n};\n\nexport const getOnchainTransactionDetails = async (\n transactionID: string\n): Promise<OnchainTransactionDetailsResponse> => {\n // Return mock data\n // return getMockOnchainTransactionDetails();\n\n // Real API call (commented out - uncomment to use real API)\n try {\n const client = getApiClient();\n const response = await client.get<OnchainTransactionDetailsResponse>(\n `cards/transactions/${transactionID}/onchain`\n );\n return response.data;\n } catch (error) {\n console.error(\"[getOnchainTransactionDetails]\", error);\n return { data: [] };\n }\n};\n\nexport const getUserIsUs = async (): Promise<{ data: boolean }> => {\n try {\n const client = getApiClient();\n const response = await client.get<{ data: boolean }>(`cards/user/is_us`);\n return response.data;\n } catch (error) {\n console.error(\"[getUserIsUs]\", error);\n return { data: false };\n }\n};\n\n/**\n * Fetches a preview of the Euler credit limits based on the provided vaults.\n * @param vaults Array of vault addresses to be used for the preview.\n * @returns An object containing credit and collateral amounts as strings.\n */\nexport const getEulerCardPreview = async (\n vaults: string[],\n creditType: CreditType = \"euler\"\n): Promise<{ data: { credit: string; collateral: string } }> => {\n try {\n const client = getApiClient();\n const response = await client.post<{\n data: { credit: string; collateral: string };\n }>(`/cards/credit/${creditType}/preview`, {\n vaults,\n borrowVaultAddress: \"0x0A1a3b5f2041F33522C4efc754a7D096f880eE16\",\n });\n return response.data;\n } catch (error) {\n console.error(\"[getEulerCardPreview]\", error);\n return { data: { credit: \"0\", collateral: \"0\" } };\n }\n};\n\n/**\n * Fetches the Euler health factor for the user.\n * @returns An object containing the health factor as a string.\n */\nexport const getEulerHealthFactor = async (): Promise<{\n data: { healthFactor: string };\n}> => {\n try {\n const client = getApiClient();\n const response = await client.get<{\n data: { healthFactor: string };\n }>(\n `/cards/euler/hf?borrowVaultAddress=0x0A1a3b5f2041F33522C4efc754a7D096f880eE16`\n );\n return response.data;\n } catch (error) {\n console.error(\"[getEulerHealthFactor]\", error);\n return { data: { healthFactor: \"0\" } };\n }\n};\n\n/**\n * Fetches the pending transaction balances for a specific card.\n * @param cardID The UUID of the card for which to fetch pending transaction balances.\n * @returns An object containing the status and balances of pending transactions.\n */\nexport const getPendingTransactionBalance = async (\n cardID: string\n): Promise<{\n data: {\n status: string;\n balances: { usd: number };\n };\n}> => {\n try {\n const client = getApiClient();\n const response = await client.get<{\n data: {\n status: string;\n balances: { usd: number };\n };\n }>(`/cards/transactions/balances/pending/${cardID}`);\n return response.data;\n } catch (error) {\n console.error(\"[getPendingTransactionBalances]\", error);\n return { data: { status: \"error\", balances: { usd: 0 } } };\n }\n};\n\n/**\n * Get Euler total assets for a specific chain ID\n * @param chainID - The chain ID to get assets for\n * @returns Promise with total assets in USD\n */\nexport const getEulerTotalAsset = async (\n chainID: number\n): Promise<EulerTotalAssetResponse> => {\n try {\n const client = getApiClient();\n const response = await client.get<EulerTotalAssetResponse>(\n `/cards/euler/assets/${chainID}`\n );\n return response.data;\n } catch (error) {\n console.error(\"[getEulerTotalAsset]\", error);\n return { data: { totalAssetsUSD: 0 } };\n }\n};\n","import axios, { AxiosError, AxiosInstance } from \"axios\";\nimport { useSDKConfigStore } from \"../stores/sdkConfigStore\";\nimport { ensureValidAuth, getAuthState, clearAuth } from \"./auth\";\n\n// Get API client with auth headers\nexport function getApiClient(): AxiosInstance {\n const { eoaAddress, apiKey, chainID, baseUrl, apiPath, isInitialized } =\n useSDKConfigStore.getState();\n\n if (!isInitialized || !apiKey) {\n throw new Error(\"SwypeSDK not initialized. Call initializeSwypeSDK first.\");\n }\n\n const authState = getAuthState();\n if (!authState.signature || !authState.deadline) {\n throw new Error(\n \"SwypeSDK auth not ready. Authentication signature missing.\"\n );\n }\n\n const fullBaseURL = `${baseUrl}${apiPath}`;\n\n // Encode in base 64\n const authJson = {\n timestamp: authState.deadline,\n sig: authState.signature,\n userAddr: eoaAddress,\n chainID,\n };\n\n const encodedAuth = btoa(JSON.stringify(authJson));\n\n const client = axios.create({\n baseURL: fullBaseURL,\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n Authorization: `Bearer ${encodedAuth}`,\n \"ngrok-skip-browser-warning\": \"true\", // Skip ngrok browser warning\n },\n });\n\n // Add request interceptor to ensure valid auth before each request\n client.interceptors.request.use(\n async (config) => {\n await ensureValidAuth();\n\n // Update headers with fresh auth if regenerated\n const freshAuthState = getAuthState();\n if (freshAuthState.signature && freshAuthState.deadline) {\n config.headers.set(\"Authorization\", `Bearer ${encodedAuth}`);\n config.headers.set(\"x-api-key\", apiKey);\n config.headers.set(\"ngrok-skip-browser-warning\", \"true\");\n // config.headers.set(\"X-Deadline\", freshAuthState.deadline.toString());\n }\n\n return config;\n },\n (error) => {\n return Promise.reject(error);\n }\n );\n\n // Add response interceptor for handling errors\n client.interceptors.response.use(\n (response) => response,\n (error: AxiosError) => {\n if (error.response) {\n console.error(\"API Error Response:\", {\n status: error.response.status,\n data: error.response.data,\n });\n\n // If auth error, clear auth state to force regeneration\n if (error.response.status === 401 || error.response.status === 403) {\n clearAuth();\n }\n }\n return Promise.reject(error);\n }\n );\n\n return client;\n}\n","import { useSDKConfigStore } from \"../stores/sdkConfigStore\";\nimport { CardActionType, UserCard } from \"./types\";\n\n// Auth state interface\ninterface AuthState {\n signature: string | null;\n deadline: number | null;\n}\n\n// Global auth state\nlet authState: AuthState = {\n signature: null,\n deadline: null,\n};\n\n// EIP712 Typed Data for card operations\nfunction createCardOperationTypedData(\n eoa: `0x${string}`, // EOA address\n cardId: UserCard[\"cardID\"],\n timestamp: number, // in seconds\n action: CardActionType,\n chainId: number\n) {\n return {\n types: {\n EIP712Domain: [\n { name: \"name\", type: \"string\" },\n { name: \"version\", type: \"string\" },\n { name: \"chainId\", type: \"uint256\" },\n ],\n CardOperation: [\n { name: \"eoa\", type: \"address\" },\n { name: \"cardId\", type: \"string\" },\n { name: \"action\", type: \"string\" },\n { name: \"timestamp\", type: \"uint256\" },\n ],\n },\n primaryType: \"CardOperation\",\n domain: {\n name: \"BrahmaCards\",\n version: \"1\",\n chainId: chainId,\n },\n message: {\n eoa,\n cardId,\n action,\n timestamp,\n },\n };\n}\n\n// EIP712 Typed Data for login\nconst createLoginTypedData = (\n eoaAddress: string,\n chainId: number,\n timestamp: number\n) => ({\n types: {\n EIP712Domain: [\n { name: \"name\", type: \"string\" },\n { name: \"version\", type: \"string\" },\n { name: \"chainId\", type: \"uint256\" },\n ],\n LoginRequest: [\n { name: \"eoa\", type: \"address\" },\n { name: \"message\", type: \"string\" },\n { name: \"timestamp\", type: \"uint256\" },\n { name: \"action\", type: \"string\" },\n ],\n },\n primaryType: \"LoginRequest\",\n domain: {\n name: \"BrahmaCards\",\n version: \"1\",\n chainId,\n },\n message: {\n eoa: eoaAddress,\n message: \"I'm a BrahmaCards user\",\n timestamp: timestamp,\n action: \"login\",\n },\n});\n\n// Generate card operation signature\n// This function creates a typed data structure for card operations and signs it\n// using the provided signTypedData function. It returns the signature for the operation.\nexport const generateCardOperationSign = async (\n eoa: `0x${string}`,\n chainId: number,\n cardId: UserCard[\"cardID\"],\n action: CardActionType,\n timestamp: number,\n signTypedData: (typedData: any) => Promise<string>\n): Promise<{ signature: string }> => {\n const typedData = createCardOperationTypedData(\n eoa,\n cardId,\n timestamp,\n action,\n chainId\n );\n\n const signature = await signTypedData(typedData);\n console.log({ typedData, signature, type: typeof signature });\n\n return { signature };\n};\n\n// Generate auth signature\nconst generateAuthSignature = async (\n eoaAddress: string,\n chainId: number,\n signTypedData: (typedData: any) => Promise<string>\n): Promise<{ signature: string; deadline: number }> => {\n // Set deadline to current time + 1 hour\n const deadline = Math.floor(Date.now() / 1000);\n\n const typedData = createLoginTypedData(eoaAddress, chainId, deadline);\n const signature = await signTypedData(typedData);\n\n console.log({ typedData, deadline, signature, type: typeof signature });\n\n return { signature, deadline };\n};\n\n// Export the generateAuthSignature function for external use\nexport const generateAuthSign = async (\n eoaAddress: string,\n chainId: number,\n signTypedData: (typedData: any) => Promise<string>\n): Promise<{ signature: string; deadline: number }> => {\n return generateAuthSignature(eoaAddress, chainId, signTypedData);\n};\n\n// Initialize authentication with pre-generated signature\nexport async function initializeAuth(\n eoaAddress: string,\n authSignature: { signature: string; deadline: number }\n): Promise<void> {\n try {\n authState = {\n signature: authSignature.signature,\n deadline: authSignature.deadline,\n };\n\n console.log(\"SwypeSDK auth initialized\");\n } catch (error) {\n console.error(\"Failed to initialize SwypeSDK auth:\", error);\n throw error;\n }\n}\n\n// Check if auth is expired and throw error if needed (no auto-renewal)\nexport const ensureValidAuth = async (): Promise<void> => {\n const { eoaAddress, isInitialized } = useSDKConfigStore.getState();\n\n if (!isInitialized || !eoaAddress) {\n throw new Error(\"SwypeSDK not initialized. Call initializeSwypeSDK first.\");\n }\n\n const currentTime = Math.floor(Date.now() / 1000);\n\n // If no auth or auth is expired, throw error since we can't auto-renew\n if (!authState.signature || !authState.deadline) {\n throw new Error(\n \"Authentication expired. Please re-initialize the SDK with a new signature.\"\n );\n }\n};\n\n// Get current auth state\nexport const getAuthState = (): AuthState => {\n return { ...authState };\n};\n\n// Clear auth state (useful for logout or auth errors)\nexport const clearAuth = (): void => {\n authState = {\n signature: null,\n deadline: null,\n };\n};\n","import forge from \"node-forge\";\nimport { v4, Version4Options } from \"uuid\";\nimport { CreditType } from \"./types\";\n\n// Format Units utility function (simplified version)\nexport const formatUnits = (value: bigint, decimals: number): string => {\n const divisor = BigInt(10 ** decimals);\n const quotient = value / divisor;\n const remainder = value % divisor;\n\n const formattedRemainder = remainder.toString().padStart(decimals, \"0\");\n return `${quotient.toString()}.${formattedRemainder}`;\n};\n\n// Helper function for formatting credit limits\nexport const formatAvailableLimit = (\n availableLimit: number,\n creditType: CreditType\n): number => {\n const decimals =\n creditType === \"aave-v3\" || creditType === \"hypurrFi\" ? 6 : 18;\n const formatted = Number(formatUnits(BigInt(availableLimit), decimals));\n return Number(formatted.toFixed(4));\n};\n\n// Mapper function to convert Euler metadata to include suppliedAssets\nexport function mapEulerMetadataToSuppliedAssets(metadata: any) {\n if (!Array.isArray(metadata?.vaults)) return metadata;\n\n const suppliedAssets = metadata.vaults.map((vault: any) => {\n const formattedAsset = Number(formatUnits(BigInt(vault.assets || 0), 18));\n\n console.log({ formattedAsset });\n return {\n UsageAsCollateralEnabledOnUser: true, // Assume true for Euler\n amount: formattedAsset,\n amountUSD: 0, // No USD value provided, set to 0 or calculate if possible\n collateralAmountBase: String(formattedAsset),\n collateralAmountBig: formattedAsset,\n supplyRate: 0, // Not provided by Euler, set to 0 or fetch if available\n underlyingAsset: vault.vault || \"\",\n };\n });\n\n const suppliedValue = suppliedAssets.reduce(\n (sum: number, asset: any) => sum + asset.amountUSD,\n 0\n );\n\n return {\n ...metadata,\n suppliedAssets,\n suppliedValue,\n };\n}\n\nexport const waitFor = async (ms = 1500) =>\n await new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Encrypts a PIN using AES-GCM with a session key provided by Rain.\n *\n * @param pin - The raw numeric PIN (e.g. \"1234\")\n * @param sessionKey - Hex-encoded session key from Rain\n * @returns An object containing base64-encoded IV and encrypted data (with tag)\n */\nexport function encryptPinWithSessionKey(\n pin: string,\n sessionKey: string\n): {\n iv: string;\n data: string;\n} {\n // Step 1: Format PIN as `2{lenInHex}{PIN}{F-padding}`\n const hexLength = pin.length.toString(16);\n const formattedPin = `2${hexLength}${pin}${\"F\".repeat(14 - pin.length)}`;\n\n // Step 2: Generate 16-byte IV\n const ivBytes = forge.random.getBytesSync(16);\n const encodedIV = forge.util.encode64(ivBytes);\n\n // Step 3: Convert sessionKey to bytes\n const keyBytes = forge.util.hexToBytes(sessionKey);\n\n // Step 4: Create AES-GCM cipher\n const cipher = forge.cipher.createCipher(\"AES-GCM\", keyBytes);\n cipher.start({ iv: ivBytes });\n\n // Step 5: Encrypt the formatted PIN\n cipher.update(forge.util.createBuffer(formattedPin));\n cipher.finish();\n\n // Step 6: Concatenate ciphertext + tag, then base64 encode\n const encrypted = cipher.output.getBytes() + cipher.mode.tag.getBytes();\n const encodedEncrypted = forge.util.encode64(encrypted);\n\n return {\n iv: encodedIV,\n data: encodedEncrypted,\n };\n}\n\nexport const generateUUID = (options?: Version4Options) => {\n return v4(options);\n};\n\n/**\n * Returns the appropriate cardSolverConfig object for a given creditType.\n * Used to construct the payload for card creation.\n *\n * @param creditType - The type of credit (\"aave-v3\", \"hypurrFi\", or others)\n * @returns The cardSolverConfig object for the specified creditType\n */\nexport function getCardSolverConfig(\n creditType: string\n): Record<string, string> {\n if (creditType === \"aave-v3\") {\n return {\n borrowTokenAddress: \"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\",\n };\n } else if (creditType === \"hypurrFi\") {\n return {\n borrowTokenAddress: \"0xb8ce59fc3717ada4c02eadf9682a9e934f625ebb\",\n };\n } else {\n return {\n borrowVaultAddress: \"0x0A1a3b5f2041F33522C4efc754a7D096f880eE16\",\n };\n }\n}\n","import { create } from \"zustand\";\nimport { CreditInformationResponse, CreditType } from \"../api/types\";\nimport { getCreditInformation } from \"../api/api\";\nimport { useSDKConfigStore } from \"./sdkConfigStore\";\n\ninterface CreditState {\n creditInfo: Record<CreditType, CreditInformationResponse | null>;\n isLoading: boolean;\n error: string | null;\n // Internal methods for SDK use only\n _setCreditInfo: (type: CreditType, info: CreditInformationResponse) => void;\n _setLoading: (loading: boolean) => void;\n _setError: (error: string | null) => void;\n _reset: () => void;\n // New function to refresh positions\n _refreshPositions: (creditTypes: CreditType[]) => Promise<void>;\n}\n\nexport const useCreditStore = create<CreditState>((set, get) => ({\n creditInfo: {\n aave: null,\n euler: null,\n hypurrFi: null,\n },\n isLoading: false,\n error: null,\n\n // Internal methods for SDK use only (prefixed with _)\n _setCreditInfo: (type, info) =>\n set((state) => ({\n creditInfo: {\n ...state.creditInfo,\n [type]: info,\n },\n })),\n\n _setLoading: (loading) => set({ isLoading: loading }),\n _setError: (error) => set({ error }),\n\n _reset: () =>\n set({\n creditInfo: {\n card: null,\n aave: null,\n euler: null,\n },\n isLoading: false,\n error: null,\n }),\n\n // New function to refresh positions for specified credit types\n _refreshPositions: async (creditTypes: CreditType[]) => {\n const { eoaAddress, isInitialized } = useSDKConfigStore.getState();\n\n if (!isInitialized || !eoaAddress) {\n console.error(\n \"❌ SDK: Cannot refresh positions - SDK not initialized or no EOA address\"\n );\n return;\n }\n\n console.log(\"🔄 SDK: Refreshing positions for:\", creditTypes);\n\n // Set loading state\n set({ error: null });\n\n try {\n // Fetch all requested credit types in parallel\n const fetchPromises = creditTypes.map((creditType) =>\n getCreditInformation(\n eoaAddress,\n creditType === \"aave\" ? \"aave-v3\" : creditType\n )\n );\n\n const responses = await Promise.all(fetchPromises);\n\n // Update store with fetched data\n responses.forEach((response, index) => {\n const creditType = creditTypes[index];\n get()._setCreditInfo(\n creditType === \"aave-v3\" ? \"aave\" : creditType,\n response\n );\n });\n\n console.log(\"✅ SDK: Successfully refreshed positions for:\", creditTypes);\n } catch (error) {\n console.error(\"❌ SDK: Error refreshing positions:\", error);\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to refresh positions\";\n set({ error: errorMessage });\n }\n },\n}));\n","import { create } from \"zustand\";\nimport { Transaction, Pagination } from \"../api/types\";\n\ninterface TransactionsState {\n transactions: Transaction[];\n pagination: Pagination | null;\n isLoading: boolean;\n loadingNextPage: boolean;\n initialFetchDone: boolean;\n error: string | null;\n pendingTxnAmount: number;\n // Internal methods for SDK use only\n _setTransactions: (transactions: Transaction[]) => void;\n _appendTransactions: (transactions: Transaction[]) => void;\n _setPagination: (pagination: Pagination) => void;\n _setLoading: (loading: boolean) => void;\n _setLoadingNextPage: (loading: boolean) => void;\n _setInitialFetchDone: (done: boolean) => void;\n _setError: (error: string | null) => void;\n _setPendingTxnAmount: (amount: number) => void;\n _reset: () => void;\n}\n\nexport const useTransactionsStore = create<TransactionsState>((set) => ({\n transactions: [],\n pagination: null,\n isLoading: false,\n loadingNextPage: false,\n initialFetchDone: false,\n error: null,\n pendingTxnAmount: 0,\n\n // Internal methods for SDK use only (prefixed with _)\n _setTransactions: (transactions) => set({ transactions }),\n _appendTransactions: (newTransactions) =>\n set((state) => ({\n transactions: [...state.transactions, ...newTransactions],\n })),\n _setPagination: (pagination) => set({ pagination }),\n _setLoading: (loading) => set({ isLoading: loading }),\n _setLoadingNextPage: (loading) => set({ loadingNextPage: loading }),\n _setInitialFetchDone: (done) => set({ initialFetchDone: done }),\n _setError: (error) => set({ error }),\n _setPendingTxnAmount: (amount) => set({ pendingTxnAmount: amount }),\n _reset: () =>\n set({\n transactions: [],\n pagination: null,\n isLoading: false,\n loadingNextPage: false,\n initialFetchDone: false,\n error: null,\n }),\n}));\n","import { getCardStatus, getUserCards, getCreditInformation } from \"../api/api\";\nimport { useCardStatusStore } from \"../stores/cardStatusStore\";\nimport { useCreditStore } from \"../stores/creditStore\";\nimport { useSDKConfigStore } from \"../stores/sdkConfigStore\";\nimport { useTransactionsStore } from \"../stores/transactionsStore\";\n\nexport const BASE_CHAIN_ID = 8453; // Base chain ID\nexport const HYPER_EVM_CHAIN_ID = 999; // HyperEVM chain ID\n\nclass DataFetcher {\n private static instance: DataFetcher;\n priva