s0racle-sdk 0.0.1

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.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # s0racle-sdk
2
+
3
+ <p align="center">
4
+ <img src="https://avatars.githubusercontent.com/u/288873784?s=200&v=4" alt="s0racle" width="120" />
5
+ </p>
6
+
7
+ <p align="center">
8
+ TypeScript SDK for reading live Solana network health from the s0racle oracle.
9
+ </p>
10
+
11
+ <p align="center">
12
+ <img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT License" />
13
+ <img src="https://img.shields.io/badge/Solana-devnet-9945FF?logo=solana" alt="Solana devnet" />
14
+ <img src="https://img.shields.io/badge/npm-0.1.0-CB3837?logo=npm" alt="npm 0.1.0" />
15
+ <img src="https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript" alt="TypeScript" />
16
+ </p>
17
+
18
+ ---
19
+
20
+ ## Overview
21
+
22
+ s0racle publishes live Solana network health on-chain — validator reachability, slot latency, client diversity, and stake-weighted reach — aggregated from distributed observer nodes across multiple regions.
23
+
24
+ This SDK gives you a clean TypeScript interface to read that data. One function call, typed response, no Anchor boilerplate, no manual PDA derivation.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ npm install s0racle-sdk @solana/web3.js
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ```ts
35
+ import { Connection } from "@solana/web3.js";
36
+ import { createS0racleClient } from "s0racle-sdk";
37
+
38
+ const connection = new Connection("https://api.devnet.solana.com");
39
+ const client = createS0racleClient({ connection });
40
+
41
+ const health = await client.getNetworkHealth();
42
+
43
+ console.log(health.healthScore); // 0–100
44
+ console.log(health.tpuReachabilityPct); // % validators reachable right now
45
+ console.log(health.avgSlotLatencyMs); // how far behind is the network
46
+ console.log(health.agaveCount, health.firedancerCount, health.jitoCount);
47
+ ```
48
+
49
+ ## Stake-Weighted Reach & Client Diversity
50
+
51
+ Raw validator counts don't tell the full story. s0racle weights reachability by stake and tracks which clients are running — so you know if the network can actually finalize, not just respond.
52
+
53
+ ```ts
54
+ import { isConsensusCritical, stakeReachStatus, clientDiversityIndex } from "s0racle-sdk";
55
+
56
+ for (const region of health.regionScores) {
57
+ if (region.observerCount === 0) continue;
58
+
59
+ console.log(stakeReachStatus(region.reachableStakePct)); // healthy | degraded | critical
60
+ console.log(clientDiversityIndex(region)); // 0 = monoculture, 100 = perfectly even
61
+
62
+ if (isConsensusCritical(region.reachableStakePct)) {
63
+ // under 67% stake reachable — network cannot finalize
64
+ // pause writes, widen spreads, or alert users
65
+ }
66
+ }
67
+ ```
68
+
69
+ Thresholds: `>= 80%` healthy · `>= 67%` degraded · `< 67%` critical
70
+
71
+ ## Health Score
72
+
73
+ ```
74
+ healthScore = (reachabilityPct × 70%) + (latencyScore × 30%)
75
+ latencyScore = max(0, (400 - slotLatencyMs) × 100 / 400)
76
+ ```
77
+
78
+ Reachability is weighted higher because a degraded but reachable network can still process transactions — an unreachable one cannot.
79
+
80
+ ## API
81
+
82
+ ### `createS0racleClient(options)`
83
+
84
+ | Option | Type | Default |
85
+ |---|---|---|
86
+ | `connection` | `Connection` | required |
87
+ | `programId` | `PublicKey` | s0racle devnet address |
88
+ | `wallet` | `Wallet` | read-only by default |
89
+
90
+ ### Read methods
91
+
92
+ | Method | Returns |
93
+ |---|---|
94
+ | `getNetworkHealth()` | `Promise<NetworkHealth>` |
95
+ | `getRegistry()` | `Promise<Registry>` |
96
+ | `getObserver(pubkey)` | `Promise<Observer>` |
97
+ | `getAllObservers()` | `Promise<Observer[]>` |
98
+ | `getObserversByRegion(region)` | `Promise<Observer[]>` |
99
+
100
+ ### Utility helpers
101
+
102
+ | Function | What it does |
103
+ |---|---|
104
+ | `healthStatus(health)` | `"healthy"` · `"degraded"` · `"critical"` |
105
+ | `isStale(health, slot)` | True if data is older than 150 slots |
106
+ | `isDegraded(health)` | True if score below 70 |
107
+ | `isConsensusCritical(pct)` | True if stake reach below 67% |
108
+ | `stakeReachStatus(pct)` | Threshold label for stake reach |
109
+ | `dominantClient(region)` | Which client runs most validators here |
110
+ | `clientDiversityIndex(region)` | 0–100 diversity score |
111
+ | `regionLabel(region)` | Human readable region name |
112
+
113
+ ### Regions
114
+
115
+ `Asia` · `US` · `EU` · `SouthAmerica` · `Africa` · `Oceania` · `Other`
116
+
117
+ ## Examples
118
+
119
+ ```bash
120
+ cd examples
121
+ npm install
122
+ npm run read # fetch and print NetworkHealth
123
+ npm run observers # list observers by region
124
+ npm run diversity # stake-weighted consensus check
125
+ ```
126
+
127
+ ## Status
128
+
129
+ Early development — API may change before `1.0.0`.
130
+
131
+ Program ID (devnet): `2paXpX8Ze3tvYezviSwQJSSihG3LbrDiD7SNsaFwgTow`
132
+
133
+ ## Links
134
+
135
+ GitHub: [github.com/s0racle](https://github.com/s0racle)
136
+
137
+ ## License
138
+
139
+ MIT
@@ -0,0 +1,182 @@
1
+ import { PublicKey, Connection, TransactionInstruction } from '@solana/web3.js';
2
+ import { Wallet } from '@coral-xyz/anchor';
3
+
4
+ declare enum Region {
5
+ Asia = "Asia",
6
+ US = "US",
7
+ EU = "EU",
8
+ SouthAmerica = "SouthAmerica",
9
+ Africa = "Africa",
10
+ Oceania = "Oceania",
11
+ Other = "Other"
12
+ }
13
+ interface Attestation {
14
+ slot: bigint;
15
+ timestamp: bigint;
16
+ avgRttUs: number;
17
+ p95RttUs: number;
18
+ slotLatencyMs: number;
19
+ tpuReachable: number;
20
+ tpuProbed: number;
21
+ agaveCount: number;
22
+ firedancerCount: number;
23
+ jitoCount: number;
24
+ solanaLabsCount: number;
25
+ otherCount: number;
26
+ reachableStakePct: number;
27
+ }
28
+ interface RegionScore {
29
+ region: Region;
30
+ observerCount: number;
31
+ healthScore: number;
32
+ reachabilityPct: number;
33
+ avgRttUs: number;
34
+ slotLatencyMs: number;
35
+ lastUpdatedSlot: bigint;
36
+ agaveCount: number;
37
+ firedancerCount: number;
38
+ jitoCount: number;
39
+ solanaLabsCount: number;
40
+ otherCount: number;
41
+ reachableStakePct: number;
42
+ }
43
+ interface NetworkHealth {
44
+ healthScore: number;
45
+ tpuReachabilityPct: number;
46
+ avgSlotLatencyMs: number;
47
+ activeObserverCount: number;
48
+ activeRegionCount: number;
49
+ lastUpdatedSlot: bigint;
50
+ lastUpdatedTs: bigint;
51
+ minHealthEver: number | null;
52
+ maxHealthEver: number;
53
+ totalAttestations: bigint;
54
+ regionScores: RegionScore[];
55
+ agaveCount: number;
56
+ firedancerCount: number;
57
+ jitoCount: number;
58
+ solanaLabsCount: number;
59
+ otherCount: number;
60
+ }
61
+ interface Observer {
62
+ publicKey: PublicKey;
63
+ authority: PublicKey;
64
+ region: Region;
65
+ stakeLamports: bigint;
66
+ registeredAt: bigint;
67
+ lastAttestationSlot: bigint;
68
+ attestationCount: bigint;
69
+ latestAttestation: Attestation;
70
+ isActive: boolean;
71
+ }
72
+ interface Registry {
73
+ authority: PublicKey;
74
+ pendingAuthority: PublicKey | null;
75
+ minStakeLamports: bigint;
76
+ observerCount: number;
77
+ activeCount: number;
78
+ maxObservers: number;
79
+ paused: boolean;
80
+ version: number;
81
+ }
82
+ interface AttestationSubmittedEvent {
83
+ observer: PublicKey;
84
+ region: Region;
85
+ score: number;
86
+ reachabilityPct: number;
87
+ slotLatencyMs: number;
88
+ slot: bigint;
89
+ agaveCount: number;
90
+ firedancerCount: number;
91
+ jitoCount: number;
92
+ solanaLabsCount: number;
93
+ otherCount: number;
94
+ reachableStakePct: number;
95
+ }
96
+ interface ObserverRegisteredEvent {
97
+ observer: PublicKey;
98
+ region: Region;
99
+ stakeLamports: bigint;
100
+ }
101
+ interface ObserverDeregisteredEvent {
102
+ observer: PublicKey;
103
+ }
104
+ interface ObserverSlashedEvent {
105
+ observer: PublicKey;
106
+ slashBps: number;
107
+ amountSlashed: bigint;
108
+ }
109
+ interface ConfigUpdatedEvent {
110
+ minStakeLamports: bigint | null;
111
+ maxObservers: number | null;
112
+ paused: boolean | null;
113
+ }
114
+
115
+ declare const PROGRAM_ID: PublicKey;
116
+ declare function getRegistryPDA(programId?: PublicKey): [PublicKey, number];
117
+ declare function getNetworkHealthPDA(programId?: PublicKey): [PublicKey, number];
118
+ declare function getObserverPDA(observer: PublicKey, programId?: PublicKey): [PublicKey, number];
119
+
120
+ interface SubmitAttestationParams {
121
+ tpuReachable: number;
122
+ tpuProbed: number;
123
+ avgRttUs: number;
124
+ p95RttUs: number;
125
+ slotLatencyMs: number;
126
+ agaveCount: number;
127
+ firedancerCount: number;
128
+ jitoCount: number;
129
+ solanaLabsCount: number;
130
+ otherCount: number;
131
+ reachableStakePct: number;
132
+ }
133
+ interface UpdateConfigParams {
134
+ minStakeLamports: bigint | null;
135
+ maxObservers: number | null;
136
+ paused: boolean | null;
137
+ }
138
+
139
+ interface S0racleClientOptions {
140
+ connection: Connection;
141
+ programId?: PublicKey;
142
+ wallet?: Wallet;
143
+ }
144
+ interface S0racleClient {
145
+ readonly programId: PublicKey;
146
+ readonly connection: Connection;
147
+ getNetworkHealth(): Promise<NetworkHealth>;
148
+ getRegistry(): Promise<Registry>;
149
+ getObserver(observerPubkey: PublicKey): Promise<Observer>;
150
+ getAllObservers(): Promise<Observer[]>;
151
+ getObserversByRegion(region: Region): Promise<Observer[]>;
152
+ registerObserver(observer: PublicKey, region: Region): Promise<TransactionInstruction>;
153
+ submitAttestation(authority: PublicKey, params: SubmitAttestationParams): Promise<TransactionInstruction>;
154
+ deregisterObserver(caller: PublicKey, observerWallet: PublicKey): Promise<TransactionInstruction>;
155
+ crankAggregation(cranker: PublicKey, observerAccounts: PublicKey[]): Promise<TransactionInstruction>;
156
+ initialize(authority: PublicKey, minStakeLamports: bigint, maxObservers: number): Promise<TransactionInstruction>;
157
+ slashObserver(authority: PublicKey, observerWallet: PublicKey, treasury: PublicKey, slashBps: number): Promise<TransactionInstruction>;
158
+ updateConfig(authority: PublicKey, params: UpdateConfigParams): Promise<TransactionInstruction>;
159
+ proposeAuthority(authority: PublicKey, newAuthority: PublicKey): Promise<TransactionInstruction>;
160
+ acceptAuthority(newAuthority: PublicKey): Promise<TransactionInstruction>;
161
+ onAttestationSubmitted(callback: (event: AttestationSubmittedEvent, slot: number) => void): number;
162
+ onObserverRegistered(callback: (event: ObserverRegisteredEvent, slot: number) => void): number;
163
+ onObserverDeregistered(callback: (event: ObserverDeregisteredEvent, slot: number) => void): number;
164
+ onObserverSlashed(callback: (event: ObserverSlashedEvent, slot: number) => void): number;
165
+ onConfigUpdated(callback: (event: ConfigUpdatedEvent, slot: number) => void): number;
166
+ removeEventListener(listenerId: number): Promise<void>;
167
+ }
168
+ declare function createS0racleClient(opts: S0racleClientOptions): S0racleClient;
169
+
170
+ declare function isStale(networkHealth: NetworkHealth, currentSlot: bigint): boolean;
171
+ declare function isObserverStale(observer: Observer, currentSlot: bigint): boolean;
172
+ declare function isDegraded(networkHealth: NetworkHealth, threshold?: number): boolean;
173
+ declare function healthStatus(networkHealth: NetworkHealth, currentSlot?: bigint): "healthy" | "degraded" | "critical" | "stale";
174
+ declare function regionLabel(region: Region): string;
175
+ declare function lamportsToSol(lamports: bigint): number;
176
+ declare function latencyScore(slotLatencyMs: number): number;
177
+ declare function isConsensusCritical(reachableStakePct: number): boolean;
178
+ declare function stakeReachStatus(reachableStakePct: number): "healthy" | "degraded" | "critical";
179
+ declare function dominantClient(region: RegionScore): "agave" | "firedancer" | "jito" | "other";
180
+ declare function clientDiversityIndex(region: RegionScore): number;
181
+
182
+ export { type Attestation, type AttestationSubmittedEvent, type ConfigUpdatedEvent, type NetworkHealth, type Observer, type ObserverDeregisteredEvent, type ObserverRegisteredEvent, type ObserverSlashedEvent, PROGRAM_ID, Region, type RegionScore, type Registry, type S0racleClient, type S0racleClientOptions, type SubmitAttestationParams, type UpdateConfigParams, clientDiversityIndex, createS0racleClient, dominantClient, getNetworkHealthPDA, getObserverPDA, getRegistryPDA, healthStatus, isConsensusCritical, isDegraded, isObserverStale, isStale, lamportsToSol, latencyScore, regionLabel, stakeReachStatus };
@@ -0,0 +1,182 @@
1
+ import { PublicKey, Connection, TransactionInstruction } from '@solana/web3.js';
2
+ import { Wallet } from '@coral-xyz/anchor';
3
+
4
+ declare enum Region {
5
+ Asia = "Asia",
6
+ US = "US",
7
+ EU = "EU",
8
+ SouthAmerica = "SouthAmerica",
9
+ Africa = "Africa",
10
+ Oceania = "Oceania",
11
+ Other = "Other"
12
+ }
13
+ interface Attestation {
14
+ slot: bigint;
15
+ timestamp: bigint;
16
+ avgRttUs: number;
17
+ p95RttUs: number;
18
+ slotLatencyMs: number;
19
+ tpuReachable: number;
20
+ tpuProbed: number;
21
+ agaveCount: number;
22
+ firedancerCount: number;
23
+ jitoCount: number;
24
+ solanaLabsCount: number;
25
+ otherCount: number;
26
+ reachableStakePct: number;
27
+ }
28
+ interface RegionScore {
29
+ region: Region;
30
+ observerCount: number;
31
+ healthScore: number;
32
+ reachabilityPct: number;
33
+ avgRttUs: number;
34
+ slotLatencyMs: number;
35
+ lastUpdatedSlot: bigint;
36
+ agaveCount: number;
37
+ firedancerCount: number;
38
+ jitoCount: number;
39
+ solanaLabsCount: number;
40
+ otherCount: number;
41
+ reachableStakePct: number;
42
+ }
43
+ interface NetworkHealth {
44
+ healthScore: number;
45
+ tpuReachabilityPct: number;
46
+ avgSlotLatencyMs: number;
47
+ activeObserverCount: number;
48
+ activeRegionCount: number;
49
+ lastUpdatedSlot: bigint;
50
+ lastUpdatedTs: bigint;
51
+ minHealthEver: number | null;
52
+ maxHealthEver: number;
53
+ totalAttestations: bigint;
54
+ regionScores: RegionScore[];
55
+ agaveCount: number;
56
+ firedancerCount: number;
57
+ jitoCount: number;
58
+ solanaLabsCount: number;
59
+ otherCount: number;
60
+ }
61
+ interface Observer {
62
+ publicKey: PublicKey;
63
+ authority: PublicKey;
64
+ region: Region;
65
+ stakeLamports: bigint;
66
+ registeredAt: bigint;
67
+ lastAttestationSlot: bigint;
68
+ attestationCount: bigint;
69
+ latestAttestation: Attestation;
70
+ isActive: boolean;
71
+ }
72
+ interface Registry {
73
+ authority: PublicKey;
74
+ pendingAuthority: PublicKey | null;
75
+ minStakeLamports: bigint;
76
+ observerCount: number;
77
+ activeCount: number;
78
+ maxObservers: number;
79
+ paused: boolean;
80
+ version: number;
81
+ }
82
+ interface AttestationSubmittedEvent {
83
+ observer: PublicKey;
84
+ region: Region;
85
+ score: number;
86
+ reachabilityPct: number;
87
+ slotLatencyMs: number;
88
+ slot: bigint;
89
+ agaveCount: number;
90
+ firedancerCount: number;
91
+ jitoCount: number;
92
+ solanaLabsCount: number;
93
+ otherCount: number;
94
+ reachableStakePct: number;
95
+ }
96
+ interface ObserverRegisteredEvent {
97
+ observer: PublicKey;
98
+ region: Region;
99
+ stakeLamports: bigint;
100
+ }
101
+ interface ObserverDeregisteredEvent {
102
+ observer: PublicKey;
103
+ }
104
+ interface ObserverSlashedEvent {
105
+ observer: PublicKey;
106
+ slashBps: number;
107
+ amountSlashed: bigint;
108
+ }
109
+ interface ConfigUpdatedEvent {
110
+ minStakeLamports: bigint | null;
111
+ maxObservers: number | null;
112
+ paused: boolean | null;
113
+ }
114
+
115
+ declare const PROGRAM_ID: PublicKey;
116
+ declare function getRegistryPDA(programId?: PublicKey): [PublicKey, number];
117
+ declare function getNetworkHealthPDA(programId?: PublicKey): [PublicKey, number];
118
+ declare function getObserverPDA(observer: PublicKey, programId?: PublicKey): [PublicKey, number];
119
+
120
+ interface SubmitAttestationParams {
121
+ tpuReachable: number;
122
+ tpuProbed: number;
123
+ avgRttUs: number;
124
+ p95RttUs: number;
125
+ slotLatencyMs: number;
126
+ agaveCount: number;
127
+ firedancerCount: number;
128
+ jitoCount: number;
129
+ solanaLabsCount: number;
130
+ otherCount: number;
131
+ reachableStakePct: number;
132
+ }
133
+ interface UpdateConfigParams {
134
+ minStakeLamports: bigint | null;
135
+ maxObservers: number | null;
136
+ paused: boolean | null;
137
+ }
138
+
139
+ interface S0racleClientOptions {
140
+ connection: Connection;
141
+ programId?: PublicKey;
142
+ wallet?: Wallet;
143
+ }
144
+ interface S0racleClient {
145
+ readonly programId: PublicKey;
146
+ readonly connection: Connection;
147
+ getNetworkHealth(): Promise<NetworkHealth>;
148
+ getRegistry(): Promise<Registry>;
149
+ getObserver(observerPubkey: PublicKey): Promise<Observer>;
150
+ getAllObservers(): Promise<Observer[]>;
151
+ getObserversByRegion(region: Region): Promise<Observer[]>;
152
+ registerObserver(observer: PublicKey, region: Region): Promise<TransactionInstruction>;
153
+ submitAttestation(authority: PublicKey, params: SubmitAttestationParams): Promise<TransactionInstruction>;
154
+ deregisterObserver(caller: PublicKey, observerWallet: PublicKey): Promise<TransactionInstruction>;
155
+ crankAggregation(cranker: PublicKey, observerAccounts: PublicKey[]): Promise<TransactionInstruction>;
156
+ initialize(authority: PublicKey, minStakeLamports: bigint, maxObservers: number): Promise<TransactionInstruction>;
157
+ slashObserver(authority: PublicKey, observerWallet: PublicKey, treasury: PublicKey, slashBps: number): Promise<TransactionInstruction>;
158
+ updateConfig(authority: PublicKey, params: UpdateConfigParams): Promise<TransactionInstruction>;
159
+ proposeAuthority(authority: PublicKey, newAuthority: PublicKey): Promise<TransactionInstruction>;
160
+ acceptAuthority(newAuthority: PublicKey): Promise<TransactionInstruction>;
161
+ onAttestationSubmitted(callback: (event: AttestationSubmittedEvent, slot: number) => void): number;
162
+ onObserverRegistered(callback: (event: ObserverRegisteredEvent, slot: number) => void): number;
163
+ onObserverDeregistered(callback: (event: ObserverDeregisteredEvent, slot: number) => void): number;
164
+ onObserverSlashed(callback: (event: ObserverSlashedEvent, slot: number) => void): number;
165
+ onConfigUpdated(callback: (event: ConfigUpdatedEvent, slot: number) => void): number;
166
+ removeEventListener(listenerId: number): Promise<void>;
167
+ }
168
+ declare function createS0racleClient(opts: S0racleClientOptions): S0racleClient;
169
+
170
+ declare function isStale(networkHealth: NetworkHealth, currentSlot: bigint): boolean;
171
+ declare function isObserverStale(observer: Observer, currentSlot: bigint): boolean;
172
+ declare function isDegraded(networkHealth: NetworkHealth, threshold?: number): boolean;
173
+ declare function healthStatus(networkHealth: NetworkHealth, currentSlot?: bigint): "healthy" | "degraded" | "critical" | "stale";
174
+ declare function regionLabel(region: Region): string;
175
+ declare function lamportsToSol(lamports: bigint): number;
176
+ declare function latencyScore(slotLatencyMs: number): number;
177
+ declare function isConsensusCritical(reachableStakePct: number): boolean;
178
+ declare function stakeReachStatus(reachableStakePct: number): "healthy" | "degraded" | "critical";
179
+ declare function dominantClient(region: RegionScore): "agave" | "firedancer" | "jito" | "other";
180
+ declare function clientDiversityIndex(region: RegionScore): number;
181
+
182
+ export { type Attestation, type AttestationSubmittedEvent, type ConfigUpdatedEvent, type NetworkHealth, type Observer, type ObserverDeregisteredEvent, type ObserverRegisteredEvent, type ObserverSlashedEvent, PROGRAM_ID, Region, type RegionScore, type Registry, type S0racleClient, type S0racleClientOptions, type SubmitAttestationParams, type UpdateConfigParams, clientDiversityIndex, createS0racleClient, dominantClient, getNetworkHealthPDA, getObserverPDA, getRegistryPDA, healthStatus, isConsensusCritical, isDegraded, isObserverStale, isStale, lamportsToSol, latencyScore, regionLabel, stakeReachStatus };