{"version":3,"sources":["../../src/protocol/grants.ts"],"sourcesContent":["import { verifyTypedData } from \"viem\";\nimport {\n  GRANT_REGISTRATION_TYPES,\n  grantRegistrationDomain,\n  type DataPortabilityGatewayConfig,\n} from \"./eip712\";\n\nexport interface VerifyGrantRegistrationInput {\n  gatewayConfig: DataPortabilityGatewayConfig;\n  grantorAddress: `0x${string}`;\n  granteeId: `0x${string}`;\n  scopes: string[];\n  // Decimal-string uint256 is the wire format; bigint/number are accepted\n  // for ergonomics. Must be >= 1 (first registration uses 1; each override\n  // strictly increases it — see GRANT_REGISTRATION_TYPES).\n  grantVersion: bigint | number | string;\n  // Unix seconds. 0 = no expiry. Anything > 0 is enforced against nowSeconds.\n  expiresAt: bigint | number | string;\n  signature: `0x${string}`;\n  nowSeconds?: number;\n}\n\nexport type VerifyGrantRegistrationResult =\n  | {\n      valid: true;\n      grantorAddress: `0x${string}`;\n      granteeId: `0x${string}`;\n      scopes: string[];\n      grantVersion: string;\n      expiresAt: string;\n    }\n  | {\n      valid: false;\n      error: string;\n    };\n\nfunction isHexString(value: unknown): value is `0x${string}` {\n  return typeof value === \"string\" && value.startsWith(\"0x\");\n}\n\nexport function isDataPortabilityGatewayConfig(\n  value: unknown,\n): value is DataPortabilityGatewayConfig {\n  if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n    return false;\n  }\n  const config = value as Record<string, unknown>;\n  const contracts = config[\"contracts\"];\n  if (\n    typeof config[\"chainId\"] !== \"number\" ||\n    !Number.isInteger(config[\"chainId\"]) ||\n    config[\"chainId\"] <= 0 ||\n    contracts === null ||\n    typeof contracts !== \"object\" ||\n    Array.isArray(contracts)\n  ) {\n    return false;\n  }\n  const c = contracts as Record<string, unknown>;\n  return (\n    isHexString(c[\"dataRegistry\"]) &&\n    isHexString(c[\"dataPortabilityPermissions\"]) &&\n    isHexString(c[\"dataPortabilityServer\"]) &&\n    isHexString(c[\"dataPortabilityGrantees\"]) &&\n    isHexString(c[\"dataPortabilityEscrow\"]) &&\n    isHexString(c[\"feeRegistry\"])\n  );\n}\n\nfunction toUint256(value: bigint | number | string): bigint | null {\n  try {\n    // Reject `number` inputs outside the safe-integer range — `BigInt(2**53 + 1)`\n    // doesn't throw, it just converts whatever rounded value JS already stored,\n    // which would silently corrupt grantVersion/expiresAt for any uint256 large\n    // enough to lose precision in a double. Callers that need values past\n    // `MAX_SAFE_INTEGER` must pass a string or bigint.\n    if (typeof value === \"number\" && !Number.isSafeInteger(value)) {\n      return null;\n    }\n    const big = typeof value === \"bigint\" ? value : BigInt(value);\n    if (big < 0n) return null;\n    return big;\n  } catch {\n    return null;\n  }\n}\n\nexport async function verifyGrantRegistration(\n  input: VerifyGrantRegistrationInput,\n): Promise<VerifyGrantRegistrationResult> {\n  if (!Array.isArray(input.scopes) || input.scopes.length === 0) {\n    return { valid: false, error: \"scopes must be a non-empty array\" };\n  }\n  if (!input.scopes.every((scope) => typeof scope === \"string\")) {\n    return { valid: false, error: \"scopes must contain only strings\" };\n  }\n\n  const grantVersion = toUint256(input.grantVersion);\n  if (grantVersion === null || grantVersion < 1n) {\n    return { valid: false, error: \"grantVersion must be a uint256 >= 1\" };\n  }\n\n  const expiresAt = toUint256(input.expiresAt);\n  if (expiresAt === null) {\n    return { valid: false, error: \"expiresAt must be a non-negative uint256\" };\n  }\n\n  let valid: boolean;\n  try {\n    valid = await verifyTypedData({\n      address: input.grantorAddress,\n      domain: grantRegistrationDomain(input.gatewayConfig),\n      types: GRANT_REGISTRATION_TYPES,\n      primaryType: \"GrantRegistration\",\n      message: {\n        grantorAddress: input.grantorAddress,\n        granteeId: input.granteeId,\n        scopes: input.scopes,\n        grantVersion,\n        expiresAt,\n      },\n      signature: input.signature,\n    });\n  } catch {\n    return { valid: false, error: \"EIP-712 signature verification failed\" };\n  }\n\n  if (!valid) {\n    return { valid: false, error: \"Grant signature does not match grantor\" };\n  }\n\n  // 0 is the \"no expiry\" sentinel — only enforce when expiresAt > 0.\n  const nowSeconds = input.nowSeconds ?? Math.floor(Date.now() / 1000);\n  if (expiresAt > 0n && expiresAt < BigInt(nowSeconds)) {\n    return { valid: false, error: \"Grant has expired\" };\n  }\n\n  return {\n    valid: true,\n    grantorAddress: input.grantorAddress,\n    granteeId: input.granteeId,\n    scopes: input.scopes,\n    grantVersion: grantVersion.toString(),\n    expiresAt: expiresAt.toString(),\n  };\n}\n"],"mappings":"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","names":[]}