forked from CredenceOrg/Credence-Frontend
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhorizon.ts
More file actions
104 lines (85 loc) · 3.37 KB
/
Copy pathhorizon.ts
File metadata and controls
104 lines (85 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
* @file horizon.ts
* @description Lightweight, SSR-safe wrapper around the Stellar Horizon REST API.
*
* Provides only the subset of Horizon we need — account balance lookups — without
* pulling in the full `@stellar/stellar-sdk`. Every export guards against a
* non-browser environment so importing this module is safe in SSR/test contexts.
*
* @see {@link https://developers.stellar.org/docs/horizon-api/reference/accounts-single}
*/
import type { CredenceNetwork } from './networkLabels'
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
const HORIZON_URLS: Record<CredenceNetwork, string> = {
public: 'https://horizon.stellar.org',
test: 'https://horizon-testnet.stellar.org',
}
/** Circle's USDC issuer on each Stellar network. */
const USDC_ISSUERS: Record<CredenceNetwork, string> = {
public: 'GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN',
test: 'GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5',
}
const USDC_ASSET_CODE = 'USDC'
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
/** Shape of a single balance entry in the Horizon accounts response. */
interface HorizonBalance {
asset_type: string
asset_code?: string
asset_issuer?: string
balance: string
}
/** Shape of the Horizon accounts response (subset we consume). */
interface HorizonAccount {
balances: HorizonBalance[]
}
// ---------------------------------------------------------------------------
// Errors
// ---------------------------------------------------------------------------
export class HorizonError extends Error {
readonly status: number
constructor(status: number, message: string) {
super(message)
this.name = 'HorizonError'
this.status = status
}
}
// ---------------------------------------------------------------------------
// Fetch helpers
// ---------------------------------------------------------------------------
/**
* Fetches the USDC balance for a Stellar account from Horizon.
*
* Returns `0` when the account has no USDC trustline (asset not found).
* Throws `HorizonError` on network/HTTP failures.
*
* @param address - Stellar public key (G…).
* @param network - Active Stellar network (`'public'` or `'test'`).
* @param signal - Optional `AbortSignal` for cancellation.
* @returns The USDC balance as a number.
*/
export async function fetchUsdcBalance(
address: string,
network: CredenceNetwork,
signal?: AbortSignal
): Promise<number> {
if (typeof window === 'undefined') return 0
const horizonUrl = HORIZON_URLS[network]
const url = `${horizonUrl}/accounts/${encodeURIComponent(address)}`
const response = await fetch(url, { signal })
if (!response.ok) {
// 404 means the account doesn't exist on this network — treat as zero balance.
if (response.status === 404) return 0
throw new HorizonError(response.status, `Horizon request failed (${response.status})`)
}
const account: HorizonAccount = await response.json()
const usdcIssuer = USDC_ISSUERS[network]
const match = account.balances.find(
(b) =>
b.asset_type !== 'native' && b.asset_code === USDC_ASSET_CODE && b.asset_issuer === usdcIssuer
)
return match ? Number(match.balance) : 0
}