UNPKG

swype-sdk

Version:

Official SDK for integrating Swype payment and credit services

1,221 lines (1,190 loc) 40.2 kB
// src/widgets/kyc/KycModal.tsx import { useMemo, useEffect as useEffect2 } from "react"; // src/components/Modal.tsx import { motion, AnimatePresence } from "framer-motion"; // src/components/SDKWrapper.tsx import { jsx } from "react/jsx-runtime"; function SDKWrapper({ children, className = "" }) { return /* @__PURE__ */ jsx("div", { className: `swype-sdk-root ${className}`, children }); } // src/components/Modal.tsx import { jsx as jsx2, jsxs } from "react/jsx-runtime"; function Modal({ title, children, onClose, onOutsideClick, overlayOpacity = "bg-black bg-opacity-100", topDistance = "pt-[100px]", open = true }) { return /* @__PURE__ */ jsx2(SDKWrapper, { children: /* @__PURE__ */ jsx2(AnimatePresence, { children: open && /* @__PURE__ */ jsx2( motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: `fixed inset-0 z-50 flex items-start justify-center ${topDistance} ${overlayOpacity} px-4 sm:px-0`, onClick: onOutsideClick || onClose, "aria-modal": "true", role: "dialog", children: /* @__PURE__ */ jsxs( motion.div, { initial: { scale: 0.95, opacity: 0 }, animate: { scale: 1, opacity: 1 }, exit: { scale: 0.95, opacity: 0 }, transition: { duration: 0.2 }, className: "relative max-h-[90vh] w-full max-w-lg p-0 flex flex-col", onClick: (e) => e.stopPropagation(), children: [ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pb-[14px]", children: [ /* @__PURE__ */ jsx2("h2", { className: "text-base sm:text-lg font-semibold text-[#E6E8ED]", children: title }), /* @__PURE__ */ jsx2( "button", { onClick: onClose, "aria-label": "Close modal", className: "text-gray-400 hover:text-white text-xl sm:text-2xl px-2", children: "\xD7" } ) ] }), /* @__PURE__ */ jsx2("div", { className: "border border-[#292930] rounded-[9px] bg-[#0D0D10] shadow-lg flex flex-col max-h-[70vh] 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", children: /* @__PURE__ */ jsx2("div", { className: "px-4 sm:px-6 py-4 sm:py-6 relative", children }) }) ] } ) } ) }) }); } // src/stores/cardStatusStore.ts import { create as create3 } from "zustand"; // src/stores/sdkConfigStore.ts import { create as create2 } from "zustand"; // src/api/api.ts import axios2 from "axios"; // src/api/httpClient.ts import axios from "axios"; // src/api/auth.ts var authState = { signature: null, deadline: null }; var createLoginTypedData = (eoaAddress, timestamp) => ({ types: { EIP712Domain: [ { name: "name", type: "string" }, { name: "version", type: "string" } ], LoginRequest: [ { name: "eoa", type: "address" }, { name: "message", type: "string" }, { name: "timestamp", type: "uint256" }, { name: "action", type: "string" } ] }, primaryType: "LoginRequest", domain: { name: "BrahmaCards", version: "1" }, message: { eoa: eoaAddress, message: "I'm a BrahmaCards user", timestamp, action: "login" } }); var generateAuthSignature = async (eoaAddress, signTypedData) => { const deadline = Math.floor(Date.now() / 1e3) + 3600; const typedData = createLoginTypedData(eoaAddress, deadline); const signature = await signTypedData(typedData); console.log({ typedData, deadline, signature, type: typeof signature }); return { signature, deadline }; }; var generateAuthSign = async (eoaAddress, signTypedData) => { return generateAuthSignature(eoaAddress, signTypedData); }; async function initializeAuth(eoaAddress, authSignature) { try { authState = { signature: authSignature.signature, deadline: authSignature.deadline }; console.log("SwypeSDK auth initialized"); } catch (error) { console.error("Failed to initialize SwypeSDK auth:", error); throw error; } } var ensureValidAuth = async () => { const { eoaAddress, isInitialized } = useSDKConfigStore.getState(); if (!isInitialized || !eoaAddress) { throw new Error("SwypeSDK not initialized. Call initializeSwypeSDK first."); } const currentTime = Math.floor(Date.now() / 1e3); if (!authState.signature || !authState.deadline || authState.deadline <= currentTime) { throw new Error( "Authentication expired. Please re-initialize the SDK with a new signature." ); } }; var getAuthState = () => { return { ...authState }; }; var clearAuth = () => { authState = { signature: null, deadline: null }; }; // src/api/httpClient.ts function getApiClient() { const { apiKey, baseUrl, apiPath, isInitialized } = useSDKConfigStore.getState(); if (!isInitialized || !apiKey) { throw new Error("SwypeSDK not initialized. Call initializeSwypeSDK first."); } const authState2 = getAuthState(); if (!authState2.signature || !authState2.deadline) { throw new Error( "SwypeSDK auth not ready. Authentication signature missing." ); } const fullBaseURL = `${baseUrl}${apiPath}`; const client = axios.create({ baseURL: fullBaseURL, headers: { "Content-Type": "application/json", "x-api-key": apiKey, Authorization: `Bearer ${authState2.signature}`, "X-Deadline": authState2.deadline.toString() } }); client.interceptors.request.use( async (config) => { await ensureValidAuth(); const freshAuthState = getAuthState(); if (freshAuthState.signature && freshAuthState.deadline) { config.headers.set( "Authorization", `Bearer ${freshAuthState.signature}` ); config.headers.set("X-Deadline", freshAuthState.deadline.toString()); } return config; }, (error) => { return Promise.reject(error); } ); client.interceptors.response.use( (response) => response, (error) => { if (error.response) { console.error("API Error Response:", { status: error.response.status, data: error.response.data }); if (error.response.status === 401 || error.response.status === 403) { clearAuth(); } } return Promise.reject(error); } ); return client; } // src/api/utils.ts var formatUnits = (value, decimals) => { const divisor = BigInt(10 ** decimals); const quotient = value / divisor; const remainder = value % divisor; const formattedRemainder = remainder.toString().padStart(decimals, "0"); return `${quotient.toString()}.${formattedRemainder}`; }; var formatAvailableLimit = (availableLimit, creditType) => { const decimals = creditType === "aave-v3" ? 6 : 18; const formatted = Number(formatUnits(BigInt(availableLimit), decimals)); return Number(formatted.toFixed(4)); }; function mapEulerMetadataToSuppliedAssets(metadata) { if (!Array.isArray(metadata?.vaults)) return metadata; const suppliedAssets = metadata.vaults.map((vault) => { const formattedAsset = Number(formatUnits(BigInt(vault.assets || 0), 18)); console.log({ formattedAsset }); return { UsageAsCollateralEnabledOnUser: true, // Assume true for Euler amount: formattedAsset, amountUSD: 0, // No USD value provided, set to 0 or calculate if possible collateralAmountBase: String(formattedAsset), collateralAmountBig: formattedAsset, supplyRate: 0, // Not provided by Euler, set to 0 or fetch if available underlyingAsset: vault.vault || "" }; }); const suppliedValue = suppliedAssets.reduce( (sum, asset) => sum + asset.amountUSD, 0 ); return { ...metadata, suppliedAssets, suppliedValue }; } // src/api/api.ts var getCardStatus = async (eoaAddress) => { try { const client = getApiClient(); const response = await client.get( `/cards/user/${eoaAddress}/status` ); return response.data; } catch (error) { if (axios2.isAxiosError(error) && error.response?.status === 404) { return { data: "userNotExisting" }; } throw error; } }; var getUserCards = async (eoaAddress) => { try { const client = getApiClient(); const response = await client.get( `/cards/user/${eoaAddress}` ); return response.data; } catch (err) { console.error("[getUserCards]", err); return { data: null }; } }; var getCreditInformation = async (creditAddress, creditType) => { try { const client = getApiClient(); const response = await client.get( `/cards/credit/${creditAddress}/${creditType}` ); if (response.data?.data && typeof response.data.data.availableLimit === "number") { response.data.data.availableLimit = formatAvailableLimit( response.data.data.availableLimit, creditType ); response.data.data.amountDelegated = formatAvailableLimit( response.data.data.amountDelegated, creditType ); } if (creditType === "euler" && response.data?.data?.metadata) { response.data.data.metadata = mapEulerMetadataToSuppliedAssets( response.data.data.metadata ); } return response.data; } catch (error) { console.error("[getCreditInformation]", error); return { data: null }; } }; var getSumSubAccessToken = async (userId, ipAddress) => { try { const client = getApiClient(); const response = await client.post( "/cards/sumsub/token", { userId, ipAddress } ); return response.data.data; } catch (error) { console.error("[error at getSumSubAccessToken]", error); return void 0; } }; var getCreditDelegationTxData = async (requestData) => { const client = getApiClient(); const response = await client.post( "/cards/credit/delegate", requestData ); return response.data; }; var linkCardToCreditAccount = async (requestData) => { const client = getApiClient(); const response = await client.patch("/cards/", requestData); return response.data; }; var createCard = async (userEoa) => { const client = getApiClient(); const response = await client.post( `/card/create/${userEoa}` ); return response.data; }; var freezeCard = async (userEoa, cardID, freeze) => { const client = getApiClient(); try { const response = await client.patch( `/cards/user/${userEoa}/status/${cardID}`, { cardID, freeze } ); if (response.status === 200) { return { success: true }; } return { success: false, message: response.data?.message || "Failed to freeze card" }; } catch (error) { console.error("[freezeCard]", error); return { success: false, message: error?.message || "Failed to freeze card" }; } }; // src/stores/creditStore.ts import { create } from "zustand"; var useCreditStore = create((set) => ({ creditInfo: { card: null, aave: null, euler: null }, isLoading: false, error: null, // Internal methods for SDK use only (prefixed with _) _setCreditInfo: (type, info) => set((state) => ({ creditInfo: { ...state.creditInfo, [type]: info } })), _setLoading: (loading) => set({ isLoading: loading }), _setError: (error) => set({ error }), _reset: () => set({ creditInfo: { card: null, aave: null, euler: null }, isLoading: false, error: null }) })); // src/core/dataFetcher.ts var DataFetcher = class _DataFetcher { static instance; hasFetched = false; static getInstance() { if (!_DataFetcher.instance) { _DataFetcher.instance = new _DataFetcher(); } return _DataFetcher.instance; } async fetchAllData() { const { eoaAddress, isInitialized } = useSDKConfigStore.getState(); if (!isInitialized || !eoaAddress || this.hasFetched) { return; } console.log("\u{1F680} SDK: Starting data fetch for:", eoaAddress); const cardStore = useCardStatusStore.getState(); const creditStore = useCreditStore.getState(); cardStore._setLoading(true); creditStore._setLoading(true); try { const [ cardStatusResponse, userCardsResponse, aaveCreditResponse, eulerCreditResponse ] = await Promise.all([ getCardStatus(eoaAddress), getUserCards(eoaAddress), getCreditInformation(eoaAddress, "aave-v3"), getCreditInformation(eoaAddress, "euler") ]); const userCard = userCardsResponse.data?.[0] || null; cardStore._setCardStatus(cardStatusResponse.data, userCard); creditStore._setCreditInfo("aave", aaveCreditResponse); creditStore._setCreditInfo("euler", eulerCreditResponse); this.hasFetched = true; console.log("\u2705 SDK: Successfully fetched all data for:", eoaAddress); } catch (error) { console.error("\u274C SDK: Error fetching data:", error); const errorMessage = error instanceof Error ? error.message : "Failed to fetch data"; cardStore._setError(errorMessage); creditStore._setError(errorMessage); } finally { cardStore._setLoading(false); creditStore._setLoading(false); } } // Public method to trigger data fetching after authentication async fetchDataAfterAuth() { this.hasFetched = false; await this.fetchAllData(); } reset() { this.hasFetched = false; useCardStatusStore.getState()._reset(); useCreditStore.getState()._reset(); } }; var dataFetcher = DataFetcher.getInstance(); // src/stores/sdkConfigStore.ts var useSDKConfigStore = create2((set, get) => ({ eoaAddress: null, apiKey: null, baseUrl: null, apiPath: null, isInitialized: false, setConfig: (config) => { get().reset(); set({ eoaAddress: config.eoaAddress, apiKey: config.apiKey, baseUrl: config.baseUrl || "https://dev.console.fi", apiPath: config.apiPath || "/v1/vendor", isInitialized: true }); }, reset: () => { set({ eoaAddress: null, apiKey: null, baseUrl: null, apiPath: null, isInitialized: false }); dataFetcher.reset(); } })); // src/stores/cardStatusStore.ts var isCardDelegatedToCurrentEOA = (userCard) => { if (!userCard) return false; const { eoaAddress } = useSDKConfigStore.getState(); if (!eoaAddress) return false; return userCard.holdingAddress.toLowerCase() === eoaAddress.toLowerCase(); }; var useCardStatusStore = create3((set) => ({ status: null, userCard: null, isLoading: false, error: null, isUserCardDelegated: false, // Internal methods for SDK use only (prefixed with _) _setCardStatus: (status, userCard) => { const isCardDelegated = isCardDelegatedToCurrentEOA(userCard); set({ status, userCard, isUserCardDelegated: isCardDelegated }); }, _setLoading: (loading) => set({ isLoading: loading }), _setUserStatus: (status) => set({ status }), _setError: (error) => set({ error }), _reset: () => set({ status: null, userCard: null, isUserCardDelegated: false, isLoading: false, error: null }) })); // src/stores/useKycStore.ts import { create as create4 } from "zustand"; var useKycStore = create4((set) => ({ acceptedTerms: false, acceptedRainTerms: false, isKycModalOpen: false, isSumsubProfileVerified: false, setAcceptedTerms: (accepted) => set({ acceptedTerms: accepted }), setAcceptedRainTerms: (accepted) => set({ acceptedRainTerms: accepted }), setKycModalOpen: (open) => set({ isKycModalOpen: open }), setIsSumsubProfileVerified: (verified) => set({ isSumsubProfileVerified: verified }) })); // src/widgets/kyc/SumSubConsentView.tsx import { useState } from "react"; // src/components/shared/Checkbox.tsx import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime"; function Checkbox({ checked, onChange, label, className }) { return /* @__PURE__ */ jsxs2( "label", { className: `flex items-center gap-2 p-[20px_16px] border-[1.125px] border-[#18181C] bg-[#18181C] align-self-stretch ${className || ""}`, style: { borderRadius: 12 }, children: [ /* @__PURE__ */ jsx3("span", { children: /* @__PURE__ */ jsx3( "input", { type: "checkbox", checked, onChange: (e) => onChange(e.target.checked), className: "peer appearance-none w-[15px] h-[15px] rounded-[3px] border border-[#CBCED1] bg-[#18181C] checked:bg-[#18181C] checked:border-[#CBCED1] flex items-center justify-center transition-colors duration-150 focus:outline-none relative cursor-pointer" } ) }), /* @__PURE__ */ jsx3("span", { className: "pointer-events-none absolute w-[15px] h-[15px] flex items-center justify-center cursor-pointer", children: checked && /* @__PURE__ */ jsxs2( "svg", { width: "15", height: "15", viewBox: "0 0 27 27", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [ /* @__PURE__ */ jsx3( "rect", { x: "1.5", y: "1.5", width: "24", height: "24", rx: "6", fill: "#18181C", stroke: "#CBCED1", strokeWidth: "3" } ), /* @__PURE__ */ jsx3( "path", { d: "M7 14L12 19L20 9", stroke: "#CBCED1", strokeWidth: "3", strokeLinecap: "round", strokeLinejoin: "round" } ) ] } ) }), /* @__PURE__ */ jsx3("span", { className: "text-[#CBCED1] font-['Neue Montreal'] text-[15.75px] font-medium leading-[22.5px] ml-2 select-none", children: label }) ] } ); } // src/widgets/kyc/SumSubConsentView.tsx import { InfoCircledIcon } from "@radix-ui/react-icons"; import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime"; function SumSubConsentView() { const [checked1, setChecked1] = useState(false); const { setAcceptedTerms } = useKycStore(); return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col gap-6 w-full pt-4", children: [ /* @__PURE__ */ jsx4("p", { className: "text-[#CBCED1] font-['Neue Montreal'] text-[18px] font-medium leading-[22.5px]", children: "Provide consent to proceed" }), /* @__PURE__ */ jsx4("div", { className: "flex flex-col gap-1 w-full", children: /* @__PURE__ */ jsx4( Checkbox, { checked: checked1, onChange: setChecked1, label: "I accept E-Sign consent", className: "rounded-t-md" } ) }), /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [ /* @__PURE__ */ jsx4("span", { children: /* @__PURE__ */ jsx4( InfoCircledIcon, { color: "#CBCED1", style: { width: "20px", height: "20px" } } ) }), /* @__PURE__ */ jsx4("p", { className: "text-[#CBCED1] font-['Neue Montreal'] text-[15.75px] font-medium leading-[22.5px]", children: "BrahmaFi card is not affiliated with swype.fun , AAVE, Euler or other Defi and issued subject to separate terms provided by Issuer." }) ] }), /* @__PURE__ */ jsx4( "button", { className: `flex h-[44px] justify-center items-center gap-1 self-stretch rounded-lg bg-white backdrop-blur-[6px] text-black font-medium text-[16px] transition ease-in-out ${checked1 ? "cursor-pointer opacity-100" : "cursor-not-allowed opacity-50"}`, disabled: !checked1, onClick: () => setAcceptedTerms(true), children: "Continue to Sumsub Verification" } ), /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [ /* @__PURE__ */ jsx4("span", { children: /* @__PURE__ */ jsx4( InfoCircledIcon, { color: "#CBCED1", style: { width: "20px", height: "20px" } } ) }), /* @__PURE__ */ jsx4("p", { className: "text-[#CBCED1] font-['Neue Montreal'] text-[15.75px] font-medium leading-[22.5px]", children: "Brahma never stores your data we use Sumsub that completes the verification process to ensure safety" }) ] }) ] }); } // src/widgets/kyc/KycPendingViaRain.tsx import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime"; function KycPendingViaRain() { return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-[10px]", style: { gap: "10px" }, children: [ /* @__PURE__ */ jsxs4( "div", { className: "rounded-[4px] border border-[#292930] bg-black flex flex-col items-center justify-center gap-[15px] h-[372px]", style: { height: "372px", gap: "15px" }, children: [ /* @__PURE__ */ jsx5(PendingSvg, {}), /* @__PURE__ */ jsxs4("p", { className: "text-[#E6E8ED] text-center font-['Neue Montreal'] text-[23.391px] font-medium leading-[120%]", children: [ "Processing your", /* @__PURE__ */ jsx5("br", {}), "KYC Verificationn" ] }) ] } ), /* @__PURE__ */ jsx5("p", { className: "text-[#E6E8ED] text-center font-['Neue Montreal'] text-[16px] font-medium leading-[120%]", children: "Your KYC verification is pending and may take up to 2 days. Please check back later for updates." }) ] }); } var PendingSvg = () => { return /* @__PURE__ */ jsxs4( "svg", { width: "88", height: "89", viewBox: "0 0 88 89", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [ /* @__PURE__ */ jsx5( "path", { d: "M5.49609 3.19922H43.7531L54.4651 12.993V72.98H5.49609V3.19922Z", fill: "#FFAD0D" } ), /* @__PURE__ */ jsx5( "path", { d: "M41.2347 8.17186H7.9063V67.5467H52.3441V19.3047H41.2347V8.17186ZM7.9063 0.75H44.9378L59.7504 15.5937V67.5467C59.7504 69.5151 58.9701 71.4029 57.5812 72.7948C56.1922 74.1867 54.3084 74.9686 52.3441 74.9686H7.9063C5.94203 74.9686 4.05821 74.1867 2.66926 72.7948C1.2803 71.4029 0.5 69.5151 0.5 67.5467V8.17186C0.5 6.20346 1.2803 4.31568 2.66926 2.92381C4.05821 1.53194 5.94203 0.75 7.9063 0.75ZM15.3126 34.1484H44.9378V41.5702H15.3126V34.1484ZM15.3126 48.9921H44.9378V56.414H15.3126V48.9921Z", fill: "#0D0D10" } ), /* @__PURE__ */ jsx5( "path", { d: "M54.4465 61.1122C44.3921 61.1122 24.3926 66.6428 24.3926 77.5397V85.7535H84.5005V77.5397C84.5005 66.6428 64.5009 61.1122 54.4465 61.1122ZM61.0584 54.6894C65.2113 52.3348 68.1074 47.8994 68.1074 42.7521C68.1074 35.1954 61.9873 29.0625 54.4465 29.0625C46.9057 29.0625 40.7856 35.1954 40.7856 42.7521C40.7856 47.8994 43.6817 52.3348 47.8347 54.6894C49.8018 55.7846 52.0422 56.4417 54.4465 56.4417C56.8508 56.4417 59.0912 55.7846 61.0584 54.6894Z", fill: "white", stroke: "#0D0D10", strokeWidth: "5.25554" } ) ] } ); }; // src/widgets/kyc/PreRainTerms.tsx import { useState as useState2 } from "react"; import { Fragment, jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime"; function PreRainTerms() { const [checked1, setChecked1] = useState2(false); const [checked2, setChecked2] = useState2(false); const [checked3, setChecked3] = useState2(false); const { setAcceptedRainTerms } = useKycStore(); const allChecked = checked1 && checked2 && checked3; return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-6 w-full pt-4", children: [ /* @__PURE__ */ jsx6("div", { className: "text-[#CBCED1] font-['Neue Montreal'] text-[18px] font-medium leading-[22.5px]", children: "By continuing you accept our terms" }), /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-1 w-full", children: [ /* @__PURE__ */ jsx6( Checkbox, { checked: checked1, onChange: setChecked1, label: /* @__PURE__ */ jsxs5(Fragment, { children: [ "I accept the", " ", /* @__PURE__ */ jsx6("a", { href: "#", className: "underline", children: "BrahmaFi Card Terms Privacy Policy" }) ] }), className: "rounded-t-md" } ), /* @__PURE__ */ jsx6( Checkbox, { checked: checked2, onChange: setChecked2, label: "I certify that the information I have provided is accurate and that I will abide by all the rules and requirements related to my BrahmaFi Card" } ), /* @__PURE__ */ jsx6( Checkbox, { checked: checked3, onChange: setChecked3, label: /* @__PURE__ */ jsxs5(Fragment, { children: [ "I confirm I'm not a U.S. resident, have reviewed the", " ", /* @__PURE__ */ jsx6("a", { href: "#", className: "underline", children: "supported countries list" }), ", comply with local laws, and understand the BrahmaFi card is not an unauthorised solicitation." ] }), className: "rounded-b-md" } ) ] }), /* @__PURE__ */ jsx6( "button", { className: `flex h-[44px] justify-center items-center gap-1 self-stretch rounded-lg bg-white backdrop-blur-[6px] text-black font-medium ${!allChecked ? "opacity-50 cursor-not-allowed" : ""}`, disabled: !allChecked, onClick: () => setAcceptedRainTerms(true), children: "Continue" } ) ] }); } // src/components/SumSubWidget.tsx import SumsubWebSdk from "@sumsub/websdk-react"; import { motion as motion2 } from "framer-motion"; // src/hooks/use-sumsub.ts import { useState as useState3, useEffect, useCallback } from "react"; // src/utils/getUserIpAddress.ts var getUserIpAddress = async () => { try { const response = await fetch("https://api.ipify.org?format=json"); const data = await response.json(); return data.ip; } catch (err) { console.error("[error at getUserIpAddress]", err); return ""; } }; // src/hooks/use-sumsub.ts var useSumSub = ({ eoaAddress, onApplicantIdChange, onVerificationComplete }) => { const [accessToken, setAccessToken] = useState3(null); const [isLoading, setIsLoading] = useState3(true); const [error, setError] = useState3(null); const [tokenRefreshCount, setTokenRefreshCount] = useState3(0); const [applicantId, setApplicantId] = useState3(null); const getAccessToken = useCallback(async () => { console.log("Attempting to get SumSub access token..."); setIsLoading(true); setError(null); try { if (!eoaAddress) throw new Error("EOA address not provided"); const ipAddress = await getUserIpAddress(); const tokenData = await getSumSubAccessToken(eoaAddress, ipAddress); if (!tokenData?.token) { throw new Error("No token received from backend"); } setAccessToken(tokenData.token); setTokenRefreshCount(0); console.log("SumSub access token obtained successfully."); } catch (err) { console.error("Error getting access token:", err); setError( err instanceof Error ? err.message : "Failed to get access token. Please try again." ); } finally { setIsLoading(false); } }, [eoaAddress]); useEffect(() => { if (eoaAddress) { getAccessToken(); } }, [getAccessToken, eoaAddress]); const handleMessage = useCallback( (message) => { console.log("Sumsub Message Received:", message.type, message.payload); if (message.payload?.applicantId && typeof message.payload.applicantId === "string") { const newApplicantId = message.payload.applicantId; if (newApplicantId !== applicantId) { setApplicantId(newApplicantId); onApplicantIdChange?.(newApplicantId); console.log("Applicant ID updated:", newApplicantId); } } if (message.type === "idCheck.applicantReviewed" || message.payload?.reviewStatus === "completed") { console.log("Verification process completed."); onVerificationComplete?.(); } }, [applicantId, onApplicantIdChange, onVerificationComplete] ); const handleError = useCallback((sdkError) => { console.error("Sumsub WebSDK error:", sdkError); setError(`Sumsub SDK Error: ${sdkError.message}. Please try again.`); }, []); const handleTokenExpiration = useCallback(async () => { console.warn("Sumsub token expired, attempting refresh..."); if (tokenRefreshCount >= 2) { console.error("Max token refresh attempts reached."); setError("Failed to refresh session. Please try again later."); setAccessToken(null); return; } setTokenRefreshCount((prev) => prev + 1); setTimeout(async () => { await getAccessToken(); }, 2e3); }, [tokenRefreshCount, getAccessToken]); const retry = useCallback(() => { setTokenRefreshCount(0); getAccessToken(); }, [getAccessToken]); return { accessToken, isLoading, error, applicantId, handleMessage, handleError, handleTokenExpiration, retry }; }; // src/components/SumSubWidget.tsx import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime"; var LoadingBar = ({ className = "" }) => { return /* @__PURE__ */ jsx7( "div", { className: `h-4 overflow-hidden bg-white relative w-full ${className}`, children: /* @__PURE__ */ jsx7( motion2.div, { className: "bg-red-500 h-full absolute top-0 left-0", initial: { width: "0%" }, animate: { width: "100%" }, transition: { duration: 2, ease: "linear", repeat: Infinity, repeatType: "loop" } } ) } ); }; var RetroButton = ({ children, variant = "default", className = "", ...props }) => { const variantClasses = { default: "bg-[#E6BE8A] text-[#3B2F2F] border-[#8B4513] hover:bg-[#E6BE8A]", primary: "bg-[#8B4513] text-[#FFD700] border-[#D4AF37] hover:bg-[#8B4513]" }; return /* @__PURE__ */ jsx7( "button", { className: ` font-['Times_New_Roman',_Times,_serif] px-4 py-1.5 text-base ${variantClasses[variant]} ${className} border-2 active:translate-y-[1px] flex items-center justify-center gap-2 transition-none disabled:opacity-50 disabled:cursor-not-allowed `, ...props, children: /* @__PURE__ */ jsx7("span", { children }) } ); }; function SumSubWidget({ eoaAddress, onApplicantIdChange, onVerificationComplete }) { const { accessToken, isLoading, error, handleMessage, handleError, handleTokenExpiration, retry } = useSumSub({ eoaAddress, onApplicantIdChange, onVerificationComplete }); const { _setUserStatus } = useCardStatusStore(); const { setIsSumsubProfileVerified, isSumsubProfileVerified } = useKycStore(); if (isLoading) { return /* @__PURE__ */ jsx7(SDKWrapper, { children: /* @__PURE__ */ jsxs6("div", { className: "text-center p-6 min-h-[30vh] flex flex-col justify-center", children: [ /* @__PURE__ */ jsx7("p", { className: "font-['MS_Sans_Serif',_Arial,_sans-serif] mb-4 text-white", children: "Loading SumSub verification module..." }), /* @__PURE__ */ jsx7(LoadingBar, { className: "w-[50rem] mx-auto" }) ] }) }); } if (error || !accessToken) { return /* @__PURE__ */ jsx7(SDKWrapper, { children: /* @__PURE__ */ jsxs6("div", { className: "text-center p-6 min-h-[10vh] flex flex-col justify-center", children: [ /* @__PURE__ */ jsx7("p", { className: "font-['MS_Sans_Serif',_Arial,_sans-serif] mb-4 text-red-600", children: error || "Failed to load verification module. Please try again." }), /* @__PURE__ */ jsx7(RetroButton, { variant: "primary", onClick: retry, children: "Try Again" }) ] }) }); } const sdkKey = `sumsub-sdk-${accessToken}`; return /* @__PURE__ */ jsx7(SDKWrapper, { children: /* @__PURE__ */ jsxs6("div", { className: "relative rounded-md w-full min-h-[30vh]", children: [ /* @__PURE__ */ jsx7("div", { className: "relative w-full", style: { minHeight: "400px" }, children: /* @__PURE__ */ jsx7( SumsubWebSdk, { accessToken, expirationHandler: handleTokenExpiration, config: { lang: "en", customizationName: "swype", theme: "dark" }, options: { addViewportTag: false, adaptIframeHeight: true }, onMessage: (message, payload) => { console.log("Sumsub onMessage:", message.type, payload); if (payload && payload.reviewStatus === "completed") { _setUserStatus("initiated"); setIsSumsubProfileVerified(true); } handleMessage(message); }, onError: handleError }, sdkKey ) }), /* @__PURE__ */ jsx7("div", { className: "w-full mt-4", children: /* @__PURE__ */ jsx7( "button", { disabled: !isSumsubProfileVerified, className: `flex h-[44px] w-full justify-center items-center gap-1 self-stretch rounded-lg bg-white backdrop-blur-[6px] text-black font-medium disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-gray-100`, onClick: () => _setUserStatus("pending"), children: "Continue" } ) }) ] }) }); } // src/widgets/kyc/KycModal.tsx import { Fragment as Fragment2, jsx as jsx8 } from "react/jsx-runtime"; function KycModal({ eoaAddress, onClose, onComplete }) { const { status } = useCardStatusStore(); const { acceptedTerms, acceptedRainTerms } = useKycStore(); useEffect2(() => { if (status === "approved" && onComplete) { onComplete(); } }, [status, onComplete]); const getModalView = useMemo(() => { switch (status) { case "userNotExisting": case "initiated": { if (acceptedTerms) { return { title: "Complete KYC", component: /* @__PURE__ */ jsx8(SumSubWidget, { eoaAddress }) }; } else { return { title: "Verifying with Sumsub", component: /* @__PURE__ */ jsx8(SumSubConsentView, {}) }; } } case "review": { return { title: "KYC Verification is in Process", component: /* @__PURE__ */ jsx8(KycPendingViaRain, {}) }; } case "pending": { if (acceptedRainTerms) { return { title: "Accept Terms to Proceed", component: /* @__PURE__ */ jsx8(KycPendingViaRain, {}) }; } else { return { title: "KYC Verification is in Process", component: /* @__PURE__ */ jsx8(PreRainTerms, {}) }; } } case "approved": { return { title: "", component: /* @__PURE__ */ jsx8(Fragment2, {}) }; } default: return { title: "Apply for Card", component: /* @__PURE__ */ jsx8(Fragment2, {}) }; } }, [status, acceptedTerms, acceptedRainTerms]); const handleClose = onClose || (() => { }); const { title, component } = getModalView; if (status === "approved" || !component) { return null; } return /* @__PURE__ */ jsx8(Modal, { title, onOutsideClick: handleClose, onClose: handleClose, children: component }); } // src/core/cardActivation.ts async function delegateCard(params) { const { eoaAddress } = useSDKConfigStore.getState(); if (!eoaAddress) { throw new Error("EOA address not found. Please initialize the SDK first."); } if (params.creditLimit <= 0) { throw new Error("Credit limit must be greater than 0."); } try { const delegationResponse = await getCreditDelegationTxData({ from: eoaAddress, creditLimit: params.creditLimit, creditType: params.creditType }); console.log({ delegationResponse }); const delegation = delegationResponse.data; console.log({ delegation }); if (!delegation.to || !delegation.callData) { throw new Error( "Invalid transaction data received from delegation service" ); } const transactionHash = await params.sendTransactionAsync({ to: delegation.to, value: BigInt(delegation.value || 0), data: delegation.callData }); console.log({ transactionHash }); const createCardResponse = await createCard(eoaAddress); console.log({ createCardResponse }); const createdCard = createCardResponse.data; if (!createdCard.id) { throw new Error("Failed to create card - invalid card ID received"); } const signature = await params.signTypedDataAsync({ domain: { chainId: 8453 }, types: { DelegationParams: [ { name: "cardID", type: "string" }, { name: "creditProvider", type: "address" }, { name: "creditType", type: "string" } ] }, primaryType: "DelegationParams", message: { cardID: createdCard.id, creditProvider: eoaAddress, creditType: params.creditType } }); console.log({ signature }); await linkCardToCreditAccount({ cardID: createdCard.id, creditProviderAddress: eoaAddress, chainID: 8453, // Base chain ID signature, creditType: params.creditType }); const userCardsResponse = await getUserCards(eoaAddress); console.log({ userCardsResponse }); const updatedCard = userCardsResponse.data?.find( (card) => card.cardID === createdCard.id ); if (!updatedCard) { throw new Error( "Card not found after delegation - delegation may have failed" ); } if (updatedCard.holdingAddress.toLowerCase() !== eoaAddress.toLowerCase()) { throw new Error( "Delegation failed - holding address does not match EOA address" ); } await dataFetcher.fetchDataAfterAuth(); } catch (err) { const errorMessage = err instanceof Error ? err.message : "Failed to delegate card"; throw new Error(errorMessage); } } // src/api/sdk.ts async function initializeSwypeSDK(config) { const { setConfig } = useSDKConfigStore.getState(); setConfig({ eoaAddress: config.eoaAddress, apiKey: config.apiKey, baseUrl: config.baseUrl, apiPath: config.apiPath }); try { await initializeAuth(config.eoaAddress, { signature: config.signature, deadline: config.deadline }); console.log("SwypeSDK initialized successfully"); await dataFetcher.fetchDataAfterAuth(); } catch (error) { console.error("Failed to initialize SwypeSDK:", error); throw error; } } // src/hooks/useCardData.ts var useCardData = () => { const { status, userCard, isUserCardDelegated, isLoading, error } = useCardStatusStore(); return { status, userCard, isUserCardDelegated, isLoading, error }; }; // src/hooks/useCreditData.ts var useCreditData = () => { const { creditInfo, isLoading, error } = useCreditStore(); return { creditInfo, isLoading, error }; }; export { KycModal, Modal, SumSubWidget, dataFetcher, delegateCard, freezeCard, generateAuthSign, getCardStatus, getCreditDelegationTxData, getCreditInformation, getSumSubAccessToken, getUserCards, initializeSwypeSDK, linkCardToCreditAccount, useCardData, useCardStatusStore, useCreditData, useCreditStore, useKycStore, useSDKConfigStore, useSumSub }; //# sourceMappingURL=index.mjs.map