@p2pdotme/sdk 1.2.6 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -273,8 +273,9 @@ function cleanupSeonStorage() {
273
273
  // src/fraud-engine/signing.ts
274
274
  async function getSignedHeaders(signer, action) {
275
275
  const signingAddress = (signer.signerAddress ?? signer.address).toLowerCase();
276
+ const subjectAddress = signer.address.toLowerCase();
276
277
  const timestamp = Math.floor(Date.now() / 1e3).toString();
277
- const message = `${action}:${signingAddress}:${timestamp}`;
278
+ const message = `${action}:${signingAddress}:${subjectAddress}:${timestamp}`;
278
279
  try {
279
280
  const signature = await signer.signMessage(message);
280
281
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fraud-engine/index.ts","../src/fraud-engine/client.ts","../src/lib/encoding.ts","../src/lib/logger.ts","../src/lib/subgraph.ts","../src/validation/errors.validation.ts","../src/validation/schemas.validation.ts","../src/country/currency.ts","../src/fraud-engine/device.ts","../src/fraud-engine/errors.ts","../src/fraud-engine/encryption.ts","../src/fraud-engine/fingerprint.ts","../src/fraud-engine/seon.ts","../src/fraud-engine/signing.ts","../src/fraud-engine/validation.ts"],"sourcesContent":["// ── Main entry point ────────────────────────────────────────────────────\n\nexport { createFraudEngine } from \"./client\";\n\n// ── Types ───────────────────────────────────────────────────────────────\n\nexport type {\n\tActivityType,\n\tBuyOrderDetails,\n\tDeviceDetails,\n\tFingerprintLogResult,\n\tFraudCheckApiResponse,\n\tFraudCheckResult,\n\tFraudEngine,\n\tFraudEngineConfig,\n\tFraudEngineSigner,\n\tLinkOrderResult,\n\tProcessBuyOrderResult,\n\tUserDetails,\n} from \"./types\";\n\n// ── Logger ──────────────────────────────────────────────────────────────\n\nexport { type Logger, noopLogger } from \"../lib\";\n\n// ── Errors ──────────────────────────────────────────────────────────────\n\nexport { FraudEngineError, type FraudEngineErrorCode } from \"./errors\";\n\n// ── Low-level exports (for advanced use) ────────────────────────────────\n\nexport { fetchIpAddress, getBasicDeviceDetails, getDeviceDetails } from \"./device\";\nexport { encryptPayload } from \"./encryption\";\nexport { getFingerprint, loadFingerprintAgent } from \"./fingerprint\";\nexport { cleanupSeonStorage, getSeonSession, initSeon } from \"./seon\";\nexport { getSignedHeaders } from \"./signing\";\n","import { errAsync, ResultAsync } from \"neverthrow\";\nimport { type Logger, noopLogger } from \"../lib\";\nimport { getDeviceDetails } from \"./device\";\nimport { encryptPayload } from \"./encryption\";\nimport { FraudEngineError } from \"./errors\";\nimport { getFingerprint as getFingerprintResult, loadFingerprintAgent } from \"./fingerprint\";\nimport { cleanupSeonStorage as cleanupSeon, getSeonSession, initSeon } from \"./seon\";\nimport { getSignedHeaders } from \"./signing\";\nimport type {\n\tBuyOrderDetails,\n\tFingerprintLogResult,\n\tFraudCheckApiResponse,\n\tFraudCheckResult,\n\tFraudEngine,\n\tFraudEngineConfig,\n\tFraudEngineSigner,\n\tLinkOrderResult,\n\tProcessBuyOrderResult,\n\tUserDetails,\n} from \"./types\";\nimport { validate, ZodFraudEngineConfigSchema } from \"./validation\";\n\nexport function createFraudEngine(config: FraudEngineConfig): FraudEngine {\n\tconst { apiUrl, encryptionKey } = config;\n\tconst seonRegion = config.seonRegion ?? \"asia\";\n\tconst logger: Logger = config.logger ?? noopLogger;\n\n\t// Validate eagerly so misconfiguration surfaces at construction time, but\n\t// never throw — the SDK's contract is that all failures flow through\n\t// `Result`/`ResultAsync`. Capture the error and short-circuit any method\n\t// that depends on the validated fields (`apiUrl`, `encryptionKey`).\n\t// Methods that don't depend on them (init, getFingerprint, getDeviceDetails,\n\t// cleanupSeonStorage) remain usable so consumers can still hydrate device\n\t// fingerprints even with a partially-configured engine.\n\tconst configResult = validate(ZodFraudEngineConfigSchema, {\n\t\tapiUrl,\n\t\tencryptionKey,\n\t\tseonRegion,\n\t});\n\tconst configError = configResult.isErr() ? configResult.error : null;\n\tif (configError) {\n\t\tlogger.error(\n\t\t\t\"Fraud engine config invalid; API methods will return VALIDATION_ERROR until fixed\",\n\t\t\t{ error: configError.message },\n\t\t);\n\t}\n\n\tfunction linkOrderInternal(\n\t\tsigner: FraudEngineSigner,\n\t\tactivityLogId: number,\n\t\torderId: string,\n\t): ResultAsync<LinkOrderResult, FraudEngineError> {\n\t\treturn ResultAsync.fromPromise(\n\t\t\t(async () => {\n\t\t\t\tlogger.info(\"Linking order to activity log\", { activityLogId, orderId });\n\n\t\t\t\tconst signedHeaders = await getSignedHeaders(signer, \"link-order\");\n\n\t\t\t\tconst response = await fetch(`${apiUrl}/activity-logs/link-order`, {\n\t\t\t\t\tmethod: \"PATCH\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tactivity_log_id: activityLogId,\n\t\t\t\t\t\torder_id: orderId,\n\t\t\t\t\t\tuser_address: signer.address.toLowerCase(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new FraudEngineError(`Link order API returned ${response.status}`, {\n\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst data = (await response.json()) as LinkOrderResult;\n\t\t\t\tlogger.info(\"Order linked successfully\", { orderId });\n\t\t\t\treturn data;\n\t\t\t})(),\n\t\t\t(cause) => {\n\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\treturn new FraudEngineError(\"Link order failed\", {\n\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t}\n\n\tasync function checkBuyOrderInternal(params: {\n\t\tsigner: FraudEngineSigner;\n\t\torderDetails: BuyOrderDetails;\n\t\tuserDetails?: UserDetails;\n\t\torderSource?: string;\n\t}): Promise<FraudCheckApiResponse> {\n\t\tlogger.info(\"Checking buy order for fraud\", {\n\t\t\tcurrency: params.orderDetails.currency,\n\t\t\tfiatAmount: params.orderDetails.fiatAmount,\n\t\t});\n\n\t\tconst [seonSession, deviceDetails, signedHeaders] = await Promise.all([\n\t\t\tgetSeonSession(seonRegion),\n\t\t\tgetDeviceDetails(),\n\t\t\tgetSignedHeaders(params.signer, \"activity-log\"),\n\t\t]);\n\n\t\tconst device = { ...deviceDetails, seonSession };\n\n\t\tconst userAddress = params.signer.address.toLowerCase();\n\t\tconst timestamp = Date.now();\n\n\t\tconst payload = JSON.stringify({\n\t\t\tuser_details: {\n\t\t\t\tcurrency: params.userDetails?.currency,\n\t\t\t\tcountry: params.userDetails?.country,\n\t\t\t\tlanguage: params.userDetails?.language,\n\t\t\t\tlogin_method: params.userDetails?.loginMethod,\n\t\t\t\tlogin_email: params.userDetails?.loginEmail,\n\t\t\t\tlogin_phone: params.userDetails?.loginPhone,\n\t\t\t},\n\t\t\ttransaction_details: {\n\t\t\t\tcrypto_amount: params.orderDetails.cryptoAmount,\n\t\t\t\tfiat_amount: params.orderDetails.fiatAmount,\n\t\t\t\tcurrency: params.orderDetails.currency,\n\t\t\t\trecipient_address: params.orderDetails.recipientAddress,\n\t\t\t\tfee: params.orderDetails.fee,\n\t\t\t\tamount_after_fee: params.orderDetails.amountAfterFee,\n\t\t\t\tpayment_method: params.orderDetails.paymentMethod,\n\t\t\t\testimated_processing_time: params.orderDetails.estimatedProcessingTime,\n\t\t\t\torder_timestamp: timestamp,\n\t\t\t\torder_source: params.orderSource,\n\t\t\t},\n\t\t\tdevice_details: device,\n\t\t});\n\n\t\t// AAD format must match backend: \"type|user_address|timestamp\"\n\t\tconst aad = `buy_order|${userAddress}|${timestamp}`;\n\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\tconst response = await fetch(`${apiUrl}/activity-logs`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...signedHeaders,\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttype: \"buy_order\",\n\t\t\t\tuser_address: userAddress,\n\t\t\t\ttimestamp,\n\t\t\t\tencrypted_payload: encrypted,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new FraudEngineError(`Fraud check API returned ${response.status}`, {\n\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\tcontext: { status: response.status },\n\t\t\t});\n\t\t}\n\n\t\tconst data = (await response.json()) as FraudCheckApiResponse;\n\t\tlogger.info(\"Fraud check result\", {\n\t\t\tapproved: data.approved,\n\t\t\tactivityLogId: data.activity_log_id,\n\t\t});\n\t\treturn data;\n\t}\n\n\treturn {\n\t\tasync init() {\n\t\t\tlogger.info(\"Initializing fraud engine\");\n\t\t\ttry {\n\t\t\t\tinitSeon();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"SEON initialization failed\", { cause: String(cause) });\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait loadFingerprintAgent();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"FingerprintJS initialization failed\", {\n\t\t\t\t\tcause: String(cause),\n\t\t\t\t});\n\t\t\t}\n\t\t\tlogger.info(\"Fraud engine initialized\");\n\t\t},\n\n\t\tcheckBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t}): ResultAsync<FraudCheckResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\tcheckBuyOrderInternal(params).then((data) => ({\n\t\t\t\t\tapproved: data.approved,\n\t\t\t\t\tactivityLogId: data.activity_log_id,\n\t\t\t\t\tmessage: data.message,\n\t\t\t\t\tlinkOrder: (orderId: string) =>\n\t\t\t\t\t\tlinkOrderInternal(params.signer, data.activity_log_id, orderId),\n\t\t\t\t})),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fraud check failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tprocessBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t\tplaceOrder: () => Promise<string>;\n\t\t}): ResultAsync<ProcessBuyOrderResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlet activityLogId: number | null = null;\n\n\t\t\t\t\t// Step 1: Fraud check (fail-open)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fraudCheck = await checkBuyOrderInternal(params);\n\t\t\t\t\t\tif (!fraudCheck.approved) {\n\t\t\t\t\t\t\treturn { status: \"rejected\" as const, message: fraudCheck.message };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tactivityLogId = fraudCheck.activity_log_id;\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tlogger.error(\"Fraud check failed, proceeding with order (fail-open)\", {\n\t\t\t\t\t\t\terror: String(cause),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 2: Place order (consumer callback)\n\t\t\t\t\tlet orderId: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\torderId = await params.placeOrder();\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tthrow new FraudEngineError(\"Place order callback failed\", {\n\t\t\t\t\t\t\tcode: \"PLACE_ORDER_ERROR\",\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 3: Auto-link (fire-and-forget, only if fraud check succeeded)\n\t\t\t\t\tif (activityLogId !== null) {\n\t\t\t\t\t\tlinkOrderInternal(params.signer, activityLogId, orderId).mapErr((e) => {\n\t\t\t\t\t\t\tlogger.error(\"Failed to link order to activity log\", {\n\t\t\t\t\t\t\t\torderId,\n\t\t\t\t\t\t\t\tactivityLogId: activityLogId as number,\n\t\t\t\t\t\t\t\terror: e.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { status: \"placed\" as const, orderId };\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Process buy order failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tlogFingerprint(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t}): ResultAsync<FingerprintLogResult | null, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlogger.info(\"Logging fingerprint\");\n\n\t\t\t\t\tconst fingerprintResult = await getFingerprintResult(5000);\n\t\t\t\t\tif (!fingerprintResult) {\n\t\t\t\t\t\tlogger.warn(\"Fingerprint not available, skipping\");\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst signedHeaders = await getSignedHeaders(params.signer, \"fingerprint-log\");\n\n\t\t\t\t\tconst normalizedAddress = params.signer.address.toLowerCase();\n\t\t\t\t\tconst timestamp = Date.now();\n\n\t\t\t\t\tconst payload = JSON.stringify({\n\t\t\t\t\t\tfingerprint_id: fingerprintResult.visitorId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// AAD format: \"fingerprint|user_address|timestamp\"\n\t\t\t\t\tconst aad = `fingerprint|${normalizedAddress}|${timestamp}`;\n\t\t\t\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\t\t\t\tconst response = await fetch(`${apiUrl}/fingerprint-log`, {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\tuser_address: normalizedAddress,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\tencrypted_payload: encrypted,\n\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new FraudEngineError(`Fingerprint log API returned ${response.status}`, {\n\t\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as FingerprintLogResult;\n\t\t\t\t\tlogger.info(\"Fingerprint logged successfully\");\n\t\t\t\t\treturn data;\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fingerprint log failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync getFingerprint() {\n\t\t\treturn getFingerprintResult(5000);\n\t\t},\n\n\t\tasync getDeviceDetails() {\n\t\t\treturn getDeviceDetails();\n\t\t},\n\n\t\tcleanupSeonStorage() {\n\t\t\tcleanupSeon();\n\t\t},\n\t};\n}\n","/** Converts a hex string to a Uint8Array. */\nexport function hexToBytes(hex: string): Uint8Array {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tbytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);\n\t}\n\treturn bytes;\n}\n\n/** Converts a Uint8Array to a base64 string. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary);\n}\n","export interface Logger {\n\tdebug(message: string, data?: Record<string, unknown>): void;\n\tinfo(message: string, data?: Record<string, unknown>): void;\n\twarn(message: string, data?: Record<string, unknown>): void;\n\terror(message: string, data?: Record<string, unknown>): void;\n}\n\nconst noop = () => {};\n\nexport const noopLogger: Logger = {\n\tdebug: noop,\n\tinfo: noop,\n\twarn: noop,\n\terror: noop,\n};\n","import { ResultAsync } from \"neverthrow\";\nimport { SdkError } from \"../validation\";\nimport { sleep } from \"./sleep\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RETRIES = 3;\nconst BACKOFF_MS = 500;\n\nexport type SubgraphErrorCode = \"HTTP_ERROR\" | \"GRAPHQL_ERROR\" | \"NO_DATA\" | \"TRANSPORT_ERROR\";\n\n/** Transport-level error from the shared subgraph client. Consumers remap to their own domain error. */\nexport class SubgraphError extends SdkError<SubgraphErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: SubgraphErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SubgraphError\";\n\t}\n}\n\nexport interface SubgraphQueryParams {\n\treadonly query: string;\n\treadonly variables?: Record<string, unknown>;\n\treadonly timeoutMs?: number;\n}\n\nfunction isTransient(error: unknown): boolean {\n\tif (error instanceof SubgraphError) {\n\t\tif (error.code !== \"HTTP_ERROR\") return false;\n\t\tconst status = (error.context as { status?: number } | undefined)?.status;\n\t\treturn typeof status === \"number\" && status >= 500;\n\t}\n\tif (error instanceof DOMException && error.name === \"AbortError\") return true;\n\tif (error instanceof TypeError) return true;\n\treturn false;\n}\n\n/**\n * POSTs a GraphQL query to `url` with retry + timeout. Returns the `data` field on\n * success. All transport and GraphQL-level failures surface as `SubgraphError`.\n */\nexport function querySubgraph(\n\turl: string,\n\tparams: SubgraphQueryParams,\n): ResultAsync<unknown, SubgraphError> {\n\tconst timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n\tconst fetchOnce = async () => {\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tquery: params.query,\n\t\t\t\t\tvariables: params.variables,\n\t\t\t\t}),\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new SubgraphError(`Subgraph request failed (status: ${response.status})`, {\n\t\t\t\t\tcode: \"HTTP_ERROR\",\n\t\t\t\t\tcause: response,\n\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst json = await response.json();\n\n\t\t\tif (json.errors?.length > 0) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned GraphQL errors\", {\n\t\t\t\t\tcode: \"GRAPHQL_ERROR\",\n\t\t\t\t\tcause: json.errors,\n\t\t\t\t\tcontext: { errors: json.errors },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!json.data) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned no data\", {\n\t\t\t\t\tcode: \"NO_DATA\",\n\t\t\t\t\tcause: \"Missing data field in GraphQL response\",\n\t\t\t\t\tcontext: { response: json },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn json.data;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t};\n\n\tconst fetchWithRetry = async () => {\n\t\tfor (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await fetchOnce();\n\t\t\t} catch (error) {\n\t\t\t\tconst lastAttempt = attempt === MAX_RETRIES;\n\t\t\t\tif (lastAttempt || !isTransient(error)) throw error;\n\t\t\t\tawait sleep(BACKOFF_MS * (attempt + 1));\n\t\t\t}\n\t\t}\n\t\tthrow new SubgraphError(\"Subgraph query exhausted retries\", { code: \"TRANSPORT_ERROR\" });\n\t};\n\n\treturn ResultAsync.fromPromise(fetchWithRetry(), (error) =>\n\t\terror instanceof SubgraphError\n\t\t\t? error\n\t\t\t: new SubgraphError(\"Subgraph query failed\", {\n\t\t\t\t\tcode: \"TRANSPORT_ERROR\",\n\t\t\t\t\tcause: error,\n\t\t\t\t}),\n\t);\n}\n","export class SdkError<TCode extends string = string> extends Error {\n\treadonly code: TCode;\n\treadonly cause?: unknown;\n\treadonly context?: Record<string, unknown>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: TCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SdkError\";\n\t\tthis.code = options.code;\n\t\tthis.cause = options.cause;\n\t\tthis.context = options.context;\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { isAddress } from \"viem\";\nimport { z } from \"zod\";\nimport { CURRENCY_CODES } from \"../country/currency\";\n\nexport const ZodAddressSchema = z\n\t.string()\n\t.refine((s) => isAddress(s), { message: \"Invalid Ethereum address\" });\n\nexport const ZodCurrencySchema = z.enum(CURRENCY_CODES);\n\nexport function validate<S extends z.ZodType, E>(\n\tschema: S,\n\tdata: unknown,\n\ttoError: (message: string, cause: unknown, data: unknown) => E,\n): Result<z.infer<S>, E> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(toError(z.prettifyError(result.error), result.error, data));\n}\n","/**\n * All supported currency symbols. Single source of truth for the SDK.\n *\n * Lives alongside the country metadata so that adding a currency is a\n * single-folder operation: drop a new file in `currencies/<code>.ts`, add it\n * to this map, and both `COUNTRY_OPTIONS` and `ZodCurrencySchema` pick it up.\n */\nexport const CURRENCY = {\n\tIDR: \"IDR\",\n\tINR: \"INR\",\n\tBRL: \"BRL\",\n\tARS: \"ARS\",\n\tMEX: \"MEX\",\n\tVEN: \"VEN\",\n\tEUR: \"EUR\",\n\tNGN: \"NGN\",\n\tUSD: \"USD\",\n\tCOP: \"COP\",\n\tECU: \"ECU\",\n\tPEN: \"PEN\",\n} as const;\n\n/** Union of supported currency codes. */\nexport type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];\n\n/**\n * Tuple form of the currency codes — used by `z.enum(...)` in the shared\n * validation layer. Narrow tuple type required by Zod.\n */\nexport const CURRENCY_CODES = Object.values(CURRENCY) as [CurrencyCode, ...CurrencyCode[]];\n","import type { DeviceDetails } from \"./types\";\n\ninterface NavigatorConnection {\n\treadonly effectiveType?: string;\n}\n\ninterface NavigatorExtended extends Navigator {\n\treadonly connection?: NavigatorConnection;\n\treadonly deviceMemory?: number;\n}\n\nexport function getBasicDeviceDetails(): Omit<DeviceDetails, \"ip\" | \"seonSession\"> {\n\tconst nav = typeof navigator !== \"undefined\" ? (navigator as NavigatorExtended) : undefined;\n\tconst win = typeof window !== \"undefined\" ? window : undefined;\n\n\treturn {\n\t\tuserAgent: nav?.userAgent ?? \"\",\n\t\tplatform: nav?.platform ?? \"\",\n\t\tlanguage: nav?.language ?? \"\",\n\t\tlanguages: nav ? Array.from(nav.languages) : [],\n\t\tscreenWidth: win?.screen?.width ?? 0,\n\t\tscreenHeight: win?.screen?.height ?? 0,\n\t\tdevicePixelRatio: win?.devicePixelRatio ?? 1,\n\t\ttimezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone ?? \"\",\n\t\ttimezoneOffset: new Date().getTimezoneOffset(),\n\t\tcookiesEnabled: nav?.cookieEnabled ?? false,\n\t\tdoNotTrack: nav?.doNotTrack ?? null,\n\t\tonline: nav?.onLine ?? true,\n\t\tconnectionType: nav?.connection?.effectiveType,\n\t\tdeviceMemory: nav?.deviceMemory,\n\t\thardwareConcurrency: nav?.hardwareConcurrency,\n\t\ttouchSupport: nav ? \"ontouchstart\" in window : false,\n\t\tmaxTouchPoints: nav?.maxTouchPoints ?? 0,\n\t\tvendor: nav?.vendor ?? \"\",\n\t\tappVersion: nav?.appVersion ?? \"\",\n\t\tcolorDepth: win?.screen?.colorDepth ?? 0,\n\t\tpixelDepth: win?.screen?.pixelDepth ?? 0,\n\t};\n}\n\nexport async function fetchIpAddress(): Promise<string | undefined> {\n\ttry {\n\t\tconst response = await fetch(\"https://api.ipify.org?format=json\");\n\t\tconst data = (await response.json()) as { ip: string };\n\t\treturn data.ip;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport async function getDeviceDetails(seonSession?: string): Promise<DeviceDetails> {\n\tconst basic = getBasicDeviceDetails();\n\tconst ip = await fetchIpAddress();\n\treturn { ...basic, ip, seonSession };\n}\n","import { SdkError } from \"../validation\";\n\nexport type FraudEngineErrorCode =\n\t| \"API_ERROR\"\n\t| \"ENCRYPTION_ERROR\"\n\t| \"SIGNING_ERROR\"\n\t| \"VALIDATION_ERROR\"\n\t| \"NETWORK_ERROR\"\n\t| \"PLACE_ORDER_ERROR\";\n\nexport class FraudEngineError extends SdkError<FraudEngineErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: FraudEngineErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"FraudEngineError\";\n\t}\n}\n","import { bytesToBase64, hexToBytes } from \"../lib/encoding\";\nimport { FraudEngineError } from \"./errors\";\n\nexport async function getEncryptionKey(encryptionKeyHex: string): Promise<CryptoKey> {\n\tconst keyBytes = hexToBytes(encryptionKeyHex);\n\treturn crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tkeyBytes.buffer as ArrayBuffer,\n\t\t{ name: \"AES-GCM\" },\n\t\tfalse,\n\t\t[\"encrypt\"],\n\t);\n}\n\nexport async function encryptPayload(\n\tpayload: string,\n\taad: string,\n\tencryptionKeyHex: string,\n): Promise<string> {\n\ttry {\n\t\tconst key = await getEncryptionKey(encryptionKeyHex);\n\t\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\t\tconst encoded = new TextEncoder().encode(payload);\n\t\tconst aadEncoded = new TextEncoder().encode(aad);\n\n\t\tconst ciphertext = await crypto.subtle.encrypt(\n\t\t\t{ name: \"AES-GCM\", iv, additionalData: aadEncoded, tagLength: 128 },\n\t\t\tkey,\n\t\t\tencoded,\n\t\t);\n\n\t\t// IV (12 bytes) + ciphertext + auth tag (16 bytes appended by Web Crypto)\n\t\tconst result = new Uint8Array(iv.length + ciphertext.byteLength);\n\t\tresult.set(iv, 0);\n\t\tresult.set(new Uint8Array(ciphertext), iv.length);\n\n\t\treturn bytesToBase64(result);\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Encryption failed\", {\n\t\t\tcode: \"ENCRYPTION_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import FingerprintJS from \"@fingerprintjs/fingerprintjs\";\n\nlet agent: Awaited<ReturnType<typeof FingerprintJS.load>> | null = null;\nlet cachedResult: { visitorId: string; confidence: number } | null = null;\n\nexport async function loadFingerprintAgent(): Promise<void> {\n\tif (agent) return;\n\tagent = await FingerprintJS.load();\n}\n\nexport async function getFingerprint(\n\ttimeoutMs = 5000,\n): Promise<{ visitorId: string; confidence: number } | null> {\n\tif (cachedResult) return cachedResult;\n\tif (!agent) {\n\t\ttry {\n\t\t\tawait loadFingerprintAgent();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst loadedAgent = agent;\n\tif (!loadedAgent) return null;\n\ttry {\n\t\tconst result = await Promise.race([\n\t\t\tloadedAgent.get(),\n\t\t\tnew Promise<never>((_, reject) =>\n\t\t\t\tsetTimeout(() => reject(new Error(\"FingerprintJS timed out\")), timeoutMs),\n\t\t\t),\n\t\t]);\n\t\tcachedResult = {\n\t\t\tvisitorId: result.visitorId,\n\t\t\tconfidence: result.confidence.score,\n\t\t};\n\t\treturn cachedResult;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import seon from \"@seontechnologies/seon-javascript-sdk\";\n\nlet initialized = false;\n\nexport function initSeon(): void {\n\tif (initialized) return;\n\tseon.init();\n\tinitialized = true;\n}\n\nexport async function getSeonSession(region: string): Promise<string | undefined> {\n\ttry {\n\t\treturn await seon.getSession({\n\t\t\tgeolocation: { canPrompt: false },\n\t\t\tnetworkTimeoutMs: 2000,\n\t\t\tfieldTimeoutMs: 2000,\n\t\t\tregion,\n\t\t\tsilentMode: true,\n\t\t});\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function cleanupSeonStorage(): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst keysToRemove: string[] = [];\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst key = localStorage.key(i);\n\t\tif (key?.startsWith(\"seon_session_sent_\")) keysToRemove.push(key);\n\t}\n\tfor (const key of keysToRemove) localStorage.removeItem(key);\n}\n","import { FraudEngineError } from \"./errors\";\nimport type { FraudEngineSigner } from \"./types\";\n\nexport async function getSignedHeaders(\n\tsigner: FraudEngineSigner,\n\taction: \"activity-log\" | \"link-order\" | \"fingerprint-log\",\n): Promise<Record<string, string>> {\n\t// The EIP-191 signed message is bound to the address of the key that\n\t// actually produces the signature (the admin EOA for AA smart wallets),\n\t// not to the tracked subject address. For plain EOA signers where no\n\t// separate `signerAddress` is provided, this falls back to `signer.address`\n\t// and behaviour is identical to the single-address case.\n\tconst signingAddress = (signer.signerAddress ?? signer.address).toLowerCase();\n\tconst timestamp = Math.floor(Date.now() / 1000).toString();\n\tconst message = `${action}:${signingAddress}:${timestamp}`;\n\n\ttry {\n\t\tconst signature = await signer.signMessage(message);\n\t\treturn {\n\t\t\t\"X-Signer-Address\": signingAddress,\n\t\t\t\"X-Timestamp\": timestamp,\n\t\t\t\"X-Signature\": signature,\n\t\t};\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Failed to sign message\", {\n\t\t\tcode: \"SIGNING_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { z } from \"zod\";\nimport { FraudEngineError } from \"./errors\";\n\n// ── Config schema ──────────────────────────────────────────────────────\n\nexport const ZodFraudEngineConfigSchema = z.object({\n\tapiUrl: z.url(),\n\tencryptionKey: z.string().min(1),\n\tseonRegion: z.string().optional(),\n});\n\n// ── Buy order details schema ───────────────────────────────────────────\n\nexport const ZodBuyOrderDetailsSchema = z.object({\n\tcryptoAmount: z.number(),\n\tfiatAmount: z.number(),\n\tcurrency: z.string().min(1),\n\trecipientAddress: z.string().min(1),\n\tfee: z.number(),\n\tamountAfterFee: z.number(),\n\tpaymentMethod: z.string().optional(),\n\testimatedProcessingTime: z.string().optional(),\n});\n\n// ── User details schema ────────────────────────────────────────────────\n\nexport const ZodUserDetailsSchema = z.object({\n\tcurrency: z.string().optional(),\n\tcountry: z.string().optional(),\n\tlanguage: z.string().optional(),\n\tloginMethod: z.enum([\"email\", \"google\", \"phone\", \"passkey\", \"unknown\"]).optional(),\n\tloginEmail: z.string().optional(),\n\tloginPhone: z.string().optional(),\n});\n\n// ── Link order schema ──────────────────────────────────────────────────\n\nexport const ZodLinkOrderParamsSchema = z.object({\n\tactivityLogId: z.number().int().positive(),\n\torderId: z.string().min(1),\n});\n\n// ── Validate helper ────────────────────────────────────────────────────\n\nexport function validate<S extends z.ZodType>(\n\tschema: S,\n\tdata: unknown,\n): Result<z.infer<S>, FraudEngineError> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(\n\t\tnew FraudEngineError(z.prettifyError(result.error), {\n\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\tcause: result.error,\n\t\t\tcontext: { data },\n\t\t}),\n\t);\n}\n"],"mappings":"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","names":["import_neverthrow","import_neverthrow","FingerprintJS","seon","import_neverthrow","import_zod","validate","validate"]}
1
+ {"version":3,"sources":["../src/fraud-engine/index.ts","../src/fraud-engine/client.ts","../src/lib/encoding.ts","../src/lib/logger.ts","../src/lib/subgraph.ts","../src/validation/errors.validation.ts","../src/validation/schemas.validation.ts","../src/country/currency.ts","../src/fraud-engine/device.ts","../src/fraud-engine/errors.ts","../src/fraud-engine/encryption.ts","../src/fraud-engine/fingerprint.ts","../src/fraud-engine/seon.ts","../src/fraud-engine/signing.ts","../src/fraud-engine/validation.ts"],"sourcesContent":["// ── Main entry point ────────────────────────────────────────────────────\n\nexport { createFraudEngine } from \"./client\";\n\n// ── Types ───────────────────────────────────────────────────────────────\n\nexport type {\n\tActivityType,\n\tBuyOrderDetails,\n\tDeviceDetails,\n\tFingerprintLogResult,\n\tFraudCheckApiResponse,\n\tFraudCheckResult,\n\tFraudEngine,\n\tFraudEngineConfig,\n\tFraudEngineSigner,\n\tLinkOrderResult,\n\tProcessBuyOrderResult,\n\tUserDetails,\n} from \"./types\";\n\n// ── Logger ──────────────────────────────────────────────────────────────\n\nexport { type Logger, noopLogger } from \"../lib\";\n\n// ── Errors ──────────────────────────────────────────────────────────────\n\nexport { FraudEngineError, type FraudEngineErrorCode } from \"./errors\";\n\n// ── Low-level exports (for advanced use) ────────────────────────────────\n\nexport { fetchIpAddress, getBasicDeviceDetails, getDeviceDetails } from \"./device\";\nexport { encryptPayload } from \"./encryption\";\nexport { getFingerprint, loadFingerprintAgent } from \"./fingerprint\";\nexport { cleanupSeonStorage, getSeonSession, initSeon } from \"./seon\";\nexport { getSignedHeaders } from \"./signing\";\n","import { errAsync, ResultAsync } from \"neverthrow\";\nimport { type Logger, noopLogger } from \"../lib\";\nimport { getDeviceDetails } from \"./device\";\nimport { encryptPayload } from \"./encryption\";\nimport { FraudEngineError } from \"./errors\";\nimport { getFingerprint as getFingerprintResult, loadFingerprintAgent } from \"./fingerprint\";\nimport { cleanupSeonStorage as cleanupSeon, getSeonSession, initSeon } from \"./seon\";\nimport { getSignedHeaders } from \"./signing\";\nimport type {\n\tBuyOrderDetails,\n\tFingerprintLogResult,\n\tFraudCheckApiResponse,\n\tFraudCheckResult,\n\tFraudEngine,\n\tFraudEngineConfig,\n\tFraudEngineSigner,\n\tLinkOrderResult,\n\tProcessBuyOrderResult,\n\tUserDetails,\n} from \"./types\";\nimport { validate, ZodFraudEngineConfigSchema } from \"./validation\";\n\nexport function createFraudEngine(config: FraudEngineConfig): FraudEngine {\n\tconst { apiUrl, encryptionKey } = config;\n\tconst seonRegion = config.seonRegion ?? \"asia\";\n\tconst logger: Logger = config.logger ?? noopLogger;\n\n\t// Validate eagerly so misconfiguration surfaces at construction time, but\n\t// never throw — the SDK's contract is that all failures flow through\n\t// `Result`/`ResultAsync`. Capture the error and short-circuit any method\n\t// that depends on the validated fields (`apiUrl`, `encryptionKey`).\n\t// Methods that don't depend on them (init, getFingerprint, getDeviceDetails,\n\t// cleanupSeonStorage) remain usable so consumers can still hydrate device\n\t// fingerprints even with a partially-configured engine.\n\tconst configResult = validate(ZodFraudEngineConfigSchema, {\n\t\tapiUrl,\n\t\tencryptionKey,\n\t\tseonRegion,\n\t});\n\tconst configError = configResult.isErr() ? configResult.error : null;\n\tif (configError) {\n\t\tlogger.error(\n\t\t\t\"Fraud engine config invalid; API methods will return VALIDATION_ERROR until fixed\",\n\t\t\t{ error: configError.message },\n\t\t);\n\t}\n\n\tfunction linkOrderInternal(\n\t\tsigner: FraudEngineSigner,\n\t\tactivityLogId: number,\n\t\torderId: string,\n\t): ResultAsync<LinkOrderResult, FraudEngineError> {\n\t\treturn ResultAsync.fromPromise(\n\t\t\t(async () => {\n\t\t\t\tlogger.info(\"Linking order to activity log\", { activityLogId, orderId });\n\n\t\t\t\tconst signedHeaders = await getSignedHeaders(signer, \"link-order\");\n\n\t\t\t\tconst response = await fetch(`${apiUrl}/activity-logs/link-order`, {\n\t\t\t\t\tmethod: \"PATCH\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tactivity_log_id: activityLogId,\n\t\t\t\t\t\torder_id: orderId,\n\t\t\t\t\t\tuser_address: signer.address.toLowerCase(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new FraudEngineError(`Link order API returned ${response.status}`, {\n\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst data = (await response.json()) as LinkOrderResult;\n\t\t\t\tlogger.info(\"Order linked successfully\", { orderId });\n\t\t\t\treturn data;\n\t\t\t})(),\n\t\t\t(cause) => {\n\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\treturn new FraudEngineError(\"Link order failed\", {\n\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t}\n\n\tasync function checkBuyOrderInternal(params: {\n\t\tsigner: FraudEngineSigner;\n\t\torderDetails: BuyOrderDetails;\n\t\tuserDetails?: UserDetails;\n\t\torderSource?: string;\n\t}): Promise<FraudCheckApiResponse> {\n\t\tlogger.info(\"Checking buy order for fraud\", {\n\t\t\tcurrency: params.orderDetails.currency,\n\t\t\tfiatAmount: params.orderDetails.fiatAmount,\n\t\t});\n\n\t\tconst [seonSession, deviceDetails, signedHeaders] = await Promise.all([\n\t\t\tgetSeonSession(seonRegion),\n\t\t\tgetDeviceDetails(),\n\t\t\tgetSignedHeaders(params.signer, \"activity-log\"),\n\t\t]);\n\n\t\tconst device = { ...deviceDetails, seonSession };\n\n\t\tconst userAddress = params.signer.address.toLowerCase();\n\t\tconst timestamp = Date.now();\n\n\t\tconst payload = JSON.stringify({\n\t\t\tuser_details: {\n\t\t\t\tcurrency: params.userDetails?.currency,\n\t\t\t\tcountry: params.userDetails?.country,\n\t\t\t\tlanguage: params.userDetails?.language,\n\t\t\t\tlogin_method: params.userDetails?.loginMethod,\n\t\t\t\tlogin_email: params.userDetails?.loginEmail,\n\t\t\t\tlogin_phone: params.userDetails?.loginPhone,\n\t\t\t},\n\t\t\ttransaction_details: {\n\t\t\t\tcrypto_amount: params.orderDetails.cryptoAmount,\n\t\t\t\tfiat_amount: params.orderDetails.fiatAmount,\n\t\t\t\tcurrency: params.orderDetails.currency,\n\t\t\t\trecipient_address: params.orderDetails.recipientAddress,\n\t\t\t\tfee: params.orderDetails.fee,\n\t\t\t\tamount_after_fee: params.orderDetails.amountAfterFee,\n\t\t\t\tpayment_method: params.orderDetails.paymentMethod,\n\t\t\t\testimated_processing_time: params.orderDetails.estimatedProcessingTime,\n\t\t\t\torder_timestamp: timestamp,\n\t\t\t\torder_source: params.orderSource,\n\t\t\t},\n\t\t\tdevice_details: device,\n\t\t});\n\n\t\t// AAD format must match backend: \"type|user_address|timestamp\"\n\t\tconst aad = `buy_order|${userAddress}|${timestamp}`;\n\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\tconst response = await fetch(`${apiUrl}/activity-logs`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...signedHeaders,\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttype: \"buy_order\",\n\t\t\t\tuser_address: userAddress,\n\t\t\t\ttimestamp,\n\t\t\t\tencrypted_payload: encrypted,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new FraudEngineError(`Fraud check API returned ${response.status}`, {\n\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\tcontext: { status: response.status },\n\t\t\t});\n\t\t}\n\n\t\tconst data = (await response.json()) as FraudCheckApiResponse;\n\t\tlogger.info(\"Fraud check result\", {\n\t\t\tapproved: data.approved,\n\t\t\tactivityLogId: data.activity_log_id,\n\t\t});\n\t\treturn data;\n\t}\n\n\treturn {\n\t\tasync init() {\n\t\t\tlogger.info(\"Initializing fraud engine\");\n\t\t\ttry {\n\t\t\t\tinitSeon();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"SEON initialization failed\", { cause: String(cause) });\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait loadFingerprintAgent();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"FingerprintJS initialization failed\", {\n\t\t\t\t\tcause: String(cause),\n\t\t\t\t});\n\t\t\t}\n\t\t\tlogger.info(\"Fraud engine initialized\");\n\t\t},\n\n\t\tcheckBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t}): ResultAsync<FraudCheckResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\tcheckBuyOrderInternal(params).then((data) => ({\n\t\t\t\t\tapproved: data.approved,\n\t\t\t\t\tactivityLogId: data.activity_log_id,\n\t\t\t\t\tmessage: data.message,\n\t\t\t\t\tlinkOrder: (orderId: string) =>\n\t\t\t\t\t\tlinkOrderInternal(params.signer, data.activity_log_id, orderId),\n\t\t\t\t})),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fraud check failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tprocessBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t\tplaceOrder: () => Promise<string>;\n\t\t}): ResultAsync<ProcessBuyOrderResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlet activityLogId: number | null = null;\n\n\t\t\t\t\t// Step 1: Fraud check (fail-open)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fraudCheck = await checkBuyOrderInternal(params);\n\t\t\t\t\t\tif (!fraudCheck.approved) {\n\t\t\t\t\t\t\treturn { status: \"rejected\" as const, message: fraudCheck.message };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tactivityLogId = fraudCheck.activity_log_id;\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tlogger.error(\"Fraud check failed, proceeding with order (fail-open)\", {\n\t\t\t\t\t\t\terror: String(cause),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 2: Place order (consumer callback)\n\t\t\t\t\tlet orderId: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\torderId = await params.placeOrder();\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tthrow new FraudEngineError(\"Place order callback failed\", {\n\t\t\t\t\t\t\tcode: \"PLACE_ORDER_ERROR\",\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 3: Auto-link (fire-and-forget, only if fraud check succeeded)\n\t\t\t\t\tif (activityLogId !== null) {\n\t\t\t\t\t\tlinkOrderInternal(params.signer, activityLogId, orderId).mapErr((e) => {\n\t\t\t\t\t\t\tlogger.error(\"Failed to link order to activity log\", {\n\t\t\t\t\t\t\t\torderId,\n\t\t\t\t\t\t\t\tactivityLogId: activityLogId as number,\n\t\t\t\t\t\t\t\terror: e.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { status: \"placed\" as const, orderId };\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Process buy order failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tlogFingerprint(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t}): ResultAsync<FingerprintLogResult | null, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlogger.info(\"Logging fingerprint\");\n\n\t\t\t\t\tconst fingerprintResult = await getFingerprintResult(5000);\n\t\t\t\t\tif (!fingerprintResult) {\n\t\t\t\t\t\tlogger.warn(\"Fingerprint not available, skipping\");\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst signedHeaders = await getSignedHeaders(params.signer, \"fingerprint-log\");\n\n\t\t\t\t\tconst normalizedAddress = params.signer.address.toLowerCase();\n\t\t\t\t\tconst timestamp = Date.now();\n\n\t\t\t\t\tconst payload = JSON.stringify({\n\t\t\t\t\t\tfingerprint_id: fingerprintResult.visitorId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// AAD format: \"fingerprint|user_address|timestamp\"\n\t\t\t\t\tconst aad = `fingerprint|${normalizedAddress}|${timestamp}`;\n\t\t\t\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\t\t\t\tconst response = await fetch(`${apiUrl}/fingerprint-log`, {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\tuser_address: normalizedAddress,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\tencrypted_payload: encrypted,\n\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new FraudEngineError(`Fingerprint log API returned ${response.status}`, {\n\t\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as FingerprintLogResult;\n\t\t\t\t\tlogger.info(\"Fingerprint logged successfully\");\n\t\t\t\t\treturn data;\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fingerprint log failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync getFingerprint() {\n\t\t\treturn getFingerprintResult(5000);\n\t\t},\n\n\t\tasync getDeviceDetails() {\n\t\t\treturn getDeviceDetails();\n\t\t},\n\n\t\tcleanupSeonStorage() {\n\t\t\tcleanupSeon();\n\t\t},\n\t};\n}\n","/** Converts a hex string to a Uint8Array. */\nexport function hexToBytes(hex: string): Uint8Array {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tbytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);\n\t}\n\treturn bytes;\n}\n\n/** Converts a Uint8Array to a base64 string. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary);\n}\n","export interface Logger {\n\tdebug(message: string, data?: Record<string, unknown>): void;\n\tinfo(message: string, data?: Record<string, unknown>): void;\n\twarn(message: string, data?: Record<string, unknown>): void;\n\terror(message: string, data?: Record<string, unknown>): void;\n}\n\nconst noop = () => {};\n\nexport const noopLogger: Logger = {\n\tdebug: noop,\n\tinfo: noop,\n\twarn: noop,\n\terror: noop,\n};\n","import { ResultAsync } from \"neverthrow\";\nimport { SdkError } from \"../validation\";\nimport { sleep } from \"./sleep\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RETRIES = 3;\nconst BACKOFF_MS = 500;\n\nexport type SubgraphErrorCode = \"HTTP_ERROR\" | \"GRAPHQL_ERROR\" | \"NO_DATA\" | \"TRANSPORT_ERROR\";\n\n/** Transport-level error from the shared subgraph client. Consumers remap to their own domain error. */\nexport class SubgraphError extends SdkError<SubgraphErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: SubgraphErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SubgraphError\";\n\t}\n}\n\nexport interface SubgraphQueryParams {\n\treadonly query: string;\n\treadonly variables?: Record<string, unknown>;\n\treadonly timeoutMs?: number;\n}\n\nfunction isTransient(error: unknown): boolean {\n\tif (error instanceof SubgraphError) {\n\t\tif (error.code !== \"HTTP_ERROR\") return false;\n\t\tconst status = (error.context as { status?: number } | undefined)?.status;\n\t\treturn typeof status === \"number\" && status >= 500;\n\t}\n\tif (error instanceof DOMException && error.name === \"AbortError\") return true;\n\tif (error instanceof TypeError) return true;\n\treturn false;\n}\n\n/**\n * POSTs a GraphQL query to `url` with retry + timeout. Returns the `data` field on\n * success. All transport and GraphQL-level failures surface as `SubgraphError`.\n */\nexport function querySubgraph(\n\turl: string,\n\tparams: SubgraphQueryParams,\n): ResultAsync<unknown, SubgraphError> {\n\tconst timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n\tconst fetchOnce = async () => {\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tquery: params.query,\n\t\t\t\t\tvariables: params.variables,\n\t\t\t\t}),\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new SubgraphError(`Subgraph request failed (status: ${response.status})`, {\n\t\t\t\t\tcode: \"HTTP_ERROR\",\n\t\t\t\t\tcause: response,\n\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst json = await response.json();\n\n\t\t\tif (json.errors?.length > 0) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned GraphQL errors\", {\n\t\t\t\t\tcode: \"GRAPHQL_ERROR\",\n\t\t\t\t\tcause: json.errors,\n\t\t\t\t\tcontext: { errors: json.errors },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!json.data) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned no data\", {\n\t\t\t\t\tcode: \"NO_DATA\",\n\t\t\t\t\tcause: \"Missing data field in GraphQL response\",\n\t\t\t\t\tcontext: { response: json },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn json.data;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t};\n\n\tconst fetchWithRetry = async () => {\n\t\tfor (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await fetchOnce();\n\t\t\t} catch (error) {\n\t\t\t\tconst lastAttempt = attempt === MAX_RETRIES;\n\t\t\t\tif (lastAttempt || !isTransient(error)) throw error;\n\t\t\t\tawait sleep(BACKOFF_MS * (attempt + 1));\n\t\t\t}\n\t\t}\n\t\tthrow new SubgraphError(\"Subgraph query exhausted retries\", { code: \"TRANSPORT_ERROR\" });\n\t};\n\n\treturn ResultAsync.fromPromise(fetchWithRetry(), (error) =>\n\t\terror instanceof SubgraphError\n\t\t\t? error\n\t\t\t: new SubgraphError(\"Subgraph query failed\", {\n\t\t\t\t\tcode: \"TRANSPORT_ERROR\",\n\t\t\t\t\tcause: error,\n\t\t\t\t}),\n\t);\n}\n","export class SdkError<TCode extends string = string> extends Error {\n\treadonly code: TCode;\n\treadonly cause?: unknown;\n\treadonly context?: Record<string, unknown>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: TCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SdkError\";\n\t\tthis.code = options.code;\n\t\tthis.cause = options.cause;\n\t\tthis.context = options.context;\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { isAddress } from \"viem\";\nimport { z } from \"zod\";\nimport { CURRENCY_CODES } from \"../country/currency\";\n\nexport const ZodAddressSchema = z\n\t.string()\n\t.refine((s) => isAddress(s), { message: \"Invalid Ethereum address\" });\n\nexport const ZodCurrencySchema = z.enum(CURRENCY_CODES);\n\nexport function validate<S extends z.ZodType, E>(\n\tschema: S,\n\tdata: unknown,\n\ttoError: (message: string, cause: unknown, data: unknown) => E,\n): Result<z.infer<S>, E> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(toError(z.prettifyError(result.error), result.error, data));\n}\n","/**\n * All supported currency symbols. Single source of truth for the SDK.\n *\n * Lives alongside the country metadata so that adding a currency is a\n * single-folder operation: drop a new file in `currencies/<code>.ts`, add it\n * to this map, and both `COUNTRY_OPTIONS` and `ZodCurrencySchema` pick it up.\n */\nexport const CURRENCY = {\n\tIDR: \"IDR\",\n\tINR: \"INR\",\n\tBRL: \"BRL\",\n\tARS: \"ARS\",\n\tMEX: \"MEX\",\n\tVEN: \"VEN\",\n\tEUR: \"EUR\",\n\tNGN: \"NGN\",\n\tUSD: \"USD\",\n\tCOP: \"COP\",\n\tECU: \"ECU\",\n\tPEN: \"PEN\",\n} as const;\n\n/** Union of supported currency codes. */\nexport type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];\n\n/**\n * Tuple form of the currency codes — used by `z.enum(...)` in the shared\n * validation layer. Narrow tuple type required by Zod.\n */\nexport const CURRENCY_CODES = Object.values(CURRENCY) as [CurrencyCode, ...CurrencyCode[]];\n","import type { DeviceDetails } from \"./types\";\n\ninterface NavigatorConnection {\n\treadonly effectiveType?: string;\n}\n\ninterface NavigatorExtended extends Navigator {\n\treadonly connection?: NavigatorConnection;\n\treadonly deviceMemory?: number;\n}\n\nexport function getBasicDeviceDetails(): Omit<DeviceDetails, \"ip\" | \"seonSession\"> {\n\tconst nav = typeof navigator !== \"undefined\" ? (navigator as NavigatorExtended) : undefined;\n\tconst win = typeof window !== \"undefined\" ? window : undefined;\n\n\treturn {\n\t\tuserAgent: nav?.userAgent ?? \"\",\n\t\tplatform: nav?.platform ?? \"\",\n\t\tlanguage: nav?.language ?? \"\",\n\t\tlanguages: nav ? Array.from(nav.languages) : [],\n\t\tscreenWidth: win?.screen?.width ?? 0,\n\t\tscreenHeight: win?.screen?.height ?? 0,\n\t\tdevicePixelRatio: win?.devicePixelRatio ?? 1,\n\t\ttimezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone ?? \"\",\n\t\ttimezoneOffset: new Date().getTimezoneOffset(),\n\t\tcookiesEnabled: nav?.cookieEnabled ?? false,\n\t\tdoNotTrack: nav?.doNotTrack ?? null,\n\t\tonline: nav?.onLine ?? true,\n\t\tconnectionType: nav?.connection?.effectiveType,\n\t\tdeviceMemory: nav?.deviceMemory,\n\t\thardwareConcurrency: nav?.hardwareConcurrency,\n\t\ttouchSupport: nav ? \"ontouchstart\" in window : false,\n\t\tmaxTouchPoints: nav?.maxTouchPoints ?? 0,\n\t\tvendor: nav?.vendor ?? \"\",\n\t\tappVersion: nav?.appVersion ?? \"\",\n\t\tcolorDepth: win?.screen?.colorDepth ?? 0,\n\t\tpixelDepth: win?.screen?.pixelDepth ?? 0,\n\t};\n}\n\nexport async function fetchIpAddress(): Promise<string | undefined> {\n\ttry {\n\t\tconst response = await fetch(\"https://api.ipify.org?format=json\");\n\t\tconst data = (await response.json()) as { ip: string };\n\t\treturn data.ip;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport async function getDeviceDetails(seonSession?: string): Promise<DeviceDetails> {\n\tconst basic = getBasicDeviceDetails();\n\tconst ip = await fetchIpAddress();\n\treturn { ...basic, ip, seonSession };\n}\n","import { SdkError } from \"../validation\";\n\nexport type FraudEngineErrorCode =\n\t| \"API_ERROR\"\n\t| \"ENCRYPTION_ERROR\"\n\t| \"SIGNING_ERROR\"\n\t| \"VALIDATION_ERROR\"\n\t| \"NETWORK_ERROR\"\n\t| \"PLACE_ORDER_ERROR\";\n\nexport class FraudEngineError extends SdkError<FraudEngineErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: FraudEngineErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"FraudEngineError\";\n\t}\n}\n","import { bytesToBase64, hexToBytes } from \"../lib/encoding\";\nimport { FraudEngineError } from \"./errors\";\n\nexport async function getEncryptionKey(encryptionKeyHex: string): Promise<CryptoKey> {\n\tconst keyBytes = hexToBytes(encryptionKeyHex);\n\treturn crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tkeyBytes.buffer as ArrayBuffer,\n\t\t{ name: \"AES-GCM\" },\n\t\tfalse,\n\t\t[\"encrypt\"],\n\t);\n}\n\nexport async function encryptPayload(\n\tpayload: string,\n\taad: string,\n\tencryptionKeyHex: string,\n): Promise<string> {\n\ttry {\n\t\tconst key = await getEncryptionKey(encryptionKeyHex);\n\t\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\t\tconst encoded = new TextEncoder().encode(payload);\n\t\tconst aadEncoded = new TextEncoder().encode(aad);\n\n\t\tconst ciphertext = await crypto.subtle.encrypt(\n\t\t\t{ name: \"AES-GCM\", iv, additionalData: aadEncoded, tagLength: 128 },\n\t\t\tkey,\n\t\t\tencoded,\n\t\t);\n\n\t\t// IV (12 bytes) + ciphertext + auth tag (16 bytes appended by Web Crypto)\n\t\tconst result = new Uint8Array(iv.length + ciphertext.byteLength);\n\t\tresult.set(iv, 0);\n\t\tresult.set(new Uint8Array(ciphertext), iv.length);\n\n\t\treturn bytesToBase64(result);\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Encryption failed\", {\n\t\t\tcode: \"ENCRYPTION_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import FingerprintJS from \"@fingerprintjs/fingerprintjs\";\n\nlet agent: Awaited<ReturnType<typeof FingerprintJS.load>> | null = null;\nlet cachedResult: { visitorId: string; confidence: number } | null = null;\n\nexport async function loadFingerprintAgent(): Promise<void> {\n\tif (agent) return;\n\tagent = await FingerprintJS.load();\n}\n\nexport async function getFingerprint(\n\ttimeoutMs = 5000,\n): Promise<{ visitorId: string; confidence: number } | null> {\n\tif (cachedResult) return cachedResult;\n\tif (!agent) {\n\t\ttry {\n\t\t\tawait loadFingerprintAgent();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst loadedAgent = agent;\n\tif (!loadedAgent) return null;\n\ttry {\n\t\tconst result = await Promise.race([\n\t\t\tloadedAgent.get(),\n\t\t\tnew Promise<never>((_, reject) =>\n\t\t\t\tsetTimeout(() => reject(new Error(\"FingerprintJS timed out\")), timeoutMs),\n\t\t\t),\n\t\t]);\n\t\tcachedResult = {\n\t\t\tvisitorId: result.visitorId,\n\t\t\tconfidence: result.confidence.score,\n\t\t};\n\t\treturn cachedResult;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import seon from \"@seontechnologies/seon-javascript-sdk\";\n\nlet initialized = false;\n\nexport function initSeon(): void {\n\tif (initialized) return;\n\tseon.init();\n\tinitialized = true;\n}\n\nexport async function getSeonSession(region: string): Promise<string | undefined> {\n\ttry {\n\t\treturn await seon.getSession({\n\t\t\tgeolocation: { canPrompt: false },\n\t\t\tnetworkTimeoutMs: 2000,\n\t\t\tfieldTimeoutMs: 2000,\n\t\t\tregion,\n\t\t\tsilentMode: true,\n\t\t});\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function cleanupSeonStorage(): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst keysToRemove: string[] = [];\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst key = localStorage.key(i);\n\t\tif (key?.startsWith(\"seon_session_sent_\")) keysToRemove.push(key);\n\t}\n\tfor (const key of keysToRemove) localStorage.removeItem(key);\n}\n","import { FraudEngineError } from \"./errors\";\nimport type { FraudEngineSigner } from \"./types\";\n\nexport async function getSignedHeaders(\n\tsigner: FraudEngineSigner,\n\taction: \"activity-log\" | \"link-order\" | \"fingerprint-log\",\n): Promise<Record<string, string>> {\n\t// The signed message binds BOTH addresses: the key that produces the\n\t// signature (the admin EOA for AA smart wallets) and the subject the\n\t// request acts for (the smart account). Binding the subject stops a\n\t// signature captured for one account being replayed against another\n\t// inside the backend's freshness window.\n\t//\n\t// This is not by itself an authorisation control — the backend decides\n\t// entitlement by checking on-chain that the signer is an admin of the\n\t// subject account. See fraud-engine `core/wallet_auth.py`.\n\t//\n\t// For plain EOA signers where no separate `signerAddress` is provided,\n\t// both values collapse to `signer.address`.\n\tconst signingAddress = (signer.signerAddress ?? signer.address).toLowerCase();\n\tconst subjectAddress = signer.address.toLowerCase();\n\tconst timestamp = Math.floor(Date.now() / 1000).toString();\n\tconst message = `${action}:${signingAddress}:${subjectAddress}:${timestamp}`;\n\n\ttry {\n\t\tconst signature = await signer.signMessage(message);\n\t\treturn {\n\t\t\t\"X-Signer-Address\": signingAddress,\n\t\t\t\"X-Timestamp\": timestamp,\n\t\t\t\"X-Signature\": signature,\n\t\t};\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Failed to sign message\", {\n\t\t\tcode: \"SIGNING_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { z } from \"zod\";\nimport { FraudEngineError } from \"./errors\";\n\n// ── Config schema ──────────────────────────────────────────────────────\n\nexport const ZodFraudEngineConfigSchema = z.object({\n\tapiUrl: z.url(),\n\tencryptionKey: z.string().min(1),\n\tseonRegion: z.string().optional(),\n});\n\n// ── Buy order details schema ───────────────────────────────────────────\n\nexport const ZodBuyOrderDetailsSchema = z.object({\n\tcryptoAmount: z.number(),\n\tfiatAmount: z.number(),\n\tcurrency: z.string().min(1),\n\trecipientAddress: z.string().min(1),\n\tfee: z.number(),\n\tamountAfterFee: z.number(),\n\tpaymentMethod: z.string().optional(),\n\testimatedProcessingTime: z.string().optional(),\n});\n\n// ── User details schema ────────────────────────────────────────────────\n\nexport const ZodUserDetailsSchema = z.object({\n\tcurrency: z.string().optional(),\n\tcountry: z.string().optional(),\n\tlanguage: z.string().optional(),\n\tloginMethod: z.enum([\"email\", \"google\", \"phone\", \"passkey\", \"unknown\"]).optional(),\n\tloginEmail: z.string().optional(),\n\tloginPhone: z.string().optional(),\n});\n\n// ── Link order schema ──────────────────────────────────────────────────\n\nexport const ZodLinkOrderParamsSchema = z.object({\n\tactivityLogId: z.number().int().positive(),\n\torderId: z.string().min(1),\n});\n\n// ── Validate helper ────────────────────────────────────────────────────\n\nexport function validate<S extends z.ZodType>(\n\tschema: S,\n\tdata: unknown,\n): Result<z.infer<S>, FraudEngineError> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(\n\t\tnew FraudEngineError(z.prettifyError(result.error), {\n\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\tcause: result.error,\n\t\t\tcontext: { data },\n\t\t}),\n\t);\n}\n"],"mappings":"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","names":["import_neverthrow","import_neverthrow","FingerprintJS","seon","import_neverthrow","import_zod","validate","validate"]}
@@ -225,8 +225,9 @@ function cleanupSeonStorage() {
225
225
  // src/fraud-engine/signing.ts
226
226
  async function getSignedHeaders(signer, action) {
227
227
  const signingAddress = (signer.signerAddress ?? signer.address).toLowerCase();
228
+ const subjectAddress = signer.address.toLowerCase();
228
229
  const timestamp = Math.floor(Date.now() / 1e3).toString();
229
- const message = `${action}:${signingAddress}:${timestamp}`;
230
+ const message = `${action}:${signingAddress}:${subjectAddress}:${timestamp}`;
230
231
  try {
231
232
  const signature = await signer.signMessage(message);
232
233
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fraud-engine/client.ts","../src/lib/encoding.ts","../src/lib/logger.ts","../src/lib/subgraph.ts","../src/validation/errors.validation.ts","../src/validation/schemas.validation.ts","../src/country/currency.ts","../src/fraud-engine/device.ts","../src/fraud-engine/errors.ts","../src/fraud-engine/encryption.ts","../src/fraud-engine/fingerprint.ts","../src/fraud-engine/seon.ts","../src/fraud-engine/signing.ts","../src/fraud-engine/validation.ts"],"sourcesContent":["import { errAsync, ResultAsync } from \"neverthrow\";\nimport { type Logger, noopLogger } from \"../lib\";\nimport { getDeviceDetails } from \"./device\";\nimport { encryptPayload } from \"./encryption\";\nimport { FraudEngineError } from \"./errors\";\nimport { getFingerprint as getFingerprintResult, loadFingerprintAgent } from \"./fingerprint\";\nimport { cleanupSeonStorage as cleanupSeon, getSeonSession, initSeon } from \"./seon\";\nimport { getSignedHeaders } from \"./signing\";\nimport type {\n\tBuyOrderDetails,\n\tFingerprintLogResult,\n\tFraudCheckApiResponse,\n\tFraudCheckResult,\n\tFraudEngine,\n\tFraudEngineConfig,\n\tFraudEngineSigner,\n\tLinkOrderResult,\n\tProcessBuyOrderResult,\n\tUserDetails,\n} from \"./types\";\nimport { validate, ZodFraudEngineConfigSchema } from \"./validation\";\n\nexport function createFraudEngine(config: FraudEngineConfig): FraudEngine {\n\tconst { apiUrl, encryptionKey } = config;\n\tconst seonRegion = config.seonRegion ?? \"asia\";\n\tconst logger: Logger = config.logger ?? noopLogger;\n\n\t// Validate eagerly so misconfiguration surfaces at construction time, but\n\t// never throw — the SDK's contract is that all failures flow through\n\t// `Result`/`ResultAsync`. Capture the error and short-circuit any method\n\t// that depends on the validated fields (`apiUrl`, `encryptionKey`).\n\t// Methods that don't depend on them (init, getFingerprint, getDeviceDetails,\n\t// cleanupSeonStorage) remain usable so consumers can still hydrate device\n\t// fingerprints even with a partially-configured engine.\n\tconst configResult = validate(ZodFraudEngineConfigSchema, {\n\t\tapiUrl,\n\t\tencryptionKey,\n\t\tseonRegion,\n\t});\n\tconst configError = configResult.isErr() ? configResult.error : null;\n\tif (configError) {\n\t\tlogger.error(\n\t\t\t\"Fraud engine config invalid; API methods will return VALIDATION_ERROR until fixed\",\n\t\t\t{ error: configError.message },\n\t\t);\n\t}\n\n\tfunction linkOrderInternal(\n\t\tsigner: FraudEngineSigner,\n\t\tactivityLogId: number,\n\t\torderId: string,\n\t): ResultAsync<LinkOrderResult, FraudEngineError> {\n\t\treturn ResultAsync.fromPromise(\n\t\t\t(async () => {\n\t\t\t\tlogger.info(\"Linking order to activity log\", { activityLogId, orderId });\n\n\t\t\t\tconst signedHeaders = await getSignedHeaders(signer, \"link-order\");\n\n\t\t\t\tconst response = await fetch(`${apiUrl}/activity-logs/link-order`, {\n\t\t\t\t\tmethod: \"PATCH\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tactivity_log_id: activityLogId,\n\t\t\t\t\t\torder_id: orderId,\n\t\t\t\t\t\tuser_address: signer.address.toLowerCase(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new FraudEngineError(`Link order API returned ${response.status}`, {\n\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst data = (await response.json()) as LinkOrderResult;\n\t\t\t\tlogger.info(\"Order linked successfully\", { orderId });\n\t\t\t\treturn data;\n\t\t\t})(),\n\t\t\t(cause) => {\n\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\treturn new FraudEngineError(\"Link order failed\", {\n\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t}\n\n\tasync function checkBuyOrderInternal(params: {\n\t\tsigner: FraudEngineSigner;\n\t\torderDetails: BuyOrderDetails;\n\t\tuserDetails?: UserDetails;\n\t\torderSource?: string;\n\t}): Promise<FraudCheckApiResponse> {\n\t\tlogger.info(\"Checking buy order for fraud\", {\n\t\t\tcurrency: params.orderDetails.currency,\n\t\t\tfiatAmount: params.orderDetails.fiatAmount,\n\t\t});\n\n\t\tconst [seonSession, deviceDetails, signedHeaders] = await Promise.all([\n\t\t\tgetSeonSession(seonRegion),\n\t\t\tgetDeviceDetails(),\n\t\t\tgetSignedHeaders(params.signer, \"activity-log\"),\n\t\t]);\n\n\t\tconst device = { ...deviceDetails, seonSession };\n\n\t\tconst userAddress = params.signer.address.toLowerCase();\n\t\tconst timestamp = Date.now();\n\n\t\tconst payload = JSON.stringify({\n\t\t\tuser_details: {\n\t\t\t\tcurrency: params.userDetails?.currency,\n\t\t\t\tcountry: params.userDetails?.country,\n\t\t\t\tlanguage: params.userDetails?.language,\n\t\t\t\tlogin_method: params.userDetails?.loginMethod,\n\t\t\t\tlogin_email: params.userDetails?.loginEmail,\n\t\t\t\tlogin_phone: params.userDetails?.loginPhone,\n\t\t\t},\n\t\t\ttransaction_details: {\n\t\t\t\tcrypto_amount: params.orderDetails.cryptoAmount,\n\t\t\t\tfiat_amount: params.orderDetails.fiatAmount,\n\t\t\t\tcurrency: params.orderDetails.currency,\n\t\t\t\trecipient_address: params.orderDetails.recipientAddress,\n\t\t\t\tfee: params.orderDetails.fee,\n\t\t\t\tamount_after_fee: params.orderDetails.amountAfterFee,\n\t\t\t\tpayment_method: params.orderDetails.paymentMethod,\n\t\t\t\testimated_processing_time: params.orderDetails.estimatedProcessingTime,\n\t\t\t\torder_timestamp: timestamp,\n\t\t\t\torder_source: params.orderSource,\n\t\t\t},\n\t\t\tdevice_details: device,\n\t\t});\n\n\t\t// AAD format must match backend: \"type|user_address|timestamp\"\n\t\tconst aad = `buy_order|${userAddress}|${timestamp}`;\n\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\tconst response = await fetch(`${apiUrl}/activity-logs`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...signedHeaders,\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttype: \"buy_order\",\n\t\t\t\tuser_address: userAddress,\n\t\t\t\ttimestamp,\n\t\t\t\tencrypted_payload: encrypted,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new FraudEngineError(`Fraud check API returned ${response.status}`, {\n\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\tcontext: { status: response.status },\n\t\t\t});\n\t\t}\n\n\t\tconst data = (await response.json()) as FraudCheckApiResponse;\n\t\tlogger.info(\"Fraud check result\", {\n\t\t\tapproved: data.approved,\n\t\t\tactivityLogId: data.activity_log_id,\n\t\t});\n\t\treturn data;\n\t}\n\n\treturn {\n\t\tasync init() {\n\t\t\tlogger.info(\"Initializing fraud engine\");\n\t\t\ttry {\n\t\t\t\tinitSeon();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"SEON initialization failed\", { cause: String(cause) });\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait loadFingerprintAgent();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"FingerprintJS initialization failed\", {\n\t\t\t\t\tcause: String(cause),\n\t\t\t\t});\n\t\t\t}\n\t\t\tlogger.info(\"Fraud engine initialized\");\n\t\t},\n\n\t\tcheckBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t}): ResultAsync<FraudCheckResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\tcheckBuyOrderInternal(params).then((data) => ({\n\t\t\t\t\tapproved: data.approved,\n\t\t\t\t\tactivityLogId: data.activity_log_id,\n\t\t\t\t\tmessage: data.message,\n\t\t\t\t\tlinkOrder: (orderId: string) =>\n\t\t\t\t\t\tlinkOrderInternal(params.signer, data.activity_log_id, orderId),\n\t\t\t\t})),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fraud check failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tprocessBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t\tplaceOrder: () => Promise<string>;\n\t\t}): ResultAsync<ProcessBuyOrderResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlet activityLogId: number | null = null;\n\n\t\t\t\t\t// Step 1: Fraud check (fail-open)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fraudCheck = await checkBuyOrderInternal(params);\n\t\t\t\t\t\tif (!fraudCheck.approved) {\n\t\t\t\t\t\t\treturn { status: \"rejected\" as const, message: fraudCheck.message };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tactivityLogId = fraudCheck.activity_log_id;\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tlogger.error(\"Fraud check failed, proceeding with order (fail-open)\", {\n\t\t\t\t\t\t\terror: String(cause),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 2: Place order (consumer callback)\n\t\t\t\t\tlet orderId: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\torderId = await params.placeOrder();\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tthrow new FraudEngineError(\"Place order callback failed\", {\n\t\t\t\t\t\t\tcode: \"PLACE_ORDER_ERROR\",\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 3: Auto-link (fire-and-forget, only if fraud check succeeded)\n\t\t\t\t\tif (activityLogId !== null) {\n\t\t\t\t\t\tlinkOrderInternal(params.signer, activityLogId, orderId).mapErr((e) => {\n\t\t\t\t\t\t\tlogger.error(\"Failed to link order to activity log\", {\n\t\t\t\t\t\t\t\torderId,\n\t\t\t\t\t\t\t\tactivityLogId: activityLogId as number,\n\t\t\t\t\t\t\t\terror: e.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { status: \"placed\" as const, orderId };\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Process buy order failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tlogFingerprint(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t}): ResultAsync<FingerprintLogResult | null, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlogger.info(\"Logging fingerprint\");\n\n\t\t\t\t\tconst fingerprintResult = await getFingerprintResult(5000);\n\t\t\t\t\tif (!fingerprintResult) {\n\t\t\t\t\t\tlogger.warn(\"Fingerprint not available, skipping\");\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst signedHeaders = await getSignedHeaders(params.signer, \"fingerprint-log\");\n\n\t\t\t\t\tconst normalizedAddress = params.signer.address.toLowerCase();\n\t\t\t\t\tconst timestamp = Date.now();\n\n\t\t\t\t\tconst payload = JSON.stringify({\n\t\t\t\t\t\tfingerprint_id: fingerprintResult.visitorId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// AAD format: \"fingerprint|user_address|timestamp\"\n\t\t\t\t\tconst aad = `fingerprint|${normalizedAddress}|${timestamp}`;\n\t\t\t\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\t\t\t\tconst response = await fetch(`${apiUrl}/fingerprint-log`, {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\tuser_address: normalizedAddress,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\tencrypted_payload: encrypted,\n\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new FraudEngineError(`Fingerprint log API returned ${response.status}`, {\n\t\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as FingerprintLogResult;\n\t\t\t\t\tlogger.info(\"Fingerprint logged successfully\");\n\t\t\t\t\treturn data;\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fingerprint log failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync getFingerprint() {\n\t\t\treturn getFingerprintResult(5000);\n\t\t},\n\n\t\tasync getDeviceDetails() {\n\t\t\treturn getDeviceDetails();\n\t\t},\n\n\t\tcleanupSeonStorage() {\n\t\t\tcleanupSeon();\n\t\t},\n\t};\n}\n","/** Converts a hex string to a Uint8Array. */\nexport function hexToBytes(hex: string): Uint8Array {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tbytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);\n\t}\n\treturn bytes;\n}\n\n/** Converts a Uint8Array to a base64 string. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary);\n}\n","export interface Logger {\n\tdebug(message: string, data?: Record<string, unknown>): void;\n\tinfo(message: string, data?: Record<string, unknown>): void;\n\twarn(message: string, data?: Record<string, unknown>): void;\n\terror(message: string, data?: Record<string, unknown>): void;\n}\n\nconst noop = () => {};\n\nexport const noopLogger: Logger = {\n\tdebug: noop,\n\tinfo: noop,\n\twarn: noop,\n\terror: noop,\n};\n","import { ResultAsync } from \"neverthrow\";\nimport { SdkError } from \"../validation\";\nimport { sleep } from \"./sleep\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RETRIES = 3;\nconst BACKOFF_MS = 500;\n\nexport type SubgraphErrorCode = \"HTTP_ERROR\" | \"GRAPHQL_ERROR\" | \"NO_DATA\" | \"TRANSPORT_ERROR\";\n\n/** Transport-level error from the shared subgraph client. Consumers remap to their own domain error. */\nexport class SubgraphError extends SdkError<SubgraphErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: SubgraphErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SubgraphError\";\n\t}\n}\n\nexport interface SubgraphQueryParams {\n\treadonly query: string;\n\treadonly variables?: Record<string, unknown>;\n\treadonly timeoutMs?: number;\n}\n\nfunction isTransient(error: unknown): boolean {\n\tif (error instanceof SubgraphError) {\n\t\tif (error.code !== \"HTTP_ERROR\") return false;\n\t\tconst status = (error.context as { status?: number } | undefined)?.status;\n\t\treturn typeof status === \"number\" && status >= 500;\n\t}\n\tif (error instanceof DOMException && error.name === \"AbortError\") return true;\n\tif (error instanceof TypeError) return true;\n\treturn false;\n}\n\n/**\n * POSTs a GraphQL query to `url` with retry + timeout. Returns the `data` field on\n * success. All transport and GraphQL-level failures surface as `SubgraphError`.\n */\nexport function querySubgraph(\n\turl: string,\n\tparams: SubgraphQueryParams,\n): ResultAsync<unknown, SubgraphError> {\n\tconst timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n\tconst fetchOnce = async () => {\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tquery: params.query,\n\t\t\t\t\tvariables: params.variables,\n\t\t\t\t}),\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new SubgraphError(`Subgraph request failed (status: ${response.status})`, {\n\t\t\t\t\tcode: \"HTTP_ERROR\",\n\t\t\t\t\tcause: response,\n\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst json = await response.json();\n\n\t\t\tif (json.errors?.length > 0) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned GraphQL errors\", {\n\t\t\t\t\tcode: \"GRAPHQL_ERROR\",\n\t\t\t\t\tcause: json.errors,\n\t\t\t\t\tcontext: { errors: json.errors },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!json.data) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned no data\", {\n\t\t\t\t\tcode: \"NO_DATA\",\n\t\t\t\t\tcause: \"Missing data field in GraphQL response\",\n\t\t\t\t\tcontext: { response: json },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn json.data;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t};\n\n\tconst fetchWithRetry = async () => {\n\t\tfor (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await fetchOnce();\n\t\t\t} catch (error) {\n\t\t\t\tconst lastAttempt = attempt === MAX_RETRIES;\n\t\t\t\tif (lastAttempt || !isTransient(error)) throw error;\n\t\t\t\tawait sleep(BACKOFF_MS * (attempt + 1));\n\t\t\t}\n\t\t}\n\t\tthrow new SubgraphError(\"Subgraph query exhausted retries\", { code: \"TRANSPORT_ERROR\" });\n\t};\n\n\treturn ResultAsync.fromPromise(fetchWithRetry(), (error) =>\n\t\terror instanceof SubgraphError\n\t\t\t? error\n\t\t\t: new SubgraphError(\"Subgraph query failed\", {\n\t\t\t\t\tcode: \"TRANSPORT_ERROR\",\n\t\t\t\t\tcause: error,\n\t\t\t\t}),\n\t);\n}\n","export class SdkError<TCode extends string = string> extends Error {\n\treadonly code: TCode;\n\treadonly cause?: unknown;\n\treadonly context?: Record<string, unknown>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: TCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SdkError\";\n\t\tthis.code = options.code;\n\t\tthis.cause = options.cause;\n\t\tthis.context = options.context;\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { isAddress } from \"viem\";\nimport { z } from \"zod\";\nimport { CURRENCY_CODES } from \"../country/currency\";\n\nexport const ZodAddressSchema = z\n\t.string()\n\t.refine((s) => isAddress(s), { message: \"Invalid Ethereum address\" });\n\nexport const ZodCurrencySchema = z.enum(CURRENCY_CODES);\n\nexport function validate<S extends z.ZodType, E>(\n\tschema: S,\n\tdata: unknown,\n\ttoError: (message: string, cause: unknown, data: unknown) => E,\n): Result<z.infer<S>, E> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(toError(z.prettifyError(result.error), result.error, data));\n}\n","/**\n * All supported currency symbols. Single source of truth for the SDK.\n *\n * Lives alongside the country metadata so that adding a currency is a\n * single-folder operation: drop a new file in `currencies/<code>.ts`, add it\n * to this map, and both `COUNTRY_OPTIONS` and `ZodCurrencySchema` pick it up.\n */\nexport const CURRENCY = {\n\tIDR: \"IDR\",\n\tINR: \"INR\",\n\tBRL: \"BRL\",\n\tARS: \"ARS\",\n\tMEX: \"MEX\",\n\tVEN: \"VEN\",\n\tEUR: \"EUR\",\n\tNGN: \"NGN\",\n\tUSD: \"USD\",\n\tCOP: \"COP\",\n\tECU: \"ECU\",\n\tPEN: \"PEN\",\n} as const;\n\n/** Union of supported currency codes. */\nexport type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];\n\n/**\n * Tuple form of the currency codes — used by `z.enum(...)` in the shared\n * validation layer. Narrow tuple type required by Zod.\n */\nexport const CURRENCY_CODES = Object.values(CURRENCY) as [CurrencyCode, ...CurrencyCode[]];\n","import type { DeviceDetails } from \"./types\";\n\ninterface NavigatorConnection {\n\treadonly effectiveType?: string;\n}\n\ninterface NavigatorExtended extends Navigator {\n\treadonly connection?: NavigatorConnection;\n\treadonly deviceMemory?: number;\n}\n\nexport function getBasicDeviceDetails(): Omit<DeviceDetails, \"ip\" | \"seonSession\"> {\n\tconst nav = typeof navigator !== \"undefined\" ? (navigator as NavigatorExtended) : undefined;\n\tconst win = typeof window !== \"undefined\" ? window : undefined;\n\n\treturn {\n\t\tuserAgent: nav?.userAgent ?? \"\",\n\t\tplatform: nav?.platform ?? \"\",\n\t\tlanguage: nav?.language ?? \"\",\n\t\tlanguages: nav ? Array.from(nav.languages) : [],\n\t\tscreenWidth: win?.screen?.width ?? 0,\n\t\tscreenHeight: win?.screen?.height ?? 0,\n\t\tdevicePixelRatio: win?.devicePixelRatio ?? 1,\n\t\ttimezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone ?? \"\",\n\t\ttimezoneOffset: new Date().getTimezoneOffset(),\n\t\tcookiesEnabled: nav?.cookieEnabled ?? false,\n\t\tdoNotTrack: nav?.doNotTrack ?? null,\n\t\tonline: nav?.onLine ?? true,\n\t\tconnectionType: nav?.connection?.effectiveType,\n\t\tdeviceMemory: nav?.deviceMemory,\n\t\thardwareConcurrency: nav?.hardwareConcurrency,\n\t\ttouchSupport: nav ? \"ontouchstart\" in window : false,\n\t\tmaxTouchPoints: nav?.maxTouchPoints ?? 0,\n\t\tvendor: nav?.vendor ?? \"\",\n\t\tappVersion: nav?.appVersion ?? \"\",\n\t\tcolorDepth: win?.screen?.colorDepth ?? 0,\n\t\tpixelDepth: win?.screen?.pixelDepth ?? 0,\n\t};\n}\n\nexport async function fetchIpAddress(): Promise<string | undefined> {\n\ttry {\n\t\tconst response = await fetch(\"https://api.ipify.org?format=json\");\n\t\tconst data = (await response.json()) as { ip: string };\n\t\treturn data.ip;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport async function getDeviceDetails(seonSession?: string): Promise<DeviceDetails> {\n\tconst basic = getBasicDeviceDetails();\n\tconst ip = await fetchIpAddress();\n\treturn { ...basic, ip, seonSession };\n}\n","import { SdkError } from \"../validation\";\n\nexport type FraudEngineErrorCode =\n\t| \"API_ERROR\"\n\t| \"ENCRYPTION_ERROR\"\n\t| \"SIGNING_ERROR\"\n\t| \"VALIDATION_ERROR\"\n\t| \"NETWORK_ERROR\"\n\t| \"PLACE_ORDER_ERROR\";\n\nexport class FraudEngineError extends SdkError<FraudEngineErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: FraudEngineErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"FraudEngineError\";\n\t}\n}\n","import { bytesToBase64, hexToBytes } from \"../lib/encoding\";\nimport { FraudEngineError } from \"./errors\";\n\nexport async function getEncryptionKey(encryptionKeyHex: string): Promise<CryptoKey> {\n\tconst keyBytes = hexToBytes(encryptionKeyHex);\n\treturn crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tkeyBytes.buffer as ArrayBuffer,\n\t\t{ name: \"AES-GCM\" },\n\t\tfalse,\n\t\t[\"encrypt\"],\n\t);\n}\n\nexport async function encryptPayload(\n\tpayload: string,\n\taad: string,\n\tencryptionKeyHex: string,\n): Promise<string> {\n\ttry {\n\t\tconst key = await getEncryptionKey(encryptionKeyHex);\n\t\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\t\tconst encoded = new TextEncoder().encode(payload);\n\t\tconst aadEncoded = new TextEncoder().encode(aad);\n\n\t\tconst ciphertext = await crypto.subtle.encrypt(\n\t\t\t{ name: \"AES-GCM\", iv, additionalData: aadEncoded, tagLength: 128 },\n\t\t\tkey,\n\t\t\tencoded,\n\t\t);\n\n\t\t// IV (12 bytes) + ciphertext + auth tag (16 bytes appended by Web Crypto)\n\t\tconst result = new Uint8Array(iv.length + ciphertext.byteLength);\n\t\tresult.set(iv, 0);\n\t\tresult.set(new Uint8Array(ciphertext), iv.length);\n\n\t\treturn bytesToBase64(result);\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Encryption failed\", {\n\t\t\tcode: \"ENCRYPTION_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import FingerprintJS from \"@fingerprintjs/fingerprintjs\";\n\nlet agent: Awaited<ReturnType<typeof FingerprintJS.load>> | null = null;\nlet cachedResult: { visitorId: string; confidence: number } | null = null;\n\nexport async function loadFingerprintAgent(): Promise<void> {\n\tif (agent) return;\n\tagent = await FingerprintJS.load();\n}\n\nexport async function getFingerprint(\n\ttimeoutMs = 5000,\n): Promise<{ visitorId: string; confidence: number } | null> {\n\tif (cachedResult) return cachedResult;\n\tif (!agent) {\n\t\ttry {\n\t\t\tawait loadFingerprintAgent();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst loadedAgent = agent;\n\tif (!loadedAgent) return null;\n\ttry {\n\t\tconst result = await Promise.race([\n\t\t\tloadedAgent.get(),\n\t\t\tnew Promise<never>((_, reject) =>\n\t\t\t\tsetTimeout(() => reject(new Error(\"FingerprintJS timed out\")), timeoutMs),\n\t\t\t),\n\t\t]);\n\t\tcachedResult = {\n\t\t\tvisitorId: result.visitorId,\n\t\t\tconfidence: result.confidence.score,\n\t\t};\n\t\treturn cachedResult;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import seon from \"@seontechnologies/seon-javascript-sdk\";\n\nlet initialized = false;\n\nexport function initSeon(): void {\n\tif (initialized) return;\n\tseon.init();\n\tinitialized = true;\n}\n\nexport async function getSeonSession(region: string): Promise<string | undefined> {\n\ttry {\n\t\treturn await seon.getSession({\n\t\t\tgeolocation: { canPrompt: false },\n\t\t\tnetworkTimeoutMs: 2000,\n\t\t\tfieldTimeoutMs: 2000,\n\t\t\tregion,\n\t\t\tsilentMode: true,\n\t\t});\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function cleanupSeonStorage(): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst keysToRemove: string[] = [];\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst key = localStorage.key(i);\n\t\tif (key?.startsWith(\"seon_session_sent_\")) keysToRemove.push(key);\n\t}\n\tfor (const key of keysToRemove) localStorage.removeItem(key);\n}\n","import { FraudEngineError } from \"./errors\";\nimport type { FraudEngineSigner } from \"./types\";\n\nexport async function getSignedHeaders(\n\tsigner: FraudEngineSigner,\n\taction: \"activity-log\" | \"link-order\" | \"fingerprint-log\",\n): Promise<Record<string, string>> {\n\t// The EIP-191 signed message is bound to the address of the key that\n\t// actually produces the signature (the admin EOA for AA smart wallets),\n\t// not to the tracked subject address. For plain EOA signers where no\n\t// separate `signerAddress` is provided, this falls back to `signer.address`\n\t// and behaviour is identical to the single-address case.\n\tconst signingAddress = (signer.signerAddress ?? signer.address).toLowerCase();\n\tconst timestamp = Math.floor(Date.now() / 1000).toString();\n\tconst message = `${action}:${signingAddress}:${timestamp}`;\n\n\ttry {\n\t\tconst signature = await signer.signMessage(message);\n\t\treturn {\n\t\t\t\"X-Signer-Address\": signingAddress,\n\t\t\t\"X-Timestamp\": timestamp,\n\t\t\t\"X-Signature\": signature,\n\t\t};\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Failed to sign message\", {\n\t\t\tcode: \"SIGNING_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { z } from \"zod\";\nimport { FraudEngineError } from \"./errors\";\n\n// ── Config schema ──────────────────────────────────────────────────────\n\nexport const ZodFraudEngineConfigSchema = z.object({\n\tapiUrl: z.url(),\n\tencryptionKey: z.string().min(1),\n\tseonRegion: z.string().optional(),\n});\n\n// ── Buy order details schema ───────────────────────────────────────────\n\nexport const ZodBuyOrderDetailsSchema = z.object({\n\tcryptoAmount: z.number(),\n\tfiatAmount: z.number(),\n\tcurrency: z.string().min(1),\n\trecipientAddress: z.string().min(1),\n\tfee: z.number(),\n\tamountAfterFee: z.number(),\n\tpaymentMethod: z.string().optional(),\n\testimatedProcessingTime: z.string().optional(),\n});\n\n// ── User details schema ────────────────────────────────────────────────\n\nexport const ZodUserDetailsSchema = z.object({\n\tcurrency: z.string().optional(),\n\tcountry: z.string().optional(),\n\tlanguage: z.string().optional(),\n\tloginMethod: z.enum([\"email\", \"google\", \"phone\", \"passkey\", \"unknown\"]).optional(),\n\tloginEmail: z.string().optional(),\n\tloginPhone: z.string().optional(),\n});\n\n// ── Link order schema ──────────────────────────────────────────────────\n\nexport const ZodLinkOrderParamsSchema = z.object({\n\tactivityLogId: z.number().int().positive(),\n\torderId: z.string().min(1),\n});\n\n// ── Validate helper ────────────────────────────────────────────────────\n\nexport function validate<S extends z.ZodType>(\n\tschema: S,\n\tdata: unknown,\n): Result<z.infer<S>, FraudEngineError> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(\n\t\tnew FraudEngineError(z.prettifyError(result.error), {\n\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\tcause: result.error,\n\t\t\tcontext: { data },\n\t\t}),\n\t);\n}\n"],"mappings":"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","names":["ResultAsync","err","ok","z","z","validate","ok","err","validate","ResultAsync"]}
1
+ {"version":3,"sources":["../src/fraud-engine/client.ts","../src/lib/encoding.ts","../src/lib/logger.ts","../src/lib/subgraph.ts","../src/validation/errors.validation.ts","../src/validation/schemas.validation.ts","../src/country/currency.ts","../src/fraud-engine/device.ts","../src/fraud-engine/errors.ts","../src/fraud-engine/encryption.ts","../src/fraud-engine/fingerprint.ts","../src/fraud-engine/seon.ts","../src/fraud-engine/signing.ts","../src/fraud-engine/validation.ts"],"sourcesContent":["import { errAsync, ResultAsync } from \"neverthrow\";\nimport { type Logger, noopLogger } from \"../lib\";\nimport { getDeviceDetails } from \"./device\";\nimport { encryptPayload } from \"./encryption\";\nimport { FraudEngineError } from \"./errors\";\nimport { getFingerprint as getFingerprintResult, loadFingerprintAgent } from \"./fingerprint\";\nimport { cleanupSeonStorage as cleanupSeon, getSeonSession, initSeon } from \"./seon\";\nimport { getSignedHeaders } from \"./signing\";\nimport type {\n\tBuyOrderDetails,\n\tFingerprintLogResult,\n\tFraudCheckApiResponse,\n\tFraudCheckResult,\n\tFraudEngine,\n\tFraudEngineConfig,\n\tFraudEngineSigner,\n\tLinkOrderResult,\n\tProcessBuyOrderResult,\n\tUserDetails,\n} from \"./types\";\nimport { validate, ZodFraudEngineConfigSchema } from \"./validation\";\n\nexport function createFraudEngine(config: FraudEngineConfig): FraudEngine {\n\tconst { apiUrl, encryptionKey } = config;\n\tconst seonRegion = config.seonRegion ?? \"asia\";\n\tconst logger: Logger = config.logger ?? noopLogger;\n\n\t// Validate eagerly so misconfiguration surfaces at construction time, but\n\t// never throw — the SDK's contract is that all failures flow through\n\t// `Result`/`ResultAsync`. Capture the error and short-circuit any method\n\t// that depends on the validated fields (`apiUrl`, `encryptionKey`).\n\t// Methods that don't depend on them (init, getFingerprint, getDeviceDetails,\n\t// cleanupSeonStorage) remain usable so consumers can still hydrate device\n\t// fingerprints even with a partially-configured engine.\n\tconst configResult = validate(ZodFraudEngineConfigSchema, {\n\t\tapiUrl,\n\t\tencryptionKey,\n\t\tseonRegion,\n\t});\n\tconst configError = configResult.isErr() ? configResult.error : null;\n\tif (configError) {\n\t\tlogger.error(\n\t\t\t\"Fraud engine config invalid; API methods will return VALIDATION_ERROR until fixed\",\n\t\t\t{ error: configError.message },\n\t\t);\n\t}\n\n\tfunction linkOrderInternal(\n\t\tsigner: FraudEngineSigner,\n\t\tactivityLogId: number,\n\t\torderId: string,\n\t): ResultAsync<LinkOrderResult, FraudEngineError> {\n\t\treturn ResultAsync.fromPromise(\n\t\t\t(async () => {\n\t\t\t\tlogger.info(\"Linking order to activity log\", { activityLogId, orderId });\n\n\t\t\t\tconst signedHeaders = await getSignedHeaders(signer, \"link-order\");\n\n\t\t\t\tconst response = await fetch(`${apiUrl}/activity-logs/link-order`, {\n\t\t\t\t\tmethod: \"PATCH\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\tactivity_log_id: activityLogId,\n\t\t\t\t\t\torder_id: orderId,\n\t\t\t\t\t\tuser_address: signer.address.toLowerCase(),\n\t\t\t\t\t}),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new FraudEngineError(`Link order API returned ${response.status}`, {\n\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst data = (await response.json()) as LinkOrderResult;\n\t\t\t\tlogger.info(\"Order linked successfully\", { orderId });\n\t\t\t\treturn data;\n\t\t\t})(),\n\t\t\t(cause) => {\n\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\treturn new FraudEngineError(\"Link order failed\", {\n\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\tcause,\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t}\n\n\tasync function checkBuyOrderInternal(params: {\n\t\tsigner: FraudEngineSigner;\n\t\torderDetails: BuyOrderDetails;\n\t\tuserDetails?: UserDetails;\n\t\torderSource?: string;\n\t}): Promise<FraudCheckApiResponse> {\n\t\tlogger.info(\"Checking buy order for fraud\", {\n\t\t\tcurrency: params.orderDetails.currency,\n\t\t\tfiatAmount: params.orderDetails.fiatAmount,\n\t\t});\n\n\t\tconst [seonSession, deviceDetails, signedHeaders] = await Promise.all([\n\t\t\tgetSeonSession(seonRegion),\n\t\t\tgetDeviceDetails(),\n\t\t\tgetSignedHeaders(params.signer, \"activity-log\"),\n\t\t]);\n\n\t\tconst device = { ...deviceDetails, seonSession };\n\n\t\tconst userAddress = params.signer.address.toLowerCase();\n\t\tconst timestamp = Date.now();\n\n\t\tconst payload = JSON.stringify({\n\t\t\tuser_details: {\n\t\t\t\tcurrency: params.userDetails?.currency,\n\t\t\t\tcountry: params.userDetails?.country,\n\t\t\t\tlanguage: params.userDetails?.language,\n\t\t\t\tlogin_method: params.userDetails?.loginMethod,\n\t\t\t\tlogin_email: params.userDetails?.loginEmail,\n\t\t\t\tlogin_phone: params.userDetails?.loginPhone,\n\t\t\t},\n\t\t\ttransaction_details: {\n\t\t\t\tcrypto_amount: params.orderDetails.cryptoAmount,\n\t\t\t\tfiat_amount: params.orderDetails.fiatAmount,\n\t\t\t\tcurrency: params.orderDetails.currency,\n\t\t\t\trecipient_address: params.orderDetails.recipientAddress,\n\t\t\t\tfee: params.orderDetails.fee,\n\t\t\t\tamount_after_fee: params.orderDetails.amountAfterFee,\n\t\t\t\tpayment_method: params.orderDetails.paymentMethod,\n\t\t\t\testimated_processing_time: params.orderDetails.estimatedProcessingTime,\n\t\t\t\torder_timestamp: timestamp,\n\t\t\t\torder_source: params.orderSource,\n\t\t\t},\n\t\t\tdevice_details: device,\n\t\t});\n\n\t\t// AAD format must match backend: \"type|user_address|timestamp\"\n\t\tconst aad = `buy_order|${userAddress}|${timestamp}`;\n\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\tconst response = await fetch(`${apiUrl}/activity-logs`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...signedHeaders,\n\t\t\t},\n\t\t\tbody: JSON.stringify({\n\t\t\t\ttype: \"buy_order\",\n\t\t\t\tuser_address: userAddress,\n\t\t\t\ttimestamp,\n\t\t\t\tencrypted_payload: encrypted,\n\t\t\t}),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new FraudEngineError(`Fraud check API returned ${response.status}`, {\n\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\tcontext: { status: response.status },\n\t\t\t});\n\t\t}\n\n\t\tconst data = (await response.json()) as FraudCheckApiResponse;\n\t\tlogger.info(\"Fraud check result\", {\n\t\t\tapproved: data.approved,\n\t\t\tactivityLogId: data.activity_log_id,\n\t\t});\n\t\treturn data;\n\t}\n\n\treturn {\n\t\tasync init() {\n\t\t\tlogger.info(\"Initializing fraud engine\");\n\t\t\ttry {\n\t\t\t\tinitSeon();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"SEON initialization failed\", { cause: String(cause) });\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait loadFingerprintAgent();\n\t\t\t} catch (cause) {\n\t\t\t\tlogger.error(\"FingerprintJS initialization failed\", {\n\t\t\t\t\tcause: String(cause),\n\t\t\t\t});\n\t\t\t}\n\t\t\tlogger.info(\"Fraud engine initialized\");\n\t\t},\n\n\t\tcheckBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t}): ResultAsync<FraudCheckResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\tcheckBuyOrderInternal(params).then((data) => ({\n\t\t\t\t\tapproved: data.approved,\n\t\t\t\t\tactivityLogId: data.activity_log_id,\n\t\t\t\t\tmessage: data.message,\n\t\t\t\t\tlinkOrder: (orderId: string) =>\n\t\t\t\t\t\tlinkOrderInternal(params.signer, data.activity_log_id, orderId),\n\t\t\t\t})),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fraud check failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tprocessBuyOrder(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t\torderDetails: BuyOrderDetails;\n\t\t\tuserDetails?: UserDetails;\n\t\t\torderSource?: string;\n\t\t\tplaceOrder: () => Promise<string>;\n\t\t}): ResultAsync<ProcessBuyOrderResult, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlet activityLogId: number | null = null;\n\n\t\t\t\t\t// Step 1: Fraud check (fail-open)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst fraudCheck = await checkBuyOrderInternal(params);\n\t\t\t\t\t\tif (!fraudCheck.approved) {\n\t\t\t\t\t\t\treturn { status: \"rejected\" as const, message: fraudCheck.message };\n\t\t\t\t\t\t}\n\t\t\t\t\t\tactivityLogId = fraudCheck.activity_log_id;\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tlogger.error(\"Fraud check failed, proceeding with order (fail-open)\", {\n\t\t\t\t\t\t\terror: String(cause),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 2: Place order (consumer callback)\n\t\t\t\t\tlet orderId: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\torderId = await params.placeOrder();\n\t\t\t\t\t} catch (cause) {\n\t\t\t\t\t\tthrow new FraudEngineError(\"Place order callback failed\", {\n\t\t\t\t\t\t\tcode: \"PLACE_ORDER_ERROR\",\n\t\t\t\t\t\t\tcause,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// Step 3: Auto-link (fire-and-forget, only if fraud check succeeded)\n\t\t\t\t\tif (activityLogId !== null) {\n\t\t\t\t\t\tlinkOrderInternal(params.signer, activityLogId, orderId).mapErr((e) => {\n\t\t\t\t\t\t\tlogger.error(\"Failed to link order to activity log\", {\n\t\t\t\t\t\t\t\torderId,\n\t\t\t\t\t\t\t\tactivityLogId: activityLogId as number,\n\t\t\t\t\t\t\t\terror: e.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { status: \"placed\" as const, orderId };\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Process buy order failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tlogFingerprint(params: {\n\t\t\tsigner: FraudEngineSigner;\n\t\t}): ResultAsync<FingerprintLogResult | null, FraudEngineError> {\n\t\t\tif (configError) return errAsync(configError);\n\t\t\treturn ResultAsync.fromPromise(\n\t\t\t\t(async () => {\n\t\t\t\t\tlogger.info(\"Logging fingerprint\");\n\n\t\t\t\t\tconst fingerprintResult = await getFingerprintResult(5000);\n\t\t\t\t\tif (!fingerprintResult) {\n\t\t\t\t\t\tlogger.warn(\"Fingerprint not available, skipping\");\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst signedHeaders = await getSignedHeaders(params.signer, \"fingerprint-log\");\n\n\t\t\t\t\tconst normalizedAddress = params.signer.address.toLowerCase();\n\t\t\t\t\tconst timestamp = Date.now();\n\n\t\t\t\t\tconst payload = JSON.stringify({\n\t\t\t\t\t\tfingerprint_id: fingerprintResult.visitorId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// AAD format: \"fingerprint|user_address|timestamp\"\n\t\t\t\t\tconst aad = `fingerprint|${normalizedAddress}|${timestamp}`;\n\t\t\t\t\tconst encrypted = await encryptPayload(payload, aad, encryptionKey);\n\n\t\t\t\t\tconst response = await fetch(`${apiUrl}/fingerprint-log`, {\n\t\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\t...signedHeaders,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\t\t\tuser_address: normalizedAddress,\n\t\t\t\t\t\t\ttimestamp,\n\t\t\t\t\t\t\tencrypted_payload: encrypted,\n\t\t\t\t\t\t}),\n\t\t\t\t\t});\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\tthrow new FraudEngineError(`Fingerprint log API returned ${response.status}`, {\n\t\t\t\t\t\t\tcode: \"API_ERROR\",\n\t\t\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tconst data = (await response.json()) as FingerprintLogResult;\n\t\t\t\t\tlogger.info(\"Fingerprint logged successfully\");\n\t\t\t\t\treturn data;\n\t\t\t\t})(),\n\t\t\t\t(cause) => {\n\t\t\t\t\tif (cause instanceof FraudEngineError) return cause;\n\t\t\t\t\treturn new FraudEngineError(\"Fingerprint log failed\", {\n\t\t\t\t\t\tcode: \"NETWORK_ERROR\",\n\t\t\t\t\t\tcause,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\n\t\tasync getFingerprint() {\n\t\t\treturn getFingerprintResult(5000);\n\t\t},\n\n\t\tasync getDeviceDetails() {\n\t\t\treturn getDeviceDetails();\n\t\t},\n\n\t\tcleanupSeonStorage() {\n\t\t\tcleanupSeon();\n\t\t},\n\t};\n}\n","/** Converts a hex string to a Uint8Array. */\nexport function hexToBytes(hex: string): Uint8Array {\n\tconst bytes = new Uint8Array(hex.length / 2);\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tbytes[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);\n\t}\n\treturn bytes;\n}\n\n/** Converts a Uint8Array to a base64 string. */\nexport function bytesToBase64(bytes: Uint8Array): string {\n\tlet binary = \"\";\n\tfor (const byte of bytes) {\n\t\tbinary += String.fromCharCode(byte);\n\t}\n\treturn btoa(binary);\n}\n","export interface Logger {\n\tdebug(message: string, data?: Record<string, unknown>): void;\n\tinfo(message: string, data?: Record<string, unknown>): void;\n\twarn(message: string, data?: Record<string, unknown>): void;\n\terror(message: string, data?: Record<string, unknown>): void;\n}\n\nconst noop = () => {};\n\nexport const noopLogger: Logger = {\n\tdebug: noop,\n\tinfo: noop,\n\twarn: noop,\n\terror: noop,\n};\n","import { ResultAsync } from \"neverthrow\";\nimport { SdkError } from \"../validation\";\nimport { sleep } from \"./sleep\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RETRIES = 3;\nconst BACKOFF_MS = 500;\n\nexport type SubgraphErrorCode = \"HTTP_ERROR\" | \"GRAPHQL_ERROR\" | \"NO_DATA\" | \"TRANSPORT_ERROR\";\n\n/** Transport-level error from the shared subgraph client. Consumers remap to their own domain error. */\nexport class SubgraphError extends SdkError<SubgraphErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: SubgraphErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"SubgraphError\";\n\t}\n}\n\nexport interface SubgraphQueryParams {\n\treadonly query: string;\n\treadonly variables?: Record<string, unknown>;\n\treadonly timeoutMs?: number;\n}\n\nfunction isTransient(error: unknown): boolean {\n\tif (error instanceof SubgraphError) {\n\t\tif (error.code !== \"HTTP_ERROR\") return false;\n\t\tconst status = (error.context as { status?: number } | undefined)?.status;\n\t\treturn typeof status === \"number\" && status >= 500;\n\t}\n\tif (error instanceof DOMException && error.name === \"AbortError\") return true;\n\tif (error instanceof TypeError) return true;\n\treturn false;\n}\n\n/**\n * POSTs a GraphQL query to `url` with retry + timeout. Returns the `data` field on\n * success. All transport and GraphQL-level failures surface as `SubgraphError`.\n */\nexport function querySubgraph(\n\turl: string,\n\tparams: SubgraphQueryParams,\n): ResultAsync<unknown, SubgraphError> {\n\tconst timeoutMs = params.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n\tconst fetchOnce = async () => {\n\t\tconst controller = new AbortController();\n\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tquery: params.query,\n\t\t\t\t\tvariables: params.variables,\n\t\t\t\t}),\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new SubgraphError(`Subgraph request failed (status: ${response.status})`, {\n\t\t\t\t\tcode: \"HTTP_ERROR\",\n\t\t\t\t\tcause: response,\n\t\t\t\t\tcontext: { status: response.status },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst json = await response.json();\n\n\t\t\tif (json.errors?.length > 0) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned GraphQL errors\", {\n\t\t\t\t\tcode: \"GRAPHQL_ERROR\",\n\t\t\t\t\tcause: json.errors,\n\t\t\t\t\tcontext: { errors: json.errors },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (!json.data) {\n\t\t\t\tthrow new SubgraphError(\"Subgraph returned no data\", {\n\t\t\t\t\tcode: \"NO_DATA\",\n\t\t\t\t\tcause: \"Missing data field in GraphQL response\",\n\t\t\t\t\tcontext: { response: json },\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn json.data;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t};\n\n\tconst fetchWithRetry = async () => {\n\t\tfor (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n\t\t\ttry {\n\t\t\t\treturn await fetchOnce();\n\t\t\t} catch (error) {\n\t\t\t\tconst lastAttempt = attempt === MAX_RETRIES;\n\t\t\t\tif (lastAttempt || !isTransient(error)) throw error;\n\t\t\t\tawait sleep(BACKOFF_MS * (attempt + 1));\n\t\t\t}\n\t\t}\n\t\tthrow new SubgraphError(\"Subgraph query exhausted retries\", { code: \"TRANSPORT_ERROR\" });\n\t};\n\n\treturn ResultAsync.fromPromise(fetchWithRetry(), (error) =>\n\t\terror instanceof SubgraphError\n\t\t\t? error\n\t\t\t: new SubgraphError(\"Subgraph query failed\", {\n\t\t\t\t\tcode: \"TRANSPORT_ERROR\",\n\t\t\t\t\tcause: error,\n\t\t\t\t}),\n\t);\n}\n","export class SdkError<TCode extends string = string> extends Error {\n\treadonly code: TCode;\n\treadonly cause?: unknown;\n\treadonly context?: Record<string, unknown>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: TCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SdkError\";\n\t\tthis.code = options.code;\n\t\tthis.cause = options.cause;\n\t\tthis.context = options.context;\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { isAddress } from \"viem\";\nimport { z } from \"zod\";\nimport { CURRENCY_CODES } from \"../country/currency\";\n\nexport const ZodAddressSchema = z\n\t.string()\n\t.refine((s) => isAddress(s), { message: \"Invalid Ethereum address\" });\n\nexport const ZodCurrencySchema = z.enum(CURRENCY_CODES);\n\nexport function validate<S extends z.ZodType, E>(\n\tschema: S,\n\tdata: unknown,\n\ttoError: (message: string, cause: unknown, data: unknown) => E,\n): Result<z.infer<S>, E> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(toError(z.prettifyError(result.error), result.error, data));\n}\n","/**\n * All supported currency symbols. Single source of truth for the SDK.\n *\n * Lives alongside the country metadata so that adding a currency is a\n * single-folder operation: drop a new file in `currencies/<code>.ts`, add it\n * to this map, and both `COUNTRY_OPTIONS` and `ZodCurrencySchema` pick it up.\n */\nexport const CURRENCY = {\n\tIDR: \"IDR\",\n\tINR: \"INR\",\n\tBRL: \"BRL\",\n\tARS: \"ARS\",\n\tMEX: \"MEX\",\n\tVEN: \"VEN\",\n\tEUR: \"EUR\",\n\tNGN: \"NGN\",\n\tUSD: \"USD\",\n\tCOP: \"COP\",\n\tECU: \"ECU\",\n\tPEN: \"PEN\",\n} as const;\n\n/** Union of supported currency codes. */\nexport type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];\n\n/**\n * Tuple form of the currency codes — used by `z.enum(...)` in the shared\n * validation layer. Narrow tuple type required by Zod.\n */\nexport const CURRENCY_CODES = Object.values(CURRENCY) as [CurrencyCode, ...CurrencyCode[]];\n","import type { DeviceDetails } from \"./types\";\n\ninterface NavigatorConnection {\n\treadonly effectiveType?: string;\n}\n\ninterface NavigatorExtended extends Navigator {\n\treadonly connection?: NavigatorConnection;\n\treadonly deviceMemory?: number;\n}\n\nexport function getBasicDeviceDetails(): Omit<DeviceDetails, \"ip\" | \"seonSession\"> {\n\tconst nav = typeof navigator !== \"undefined\" ? (navigator as NavigatorExtended) : undefined;\n\tconst win = typeof window !== \"undefined\" ? window : undefined;\n\n\treturn {\n\t\tuserAgent: nav?.userAgent ?? \"\",\n\t\tplatform: nav?.platform ?? \"\",\n\t\tlanguage: nav?.language ?? \"\",\n\t\tlanguages: nav ? Array.from(nav.languages) : [],\n\t\tscreenWidth: win?.screen?.width ?? 0,\n\t\tscreenHeight: win?.screen?.height ?? 0,\n\t\tdevicePixelRatio: win?.devicePixelRatio ?? 1,\n\t\ttimezone: Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone ?? \"\",\n\t\ttimezoneOffset: new Date().getTimezoneOffset(),\n\t\tcookiesEnabled: nav?.cookieEnabled ?? false,\n\t\tdoNotTrack: nav?.doNotTrack ?? null,\n\t\tonline: nav?.onLine ?? true,\n\t\tconnectionType: nav?.connection?.effectiveType,\n\t\tdeviceMemory: nav?.deviceMemory,\n\t\thardwareConcurrency: nav?.hardwareConcurrency,\n\t\ttouchSupport: nav ? \"ontouchstart\" in window : false,\n\t\tmaxTouchPoints: nav?.maxTouchPoints ?? 0,\n\t\tvendor: nav?.vendor ?? \"\",\n\t\tappVersion: nav?.appVersion ?? \"\",\n\t\tcolorDepth: win?.screen?.colorDepth ?? 0,\n\t\tpixelDepth: win?.screen?.pixelDepth ?? 0,\n\t};\n}\n\nexport async function fetchIpAddress(): Promise<string | undefined> {\n\ttry {\n\t\tconst response = await fetch(\"https://api.ipify.org?format=json\");\n\t\tconst data = (await response.json()) as { ip: string };\n\t\treturn data.ip;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport async function getDeviceDetails(seonSession?: string): Promise<DeviceDetails> {\n\tconst basic = getBasicDeviceDetails();\n\tconst ip = await fetchIpAddress();\n\treturn { ...basic, ip, seonSession };\n}\n","import { SdkError } from \"../validation\";\n\nexport type FraudEngineErrorCode =\n\t| \"API_ERROR\"\n\t| \"ENCRYPTION_ERROR\"\n\t| \"SIGNING_ERROR\"\n\t| \"VALIDATION_ERROR\"\n\t| \"NETWORK_ERROR\"\n\t| \"PLACE_ORDER_ERROR\";\n\nexport class FraudEngineError extends SdkError<FraudEngineErrorCode> {\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: FraudEngineErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message, options);\n\t\tthis.name = \"FraudEngineError\";\n\t}\n}\n","import { bytesToBase64, hexToBytes } from \"../lib/encoding\";\nimport { FraudEngineError } from \"./errors\";\n\nexport async function getEncryptionKey(encryptionKeyHex: string): Promise<CryptoKey> {\n\tconst keyBytes = hexToBytes(encryptionKeyHex);\n\treturn crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tkeyBytes.buffer as ArrayBuffer,\n\t\t{ name: \"AES-GCM\" },\n\t\tfalse,\n\t\t[\"encrypt\"],\n\t);\n}\n\nexport async function encryptPayload(\n\tpayload: string,\n\taad: string,\n\tencryptionKeyHex: string,\n): Promise<string> {\n\ttry {\n\t\tconst key = await getEncryptionKey(encryptionKeyHex);\n\t\tconst iv = crypto.getRandomValues(new Uint8Array(12));\n\t\tconst encoded = new TextEncoder().encode(payload);\n\t\tconst aadEncoded = new TextEncoder().encode(aad);\n\n\t\tconst ciphertext = await crypto.subtle.encrypt(\n\t\t\t{ name: \"AES-GCM\", iv, additionalData: aadEncoded, tagLength: 128 },\n\t\t\tkey,\n\t\t\tencoded,\n\t\t);\n\n\t\t// IV (12 bytes) + ciphertext + auth tag (16 bytes appended by Web Crypto)\n\t\tconst result = new Uint8Array(iv.length + ciphertext.byteLength);\n\t\tresult.set(iv, 0);\n\t\tresult.set(new Uint8Array(ciphertext), iv.length);\n\n\t\treturn bytesToBase64(result);\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Encryption failed\", {\n\t\t\tcode: \"ENCRYPTION_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import FingerprintJS from \"@fingerprintjs/fingerprintjs\";\n\nlet agent: Awaited<ReturnType<typeof FingerprintJS.load>> | null = null;\nlet cachedResult: { visitorId: string; confidence: number } | null = null;\n\nexport async function loadFingerprintAgent(): Promise<void> {\n\tif (agent) return;\n\tagent = await FingerprintJS.load();\n}\n\nexport async function getFingerprint(\n\ttimeoutMs = 5000,\n): Promise<{ visitorId: string; confidence: number } | null> {\n\tif (cachedResult) return cachedResult;\n\tif (!agent) {\n\t\ttry {\n\t\t\tawait loadFingerprintAgent();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst loadedAgent = agent;\n\tif (!loadedAgent) return null;\n\ttry {\n\t\tconst result = await Promise.race([\n\t\t\tloadedAgent.get(),\n\t\t\tnew Promise<never>((_, reject) =>\n\t\t\t\tsetTimeout(() => reject(new Error(\"FingerprintJS timed out\")), timeoutMs),\n\t\t\t),\n\t\t]);\n\t\tcachedResult = {\n\t\t\tvisitorId: result.visitorId,\n\t\t\tconfidence: result.confidence.score,\n\t\t};\n\t\treturn cachedResult;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import seon from \"@seontechnologies/seon-javascript-sdk\";\n\nlet initialized = false;\n\nexport function initSeon(): void {\n\tif (initialized) return;\n\tseon.init();\n\tinitialized = true;\n}\n\nexport async function getSeonSession(region: string): Promise<string | undefined> {\n\ttry {\n\t\treturn await seon.getSession({\n\t\t\tgeolocation: { canPrompt: false },\n\t\t\tnetworkTimeoutMs: 2000,\n\t\t\tfieldTimeoutMs: 2000,\n\t\t\tregion,\n\t\t\tsilentMode: true,\n\t\t});\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function cleanupSeonStorage(): void {\n\tif (typeof localStorage === \"undefined\") return;\n\tconst keysToRemove: string[] = [];\n\tfor (let i = 0; i < localStorage.length; i++) {\n\t\tconst key = localStorage.key(i);\n\t\tif (key?.startsWith(\"seon_session_sent_\")) keysToRemove.push(key);\n\t}\n\tfor (const key of keysToRemove) localStorage.removeItem(key);\n}\n","import { FraudEngineError } from \"./errors\";\nimport type { FraudEngineSigner } from \"./types\";\n\nexport async function getSignedHeaders(\n\tsigner: FraudEngineSigner,\n\taction: \"activity-log\" | \"link-order\" | \"fingerprint-log\",\n): Promise<Record<string, string>> {\n\t// The signed message binds BOTH addresses: the key that produces the\n\t// signature (the admin EOA for AA smart wallets) and the subject the\n\t// request acts for (the smart account). Binding the subject stops a\n\t// signature captured for one account being replayed against another\n\t// inside the backend's freshness window.\n\t//\n\t// This is not by itself an authorisation control — the backend decides\n\t// entitlement by checking on-chain that the signer is an admin of the\n\t// subject account. See fraud-engine `core/wallet_auth.py`.\n\t//\n\t// For plain EOA signers where no separate `signerAddress` is provided,\n\t// both values collapse to `signer.address`.\n\tconst signingAddress = (signer.signerAddress ?? signer.address).toLowerCase();\n\tconst subjectAddress = signer.address.toLowerCase();\n\tconst timestamp = Math.floor(Date.now() / 1000).toString();\n\tconst message = `${action}:${signingAddress}:${subjectAddress}:${timestamp}`;\n\n\ttry {\n\t\tconst signature = await signer.signMessage(message);\n\t\treturn {\n\t\t\t\"X-Signer-Address\": signingAddress,\n\t\t\t\"X-Timestamp\": timestamp,\n\t\t\t\"X-Signature\": signature,\n\t\t};\n\t} catch (cause) {\n\t\tthrow new FraudEngineError(\"Failed to sign message\", {\n\t\t\tcode: \"SIGNING_ERROR\",\n\t\t\tcause,\n\t\t});\n\t}\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { z } from \"zod\";\nimport { FraudEngineError } from \"./errors\";\n\n// ── Config schema ──────────────────────────────────────────────────────\n\nexport const ZodFraudEngineConfigSchema = z.object({\n\tapiUrl: z.url(),\n\tencryptionKey: z.string().min(1),\n\tseonRegion: z.string().optional(),\n});\n\n// ── Buy order details schema ───────────────────────────────────────────\n\nexport const ZodBuyOrderDetailsSchema = z.object({\n\tcryptoAmount: z.number(),\n\tfiatAmount: z.number(),\n\tcurrency: z.string().min(1),\n\trecipientAddress: z.string().min(1),\n\tfee: z.number(),\n\tamountAfterFee: z.number(),\n\tpaymentMethod: z.string().optional(),\n\testimatedProcessingTime: z.string().optional(),\n});\n\n// ── User details schema ────────────────────────────────────────────────\n\nexport const ZodUserDetailsSchema = z.object({\n\tcurrency: z.string().optional(),\n\tcountry: z.string().optional(),\n\tlanguage: z.string().optional(),\n\tloginMethod: z.enum([\"email\", \"google\", \"phone\", \"passkey\", \"unknown\"]).optional(),\n\tloginEmail: z.string().optional(),\n\tloginPhone: z.string().optional(),\n});\n\n// ── Link order schema ──────────────────────────────────────────────────\n\nexport const ZodLinkOrderParamsSchema = z.object({\n\tactivityLogId: z.number().int().positive(),\n\torderId: z.string().min(1),\n});\n\n// ── Validate helper ────────────────────────────────────────────────────\n\nexport function validate<S extends z.ZodType>(\n\tschema: S,\n\tdata: unknown,\n): Result<z.infer<S>, FraudEngineError> {\n\tconst result = schema.safeParse(data);\n\tif (result.success) {\n\t\treturn ok(result.data as z.infer<S>);\n\t}\n\treturn err(\n\t\tnew FraudEngineError(z.prettifyError(result.error), {\n\t\t\tcode: \"VALIDATION_ERROR\",\n\t\t\tcause: result.error,\n\t\t\tcontext: { data },\n\t\t}),\n\t);\n}\n"],"mappings":";AAAA,SAAS,UAAU,eAAAA,oBAAmB;;;ACC/B,SAAS,WAAW,KAAyB;AACnD,QAAM,QAAQ,IAAI,WAAW,IAAI,SAAS,CAAC;AAC3C,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;AACvC,UAAM,IAAI,CAAC,IAAI,OAAO,SAAS,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE;AAAA,EACvD;AACA,SAAO;AACR;AAGO,SAAS,cAAc,OAA2B;AACxD,MAAI,SAAS;AACb,aAAW,QAAQ,OAAO;AACzB,cAAU,OAAO,aAAa,IAAI;AAAA,EACnC;AACA,SAAO,KAAK,MAAM;AACnB;;;ACTA,IAAM,OAAO,MAAM;AAAC;AAEb,IAAM,aAAqB;AAAA,EACjC,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACR;;;ACdA,SAAS,mBAAmB;;;ACArB,IAAM,WAAN,cAAsD,MAAM;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EAET,YACC,SACA,SAKC;AACD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO,QAAQ;AACpB,SAAK,QAAQ,QAAQ;AACrB,SAAK,UAAU,QAAQ;AAAA,EACxB;AACD;;;ACnBA,SAAS,KAAK,UAAuB;AACrC,SAAS,iBAAiB;AAC1B,SAAS,SAAS;;;ACKX,IAAM,WAAW;AAAA,EACvB,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,KAAK;AACN;AASO,IAAM,iBAAiB,OAAO,OAAO,QAAQ;;;ADxB7C,IAAM,mBAAmB,EAC9B,OAAO,EACP,OAAO,CAAC,MAAM,UAAU,CAAC,GAAG,EAAE,SAAS,2BAA2B,CAAC;AAE9D,IAAM,oBAAoB,EAAE,KAAK,cAAc;;;AEE/C,SAAS,wBAAmE;AAClF,QAAM,MAAM,OAAO,cAAc,cAAe,YAAkC;AAClF,QAAM,MAAM,OAAO,WAAW,cAAc,SAAS;AAErD,SAAO;AAAA,IACN,WAAW,KAAK,aAAa;AAAA,IAC7B,UAAU,KAAK,YAAY;AAAA,IAC3B,UAAU,KAAK,YAAY;AAAA,IAC3B,WAAW,MAAM,MAAM,KAAK,IAAI,SAAS,IAAI,CAAC;AAAA,IAC9C,aAAa,KAAK,QAAQ,SAAS;AAAA,IACnC,cAAc,KAAK,QAAQ,UAAU;AAAA,IACrC,kBAAkB,KAAK,oBAAoB;AAAA,IAC3C,UAAU,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,YAAY;AAAA,IACrE,iBAAgB,oBAAI,KAAK,GAAE,kBAAkB;AAAA,IAC7C,gBAAgB,KAAK,iBAAiB;AAAA,IACtC,YAAY,KAAK,cAAc;AAAA,IAC/B,QAAQ,KAAK,UAAU;AAAA,IACvB,gBAAgB,KAAK,YAAY;AAAA,IACjC,cAAc,KAAK;AAAA,IACnB,qBAAqB,KAAK;AAAA,IAC1B,cAAc,MAAM,kBAAkB,SAAS;AAAA,IAC/C,gBAAgB,KAAK,kBAAkB;AAAA,IACvC,QAAQ,KAAK,UAAU;AAAA,IACvB,YAAY,KAAK,cAAc;AAAA,IAC/B,YAAY,KAAK,QAAQ,cAAc;AAAA,IACvC,YAAY,KAAK,QAAQ,cAAc;AAAA,EACxC;AACD;AAEA,eAAsB,iBAA8C;AACnE,MAAI;AACH,UAAM,WAAW,MAAM,MAAM,mCAAmC;AAChE,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK;AAAA,EACb,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,eAAsB,iBAAiB,aAA8C;AACpF,QAAM,QAAQ,sBAAsB;AACpC,QAAM,KAAK,MAAM,eAAe;AAChC,SAAO,EAAE,GAAG,OAAO,IAAI,YAAY;AACpC;;;AC5CO,IAAM,mBAAN,cAA+B,SAA+B;AAAA,EACpE,YACC,SACA,SAKC;AACD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACb;AACD;;;ACnBA,eAAsB,iBAAiB,kBAA8C;AACpF,QAAM,WAAW,WAAW,gBAAgB;AAC5C,SAAO,OAAO,OAAO;AAAA,IACpB;AAAA,IACA,SAAS;AAAA,IACT,EAAE,MAAM,UAAU;AAAA,IAClB;AAAA,IACA,CAAC,SAAS;AAAA,EACX;AACD;AAEA,eAAsB,eACrB,SACA,KACA,kBACkB;AAClB,MAAI;AACH,UAAM,MAAM,MAAM,iBAAiB,gBAAgB;AACnD,UAAM,KAAK,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AACpD,UAAM,UAAU,IAAI,YAAY,EAAE,OAAO,OAAO;AAChD,UAAM,aAAa,IAAI,YAAY,EAAE,OAAO,GAAG;AAE/C,UAAM,aAAa,MAAM,OAAO,OAAO;AAAA,MACtC,EAAE,MAAM,WAAW,IAAI,gBAAgB,YAAY,WAAW,IAAI;AAAA,MAClE;AAAA,MACA;AAAA,IACD;AAGA,UAAM,SAAS,IAAI,WAAW,GAAG,SAAS,WAAW,UAAU;AAC/D,WAAO,IAAI,IAAI,CAAC;AAChB,WAAO,IAAI,IAAI,WAAW,UAAU,GAAG,GAAG,MAAM;AAEhD,WAAO,cAAc,MAAM;AAAA,EAC5B,SAAS,OAAO;AACf,UAAM,IAAI,iBAAiB,qBAAqB;AAAA,MAC/C,MAAM;AAAA,MACN;AAAA,IACD,CAAC;AAAA,EACF;AACD;;;AC3CA,OAAO,mBAAmB;AAE1B,IAAI,QAA+D;AACnE,IAAI,eAAiE;AAErE,eAAsB,uBAAsC;AAC3D,MAAI,MAAO;AACX,UAAQ,MAAM,cAAc,KAAK;AAClC;AAEA,eAAsB,eACrB,YAAY,KACgD;AAC5D,MAAI,aAAc,QAAO;AACzB,MAAI,CAAC,OAAO;AACX,QAAI;AACH,YAAM,qBAAqB;AAAA,IAC5B,QAAQ;AACP,aAAO;AAAA,IACR;AAAA,EACD;AACA,QAAM,cAAc;AACpB,MAAI,CAAC,YAAa,QAAO;AACzB,MAAI;AACH,UAAM,SAAS,MAAM,QAAQ,KAAK;AAAA,MACjC,YAAY,IAAI;AAAA,MAChB,IAAI;AAAA,QAAe,CAAC,GAAG,WACtB,WAAW,MAAM,OAAO,IAAI,MAAM,yBAAyB,CAAC,GAAG,SAAS;AAAA,MACzE;AAAA,IACD,CAAC;AACD,mBAAe;AAAA,MACd,WAAW,OAAO;AAAA,MAClB,YAAY,OAAO,WAAW;AAAA,IAC/B;AACA,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;;;ACtCA,OAAO,UAAU;AAEjB,IAAI,cAAc;AAEX,SAAS,WAAiB;AAChC,MAAI,YAAa;AACjB,OAAK,KAAK;AACV,gBAAc;AACf;AAEA,eAAsB,eAAe,QAA6C;AACjF,MAAI;AACH,WAAO,MAAM,KAAK,WAAW;AAAA,MAC5B,aAAa,EAAE,WAAW,MAAM;AAAA,MAChC,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB;AAAA,MACA,YAAY;AAAA,IACb,CAAC;AAAA,EACF,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEO,SAAS,qBAA2B;AAC1C,MAAI,OAAO,iBAAiB,YAAa;AACzC,QAAM,eAAyB,CAAC;AAChC,WAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK;AAC7C,UAAM,MAAM,aAAa,IAAI,CAAC;AAC9B,QAAI,KAAK,WAAW,oBAAoB,EAAG,cAAa,KAAK,GAAG;AAAA,EACjE;AACA,aAAW,OAAO,aAAc,cAAa,WAAW,GAAG;AAC5D;;;AC7BA,eAAsB,iBACrB,QACA,QACkC;AAalC,QAAM,kBAAkB,OAAO,iBAAiB,OAAO,SAAS,YAAY;AAC5E,QAAM,iBAAiB,OAAO,QAAQ,YAAY;AAClD,QAAM,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,EAAE,SAAS;AACzD,QAAM,UAAU,GAAG,MAAM,IAAI,cAAc,IAAI,cAAc,IAAI,SAAS;AAE1E,MAAI;AACH,UAAM,YAAY,MAAM,OAAO,YAAY,OAAO;AAClD,WAAO;AAAA,MACN,oBAAoB;AAAA,MACpB,eAAe;AAAA,MACf,eAAe;AAAA,IAChB;AAAA,EACD,SAAS,OAAO;AACf,UAAM,IAAI,iBAAiB,0BAA0B;AAAA,MACpD,MAAM;AAAA,MACN;AAAA,IACD,CAAC;AAAA,EACF;AACD;;;ACrCA,SAAS,OAAAC,MAAK,MAAAC,WAAuB;AACrC,SAAS,KAAAC,UAAS;AAKX,IAAM,6BAA6BC,GAAE,OAAO;AAAA,EAClD,QAAQA,GAAE,IAAI;AAAA,EACd,eAAeA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,YAAYA,GAAE,OAAO,EAAE,SAAS;AACjC,CAAC;AAIM,IAAM,2BAA2BA,GAAE,OAAO;AAAA,EAChD,cAAcA,GAAE,OAAO;AAAA,EACvB,YAAYA,GAAE,OAAO;AAAA,EACrB,UAAUA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC1B,kBAAkBA,GAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAClC,KAAKA,GAAE,OAAO;AAAA,EACd,gBAAgBA,GAAE,OAAO;AAAA,EACzB,eAAeA,GAAE,OAAO,EAAE,SAAS;AAAA,EACnC,yBAAyBA,GAAE,OAAO,EAAE,SAAS;AAC9C,CAAC;AAIM,IAAM,uBAAuBA,GAAE,OAAO;AAAA,EAC5C,UAAUA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,SAASA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,UAAUA,GAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,aAAaA,GAAE,KAAK,CAAC,SAAS,UAAU,SAAS,WAAW,SAAS,CAAC,EAAE,SAAS;AAAA,EACjF,YAAYA,GAAE,OAAO,EAAE,SAAS;AAAA,EAChC,YAAYA,GAAE,OAAO,EAAE,SAAS;AACjC,CAAC;AAIM,IAAM,2BAA2BA,GAAE,OAAO;AAAA,EAChD,eAAeA,GAAE,OAAO,EAAE,IAAI,EAAE,SAAS;AAAA,EACzC,SAASA,GAAE,OAAO,EAAE,IAAI,CAAC;AAC1B,CAAC;AAIM,SAASC,UACf,QACA,MACuC;AACvC,QAAM,SAAS,OAAO,UAAU,IAAI;AACpC,MAAI,OAAO,SAAS;AACnB,WAAOC,IAAG,OAAO,IAAkB;AAAA,EACpC;AACA,SAAOC;AAAA,IACN,IAAI,iBAAiBH,GAAE,cAAc,OAAO,KAAK,GAAG;AAAA,MACnD,MAAM;AAAA,MACN,OAAO,OAAO;AAAA,MACd,SAAS,EAAE,KAAK;AAAA,IACjB,CAAC;AAAA,EACF;AACD;;;AbtCO,SAAS,kBAAkB,QAAwC;AACzE,QAAM,EAAE,QAAQ,cAAc,IAAI;AAClC,QAAM,aAAa,OAAO,cAAc;AACxC,QAAM,SAAiB,OAAO,UAAU;AASxC,QAAM,eAAeI,UAAS,4BAA4B;AAAA,IACzD;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC;AACD,QAAM,cAAc,aAAa,MAAM,IAAI,aAAa,QAAQ;AAChE,MAAI,aAAa;AAChB,WAAO;AAAA,MACN;AAAA,MACA,EAAE,OAAO,YAAY,QAAQ;AAAA,IAC9B;AAAA,EACD;AAEA,WAAS,kBACR,QACA,eACA,SACiD;AACjD,WAAOC,aAAY;AAAA,OACjB,YAAY;AACZ,eAAO,KAAK,iCAAiC,EAAE,eAAe,QAAQ,CAAC;AAEvE,cAAM,gBAAgB,MAAM,iBAAiB,QAAQ,YAAY;AAEjE,cAAM,WAAW,MAAM,MAAM,GAAG,MAAM,6BAA6B;AAAA,UAClE,QAAQ;AAAA,UACR,SAAS;AAAA,YACR,gBAAgB;AAAA,YAChB,GAAG;AAAA,UACJ;AAAA,UACA,MAAM,KAAK,UAAU;AAAA,YACpB,iBAAiB;AAAA,YACjB,UAAU;AAAA,YACV,cAAc,OAAO,QAAQ,YAAY;AAAA,UAC1C,CAAC;AAAA,QACF,CAAC;AAED,YAAI,CAAC,SAAS,IAAI;AACjB,gBAAM,IAAI,iBAAiB,2BAA2B,SAAS,MAAM,IAAI;AAAA,YACxE,MAAM;AAAA,YACN,SAAS,EAAE,QAAQ,SAAS,OAAO;AAAA,UACpC,CAAC;AAAA,QACF;AAEA,cAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,eAAO,KAAK,6BAA6B,EAAE,QAAQ,CAAC;AACpD,eAAO;AAAA,MACR,GAAG;AAAA,MACH,CAAC,UAAU;AACV,YAAI,iBAAiB,iBAAkB,QAAO;AAC9C,eAAO,IAAI,iBAAiB,qBAAqB;AAAA,UAChD,MAAM;AAAA,UACN;AAAA,QACD,CAAC;AAAA,MACF;AAAA,IACD;AAAA,EACD;AAEA,iBAAe,sBAAsB,QAKF;AAClC,WAAO,KAAK,gCAAgC;AAAA,MAC3C,UAAU,OAAO,aAAa;AAAA,MAC9B,YAAY,OAAO,aAAa;AAAA,IACjC,CAAC;AAED,UAAM,CAAC,aAAa,eAAe,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,MACrE,eAAe,UAAU;AAAA,MACzB,iBAAiB;AAAA,MACjB,iBAAiB,OAAO,QAAQ,cAAc;AAAA,IAC/C,CAAC;AAED,UAAM,SAAS,EAAE,GAAG,eAAe,YAAY;AAE/C,UAAM,cAAc,OAAO,OAAO,QAAQ,YAAY;AACtD,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,UAAU,KAAK,UAAU;AAAA,MAC9B,cAAc;AAAA,QACb,UAAU,OAAO,aAAa;AAAA,QAC9B,SAAS,OAAO,aAAa;AAAA,QAC7B,UAAU,OAAO,aAAa;AAAA,QAC9B,cAAc,OAAO,aAAa;AAAA,QAClC,aAAa,OAAO,aAAa;AAAA,QACjC,aAAa,OAAO,aAAa;AAAA,MAClC;AAAA,MACA,qBAAqB;AAAA,QACpB,eAAe,OAAO,aAAa;AAAA,QACnC,aAAa,OAAO,aAAa;AAAA,QACjC,UAAU,OAAO,aAAa;AAAA,QAC9B,mBAAmB,OAAO,aAAa;AAAA,QACvC,KAAK,OAAO,aAAa;AAAA,QACzB,kBAAkB,OAAO,aAAa;AAAA,QACtC,gBAAgB,OAAO,aAAa;AAAA,QACpC,2BAA2B,OAAO,aAAa;AAAA,QAC/C,iBAAiB;AAAA,QACjB,cAAc,OAAO;AAAA,MACtB;AAAA,MACA,gBAAgB;AAAA,IACjB,CAAC;AAGD,UAAM,MAAM,aAAa,WAAW,IAAI,SAAS;AACjD,UAAM,YAAY,MAAM,eAAe,SAAS,KAAK,aAAa;AAElE,UAAM,WAAW,MAAM,MAAM,GAAG,MAAM,kBAAkB;AAAA,MACvD,QAAQ;AAAA,MACR,SAAS;AAAA,QACR,gBAAgB;AAAA,QAChB,GAAG;AAAA,MACJ;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACpB,MAAM;AAAA,QACN,cAAc;AAAA,QACd;AAAA,QACA,mBAAmB;AAAA,MACpB,CAAC;AAAA,IACF,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AACjB,YAAM,IAAI,iBAAiB,4BAA4B,SAAS,MAAM,IAAI;AAAA,QACzE,MAAM;AAAA,QACN,SAAS,EAAE,QAAQ,SAAS,OAAO;AAAA,MACpC,CAAC;AAAA,IACF;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,sBAAsB;AAAA,MACjC,UAAU,KAAK;AAAA,MACf,eAAe,KAAK;AAAA,IACrB,CAAC;AACD,WAAO;AAAA,EACR;AAEA,SAAO;AAAA,IACN,MAAM,OAAO;AACZ,aAAO,KAAK,2BAA2B;AACvC,UAAI;AACH,iBAAS;AAAA,MACV,SAAS,OAAO;AACf,eAAO,MAAM,8BAA8B,EAAE,OAAO,OAAO,KAAK,EAAE,CAAC;AAAA,MACpE;AACA,UAAI;AACH,cAAM,qBAAqB;AAAA,MAC5B,SAAS,OAAO;AACf,eAAO,MAAM,uCAAuC;AAAA,UACnD,OAAO,OAAO,KAAK;AAAA,QACpB,CAAC;AAAA,MACF;AACA,aAAO,KAAK,0BAA0B;AAAA,IACvC;AAAA,IAEA,cAAc,QAKsC;AACnD,UAAI,YAAa,QAAO,SAAS,WAAW;AAC5C,aAAOA,aAAY;AAAA,QAClB,sBAAsB,MAAM,EAAE,KAAK,CAAC,UAAU;AAAA,UAC7C,UAAU,KAAK;AAAA,UACf,eAAe,KAAK;AAAA,UACpB,SAAS,KAAK;AAAA,UACd,WAAW,CAAC,YACX,kBAAkB,OAAO,QAAQ,KAAK,iBAAiB,OAAO;AAAA,QAChE,EAAE;AAAA,QACF,CAAC,UAAU;AACV,cAAI,iBAAiB,iBAAkB,QAAO;AAC9C,iBAAO,IAAI,iBAAiB,sBAAsB;AAAA,YACjD,MAAM;AAAA,YACN;AAAA,UACD,CAAC;AAAA,QACF;AAAA,MACD;AAAA,IACD;AAAA,IAEA,gBAAgB,QAMyC;AACxD,UAAI,YAAa,QAAO,SAAS,WAAW;AAC5C,aAAOA,aAAY;AAAA,SACjB,YAAY;AACZ,cAAI,gBAA+B;AAGnC,cAAI;AACH,kBAAM,aAAa,MAAM,sBAAsB,MAAM;AACrD,gBAAI,CAAC,WAAW,UAAU;AACzB,qBAAO,EAAE,QAAQ,YAAqB,SAAS,WAAW,QAAQ;AAAA,YACnE;AACA,4BAAgB,WAAW;AAAA,UAC5B,SAAS,OAAO;AACf,mBAAO,MAAM,yDAAyD;AAAA,cACrE,OAAO,OAAO,KAAK;AAAA,YACpB,CAAC;AAAA,UACF;AAGA,cAAI;AACJ,cAAI;AACH,sBAAU,MAAM,OAAO,WAAW;AAAA,UACnC,SAAS,OAAO;AACf,kBAAM,IAAI,iBAAiB,+BAA+B;AAAA,cACzD,MAAM;AAAA,cACN;AAAA,YACD,CAAC;AAAA,UACF;AAGA,cAAI,kBAAkB,MAAM;AAC3B,8BAAkB,OAAO,QAAQ,eAAe,OAAO,EAAE,OAAO,CAAC,MAAM;AACtE,qBAAO,MAAM,wCAAwC;AAAA,gBACpD;AAAA,gBACA;AAAA,gBACA,OAAO,EAAE;AAAA,cACV,CAAC;AAAA,YACF,CAAC;AAAA,UACF;AAEA,iBAAO,EAAE,QAAQ,UAAmB,QAAQ;AAAA,QAC7C,GAAG;AAAA,QACH,CAAC,UAAU;AACV,cAAI,iBAAiB,iBAAkB,QAAO;AAC9C,iBAAO,IAAI,iBAAiB,4BAA4B;AAAA,YACvD,MAAM;AAAA,YACN;AAAA,UACD,CAAC;AAAA,QACF;AAAA,MACD;AAAA,IACD;AAAA,IAEA,eAAe,QAEgD;AAC9D,UAAI,YAAa,QAAO,SAAS,WAAW;AAC5C,aAAOA,aAAY;AAAA,SACjB,YAAY;AACZ,iBAAO,KAAK,qBAAqB;AAEjC,gBAAM,oBAAoB,MAAM,eAAqB,GAAI;AACzD,cAAI,CAAC,mBAAmB;AACvB,mBAAO,KAAK,qCAAqC;AACjD,mBAAO;AAAA,UACR;AAEA,gBAAM,gBAAgB,MAAM,iBAAiB,OAAO,QAAQ,iBAAiB;AAE7E,gBAAM,oBAAoB,OAAO,OAAO,QAAQ,YAAY;AAC5D,gBAAM,YAAY,KAAK,IAAI;AAE3B,gBAAM,UAAU,KAAK,UAAU;AAAA,YAC9B,gBAAgB,kBAAkB;AAAA,UACnC,CAAC;AAGD,gBAAM,MAAM,eAAe,iBAAiB,IAAI,SAAS;AACzD,gBAAM,YAAY,MAAM,eAAe,SAAS,KAAK,aAAa;AAElE,gBAAM,WAAW,MAAM,MAAM,GAAG,MAAM,oBAAoB;AAAA,YACzD,QAAQ;AAAA,YACR,SAAS;AAAA,cACR,gBAAgB;AAAA,cAChB,GAAG;AAAA,YACJ;AAAA,YACA,MAAM,KAAK,UAAU;AAAA,cACpB,cAAc;AAAA,cACd;AAAA,cACA,mBAAmB;AAAA,YACpB,CAAC;AAAA,UACF,CAAC;AAED,cAAI,CAAC,SAAS,IAAI;AACjB,kBAAM,IAAI,iBAAiB,gCAAgC,SAAS,MAAM,IAAI;AAAA,cAC7E,MAAM;AAAA,cACN,SAAS,EAAE,QAAQ,SAAS,OAAO;AAAA,YACpC,CAAC;AAAA,UACF;AAEA,gBAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,iBAAO,KAAK,iCAAiC;AAC7C,iBAAO;AAAA,QACR,GAAG;AAAA,QACH,CAAC,UAAU;AACV,cAAI,iBAAiB,iBAAkB,QAAO;AAC9C,iBAAO,IAAI,iBAAiB,0BAA0B;AAAA,YACrD,MAAM;AAAA,YACN;AAAA,UACD,CAAC;AAAA,QACF;AAAA,MACD;AAAA,IACD;AAAA,IAEA,MAAM,iBAAiB;AACtB,aAAO,eAAqB,GAAI;AAAA,IACjC;AAAA,IAEA,MAAM,mBAAmB;AACxB,aAAO,iBAAiB;AAAA,IACzB;AAAA,IAEA,qBAAqB;AACpB,yBAAY;AAAA,IACb;AAAA,EACD;AACD;","names":["ResultAsync","err","ok","z","z","validate","ok","err","validate","ResultAsync"]}
package/dist/index.cjs CHANGED
@@ -66,7 +66,7 @@ var ZodAddressSchema = import_zod.z.string().refine((s) => (0, import_viem.isAdd
66
66
  var ZodCurrencySchema = import_zod.z.enum(CURRENCY_CODES);
67
67
 
68
68
  // src/index.ts
69
- var VERSION = "1.2.6";
69
+ var VERSION = "1.2.8";
70
70
  // Annotate the CommonJS export names for ESM import in node:
71
71
  0 && (module.exports = {
72
72
  SdkError,
package/dist/index.mjs CHANGED
@@ -39,7 +39,7 @@ var ZodAddressSchema = z.string().refine((s) => isAddress(s), { message: "Invali
39
39
  var ZodCurrencySchema = z.enum(CURRENCY_CODES);
40
40
 
41
41
  // src/index.ts
42
- var VERSION = "1.2.6";
42
+ var VERSION = "1.2.8";
43
43
  export {
44
44
  SdkError,
45
45
  VERSION
package/dist/orders.cjs CHANGED
@@ -857,6 +857,38 @@ var reputationManagerAbi = [
857
857
  outputs: [{ internalType: "bool", name: "", type: "bool" }],
858
858
  stateMutability: "view",
859
859
  type: "function"
860
+ },
861
+ // ── BVN (Nigerian Bank Verification Number) attestation ─────────────────
862
+ { inputs: [], name: "BvnSignerNotSet", type: "error" },
863
+ { inputs: [], name: "BvnAttestationExpired", type: "error" },
864
+ { inputs: [], name: "BvnNullifierAlreadySpent", type: "error" },
865
+ { inputs: [], name: "BvnAlreadyVerified", type: "error" },
866
+ { inputs: [], name: "BvnInvalidSignature", type: "error" },
867
+ {
868
+ inputs: [
869
+ { internalType: "bytes32", name: "nullifier", type: "bytes32" },
870
+ { internalType: "uint256", name: "limit", type: "uint256" },
871
+ { internalType: "uint256", name: "expiry", type: "uint256" },
872
+ { internalType: "bytes", name: "signature", type: "bytes" }
873
+ ],
874
+ name: "submitBvnAttestation",
875
+ outputs: [],
876
+ stateMutability: "nonpayable",
877
+ type: "function"
878
+ },
879
+ {
880
+ inputs: [],
881
+ name: "bvnRp",
882
+ outputs: [{ internalType: "uint256", name: "", type: "uint256" }],
883
+ stateMutability: "view",
884
+ type: "function"
885
+ },
886
+ {
887
+ inputs: [{ internalType: "address", name: "", type: "address" }],
888
+ name: "bvnVerified",
889
+ outputs: [{ internalType: "bool", name: "", type: "bool" }],
890
+ stateMutability: "view",
891
+ type: "function"
860
892
  }
861
893
  ];
862
894
 
@@ -5451,6 +5483,20 @@ var ZodSimpleKycSubmitParamsSchema = import_zod8.z.object({
5451
5483
  message: "signature must be a hex string"
5452
5484
  })
5453
5485
  });
5486
+ var ZodBvnSubmitParamsSchema = import_zod8.z.object({
5487
+ /** Per-(tenant, identity) Sybil nullifier (bytes32 hex). */
5488
+ nullifier: import_zod8.z.string().refine((v) => /^0x[a-fA-F0-9]{64}$/.test(v), {
5489
+ message: "nullifier must be a bytes32 hex string"
5490
+ }),
5491
+ /** Remaining limit in micro-USDC (part of the signed struct). */
5492
+ limit: import_zod8.z.bigint(),
5493
+ /** Attestation expiry (unix seconds). */
5494
+ expiry: import_zod8.z.bigint(),
5495
+ /** 65-byte secp256k1 signature (hex). */
5496
+ signature: import_zod8.z.string().refine((v) => /^0x[a-fA-F0-9]+$/.test(v), {
5497
+ message: "signature must be a hex string"
5498
+ })
5499
+ });
5454
5500
 
5455
5501
  // src/contracts/tx-limits/index.ts
5456
5502
  var import_neverthrow11 = require("neverthrow");