{"version":3,"file":"blockberry.mjs","names":[],"sources":["../../src/providers/blockberry.ts"],"sourcesContent":["/**\n * Blockberry provider — indexed Sui data used by the Suiscan explorer.\n *\n * https://docs.blockberry.one/reference/sui-quickstart\n */\n\nimport type {\n  Balance,\n  ChainKey,\n  ProviderCapabilities,\n  ProviderConfig,\n  Transaction,\n  TxHistoryOptions,\n  TxStatus,\n} from \"../core/types.js\";\nimport { Provider } from \"../core/provider.js\";\nimport { normalizeBaseUrl, buildQuery } from \"../core/client.js\";\nimport { AuthError, UnsupportedChainError } from \"../core/errors.js\";\nimport { assertSafePathSegment } from \"../core/path-safety.js\";\nimport { clampMaxResults, formatWei } from \"../core/types.js\";\n\nconst DEFAULT_BASE = \"https://api.blockberry.one/sui\";\nconst SUI_COIN_TYPE = \"0x2::sui::SUI\";\nconst ACTIVITY_PAGE_SIZE = 50;\n\ninterface BlockberryBalance {\n  readonly coinType: string;\n  readonly coinSymbol: string;\n  readonly balance: string | number;\n  readonly decimals: number;\n}\n\ninterface BlockberryActivity {\n  readonly activityType: readonly string[];\n  readonly activityWith?: ReadonlyArray<{\n    readonly objectType: string;\n    readonly id: string;\n  }>;\n  readonly timestamp: number;\n  readonly digest: string;\n  readonly txStatus: \"SUCCESS\" | \"FAILURE\" | \"ABORT\";\n  readonly gasFee: string | number;\n}\n\ninterface BlockberryActivityPage {\n  readonly content: readonly BlockberryActivity[];\n  readonly nextCursor?: string;\n}\n\nfunction mapActivity(raw: Readonly<BlockberryActivity>): Transaction {\n  const counterparty = raw.activityWith?.find((item) => item.objectType === \"ACCOUNT\")?.id;\n  return {\n    hash: raw.digest,\n    blockNumber: 0,\n    timestamp: new Date(raw.timestamp).toISOString(),\n    from: counterparty ?? \"\",\n    to: null,\n    value: \"0\",\n    valueFormatted: \"0\",\n    fee: String(raw.gasFee),\n    status: (raw.txStatus === \"SUCCESS\" ? \"success\" : \"failed\") as TxStatus,\n    isContractInteraction: raw.activityType.some((type) => type !== \"TRANSFER\"),\n    tokenTransfers: [],\n    raw: raw as unknown as Record<string, unknown>,\n  };\n}\n\nasync function collectActivities(\n  limit: number,\n  readPage: (size: number, nextCursor: string | undefined) => Promise<BlockberryActivityPage>,\n): Promise<BlockberryActivity[]> {\n  const activities: BlockberryActivity[] = [];\n  const pageLimit = Math.ceil(limit / ACTIVITY_PAGE_SIZE);\n  let nextCursor: string | undefined;\n\n  for (let fetches = 0; fetches < pageLimit && activities.length < limit; fetches++) {\n    const size = Math.min(ACTIVITY_PAGE_SIZE, limit - activities.length);\n    const page = await readPage(size, nextCursor);\n    activities.push(...page.content.slice(0, size));\n    if (page.content.length < size || !page.nextCursor || page.nextCursor === nextCursor) break;\n    nextCursor = page.nextCursor;\n  }\n\n  return activities;\n}\n\nexport class Blockberry extends Provider {\n  static readonly key = \"blockberry\";\n\n  private readonly apiKey: string;\n  private readonly baseUrl: string;\n\n  constructor(config: Readonly<ProviderConfig>) {\n    super(config);\n    const apiKey = config.apiKey ?? process.env.BLOCKBERRY_API_KEY ?? \"\";\n    if (!apiKey) {\n      throw new AuthError(\"blockberry\", \"Set BLOCKBERRY_API_KEY or pass apiKey in config\");\n    }\n    this.apiKey = apiKey;\n    this.baseUrl = normalizeBaseUrl(config.baseUrl ?? DEFAULT_BASE);\n  }\n  get capabilities(): ProviderCapabilities {\n    return {\n      balances: true,\n      txHistory: true,\n      txDetail: false,\n      utxos: false,\n      contractInfo: false,\n      tokenBalances: false,\n      tokenTransfers: false,\n      gasData: false,\n      blockInfo: false,\n    };\n  }\n\n  private api<T>(path: string): Promise<T> {\n    return this.getJSON<T>(`${this.baseUrl}${path}`, {\n      headers: { \"x-api-key\": this.apiKey },\n    });\n  }\n\n  async getBalance(address: string, chain?: ChainKey): Promise<Balance> {\n    const c = chain ?? \"sui\";\n    if (c !== \"sui\") throw new UnsupportedChainError(c, this.name);\n    assertSafePathSegment(address, \"address\");\n\n    const balances = await this.api<BlockberryBalance[]>(\n      `/v1/accounts/${encodeURIComponent(address)}/balance`,\n    );\n    const sui = balances.find(\n      (balance) => balance.coinType === SUI_COIN_TYPE || balance.coinSymbol === \"SUI\",\n    );\n    const balance = String(sui?.balance ?? 0);\n    const decimals = sui?.decimals ?? 9;\n    return this.snapshotBalance({\n      address,\n      chain: \"sui\",\n      balance,\n      balanceFormatted: formatWei(balance, decimals),\n      symbol: \"SUI\",\n    });\n  }\n\n  async getTxHistory(\n    address: string,\n    chain?: ChainKey,\n    options?: Readonly<TxHistoryOptions>,\n  ): Promise<Transaction[]> {\n    const c = chain ?? \"sui\";\n    if (c !== \"sui\") throw new UnsupportedChainError(c, this.name);\n    assertSafePathSegment(address, \"address\");\n\n    const limit = options?.limit ? clampMaxResults(options.limit) : ACTIVITY_PAGE_SIZE;\n    const activities = await collectActivities(limit, (size, nextCursor) => {\n      const query = buildQuery({\n        actionType: \"ALL\",\n        nextCursor,\n        size,\n        orderBy: options?.sort === \"asc\" ? \"ASC\" : \"DESC\",\n      });\n      return this.api<BlockberryActivityPage>(\n        `/v1/accounts/${encodeURIComponent(address)}/activity${query}`,\n      );\n    });\n\n    return activities.map(mapActivity);\n  }\n}\n"],"mappings":"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"}