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/dist/index.mjs ADDED
@@ -0,0 +1,2147 @@
1
+ // src/types.ts
2
+ import "@solana/web3.js";
3
+ var Region = /* @__PURE__ */ ((Region2) => {
4
+ Region2["Asia"] = "Asia";
5
+ Region2["US"] = "US";
6
+ Region2["EU"] = "EU";
7
+ Region2["SouthAmerica"] = "SouthAmerica";
8
+ Region2["Africa"] = "Africa";
9
+ Region2["Oceania"] = "Oceania";
10
+ Region2["Other"] = "Other";
11
+ return Region2;
12
+ })(Region || {});
13
+
14
+ // src/accounts.ts
15
+ import { PublicKey as PublicKey2 } from "@solana/web3.js";
16
+ import "bn.js";
17
+ var PROGRAM_ID = new PublicKey2(
18
+ "2paXpX8Ze3tvYezviSwQJSSihG3LbrDiD7SNsaFwgTow"
19
+ );
20
+ var STALE_SLOTS = 150n;
21
+ var SUPPORTED_REGISTRY_VERSION = 1;
22
+ function getRegistryPDA(programId = PROGRAM_ID) {
23
+ return PublicKey2.findProgramAddressSync([Buffer.from("registry")], programId);
24
+ }
25
+ function getNetworkHealthPDA(programId = PROGRAM_ID) {
26
+ return PublicKey2.findProgramAddressSync(
27
+ [Buffer.from("network_health")],
28
+ programId
29
+ );
30
+ }
31
+ function getObserverPDA(observer, programId = PROGRAM_ID) {
32
+ return PublicKey2.findProgramAddressSync(
33
+ [Buffer.from("observer"), observer.toBuffer()],
34
+ programId
35
+ );
36
+ }
37
+ function toBigInt(val) {
38
+ if (typeof val === "bigint") return val;
39
+ if (typeof val === "number") return BigInt(val);
40
+ return BigInt(val.toString());
41
+ }
42
+ function toRegion(val) {
43
+ const key = typeof val === "string" ? val : Object.keys(val)[0] ?? "other";
44
+ const normalized = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
45
+ const map = {
46
+ Asia: "Asia",
47
+ Us: "US",
48
+ Eu: "EU",
49
+ Southamerica: "SouthAmerica",
50
+ Africa: "Africa",
51
+ Oceania: "Oceania",
52
+ Other: "Other"
53
+ };
54
+ return map[normalized] ?? "Other";
55
+ }
56
+ function decodeAttestation(raw) {
57
+ return {
58
+ slot: toBigInt(raw["slot"]),
59
+ timestamp: toBigInt(raw["timestamp"]),
60
+ avgRttUs: raw["avgRttUs"],
61
+ p95RttUs: raw["p95RttUs"],
62
+ slotLatencyMs: raw["slotLatencyMs"],
63
+ tpuReachable: raw["tpuReachable"],
64
+ tpuProbed: raw["tpuProbed"],
65
+ agaveCount: raw["agaveCount"],
66
+ firedancerCount: raw["firedancerCount"],
67
+ jitoCount: raw["jitoCount"],
68
+ solanaLabsCount: raw["solanaLabsCount"],
69
+ otherCount: raw["otherCount"],
70
+ reachableStakePct: raw["reachableStakePct"]
71
+ };
72
+ }
73
+ function decodeRegionScore(raw) {
74
+ return {
75
+ region: toRegion(raw["region"]),
76
+ observerCount: raw["observerCount"],
77
+ healthScore: raw["healthScore"],
78
+ reachabilityPct: raw["reachabilityPct"],
79
+ avgRttUs: raw["avgRttUs"],
80
+ slotLatencyMs: raw["slotLatencyMs"],
81
+ lastUpdatedSlot: toBigInt(raw["lastUpdatedSlot"]),
82
+ agaveCount: raw["agaveCount"],
83
+ firedancerCount: raw["firedancerCount"],
84
+ jitoCount: raw["jitoCount"],
85
+ solanaLabsCount: raw["solanaLabsCount"],
86
+ otherCount: raw["otherCount"],
87
+ reachableStakePct: raw["reachableStakePct"]
88
+ };
89
+ }
90
+ function decodeNetworkHealth(raw) {
91
+ const minRaw = raw["minHealthEver"];
92
+ return {
93
+ healthScore: raw["healthScore"],
94
+ tpuReachabilityPct: raw["tpuReachabilityPct"],
95
+ avgSlotLatencyMs: raw["avgSlotLatencyMs"],
96
+ activeObserverCount: raw["activeObserverCount"],
97
+ activeRegionCount: raw["activeRegionCount"],
98
+ lastUpdatedSlot: toBigInt(raw["lastUpdatedSlot"]),
99
+ lastUpdatedTs: toBigInt(raw["lastUpdatedTs"]),
100
+ minHealthEver: minRaw === 255 ? null : minRaw,
101
+ maxHealthEver: raw["maxHealthEver"],
102
+ totalAttestations: toBigInt(raw["totalAttestations"]),
103
+ regionScores: raw["regionScores"].map(
104
+ decodeRegionScore
105
+ ),
106
+ agaveCount: raw["agaveCount"],
107
+ firedancerCount: raw["firedancerCount"],
108
+ jitoCount: raw["jitoCount"],
109
+ solanaLabsCount: raw["solanaLabsCount"],
110
+ otherCount: raw["otherCount"]
111
+ };
112
+ }
113
+ function decodeObserver(raw, publicKey) {
114
+ return {
115
+ publicKey,
116
+ authority: raw["authority"],
117
+ region: toRegion(raw["region"]),
118
+ stakeLamports: toBigInt(raw["stakeLamports"]),
119
+ registeredAt: toBigInt(raw["registeredAt"]),
120
+ lastAttestationSlot: toBigInt(raw["lastAttestationSlot"]),
121
+ attestationCount: toBigInt(raw["attestationCount"]),
122
+ latestAttestation: decodeAttestation(
123
+ raw["latestAttestation"]
124
+ ),
125
+ isActive: raw["isActive"]
126
+ };
127
+ }
128
+ function decodeRegistry(raw) {
129
+ const version = raw["version"];
130
+ if (version !== SUPPORTED_REGISTRY_VERSION) {
131
+ console.warn(
132
+ `s0racle-sdk: registry version ${version} is not supported by this SDK (expected ${SUPPORTED_REGISTRY_VERSION}). Upgrade s0racle-sdk to a compatible version.`
133
+ );
134
+ }
135
+ const pending = raw["pendingAuthority"];
136
+ return {
137
+ authority: raw["authority"],
138
+ pendingAuthority: pending ?? null,
139
+ minStakeLamports: toBigInt(raw["minStakeLamports"]),
140
+ observerCount: raw["observerCount"],
141
+ activeCount: raw["activeCount"],
142
+ maxObservers: raw["maxObservers"],
143
+ paused: raw["paused"],
144
+ version: raw["version"]
145
+ };
146
+ }
147
+
148
+ // src/client.ts
149
+ import {
150
+ AnchorProvider,
151
+ Program as Program2
152
+ } from "@coral-xyz/anchor";
153
+ import {
154
+ Keypair,
155
+ PublicKey as PublicKey5
156
+ } from "@solana/web3.js";
157
+
158
+ // src/idl/s0racle_program.json
159
+ var s0racle_program_default = {
160
+ address: "2paXpX8Ze3tvYezviSwQJSSihG3LbrDiD7SNsaFwgTow",
161
+ metadata: {
162
+ name: "s0racle_program",
163
+ version: "0.1.0",
164
+ spec: "0.1.0",
165
+ description: "Created with Anchor"
166
+ },
167
+ instructions: [
168
+ {
169
+ name: "accept_authority",
170
+ discriminator: [
171
+ 107,
172
+ 86,
173
+ 198,
174
+ 91,
175
+ 33,
176
+ 12,
177
+ 107,
178
+ 160
179
+ ],
180
+ accounts: [
181
+ {
182
+ name: "registry",
183
+ writable: true
184
+ },
185
+ {
186
+ name: "new_authority",
187
+ signer: true
188
+ }
189
+ ],
190
+ args: []
191
+ },
192
+ {
193
+ name: "crank_aggregation",
194
+ discriminator: [
195
+ 37,
196
+ 116,
197
+ 43,
198
+ 137,
199
+ 4,
200
+ 13,
201
+ 88,
202
+ 26
203
+ ],
204
+ accounts: [
205
+ {
206
+ name: "cranker",
207
+ signer: true
208
+ },
209
+ {
210
+ name: "network_health",
211
+ writable: true,
212
+ pda: {
213
+ seeds: [
214
+ {
215
+ kind: "const",
216
+ value: [
217
+ 110,
218
+ 101,
219
+ 116,
220
+ 119,
221
+ 111,
222
+ 114,
223
+ 107,
224
+ 95,
225
+ 104,
226
+ 101,
227
+ 97,
228
+ 108,
229
+ 116,
230
+ 104
231
+ ]
232
+ }
233
+ ]
234
+ }
235
+ },
236
+ {
237
+ name: "registry_account",
238
+ pda: {
239
+ seeds: [
240
+ {
241
+ kind: "const",
242
+ value: [
243
+ 114,
244
+ 101,
245
+ 103,
246
+ 105,
247
+ 115,
248
+ 116,
249
+ 114,
250
+ 121
251
+ ]
252
+ }
253
+ ]
254
+ }
255
+ },
256
+ {
257
+ name: "clock",
258
+ address: "SysvarC1ock11111111111111111111111111111111"
259
+ }
260
+ ],
261
+ args: []
262
+ },
263
+ {
264
+ name: "deregister_observer",
265
+ discriminator: [
266
+ 16,
267
+ 4,
268
+ 225,
269
+ 8,
270
+ 113,
271
+ 188,
272
+ 135,
273
+ 196
274
+ ],
275
+ accounts: [
276
+ {
277
+ name: "caller",
278
+ writable: true,
279
+ signer: true
280
+ },
281
+ {
282
+ name: "observer_wallet",
283
+ docs: [
284
+ "Recipient of returned stake lamports on deregistration."
285
+ ],
286
+ writable: true
287
+ },
288
+ {
289
+ name: "observer_account",
290
+ writable: true,
291
+ pda: {
292
+ seeds: [
293
+ {
294
+ kind: "const",
295
+ value: [
296
+ 111,
297
+ 98,
298
+ 115,
299
+ 101,
300
+ 114,
301
+ 118,
302
+ 101,
303
+ 114
304
+ ]
305
+ },
306
+ {
307
+ kind: "account",
308
+ path: "observer_wallet"
309
+ }
310
+ ]
311
+ }
312
+ },
313
+ {
314
+ name: "registry",
315
+ writable: true,
316
+ pda: {
317
+ seeds: [
318
+ {
319
+ kind: "const",
320
+ value: [
321
+ 114,
322
+ 101,
323
+ 103,
324
+ 105,
325
+ 115,
326
+ 116,
327
+ 114,
328
+ 121
329
+ ]
330
+ }
331
+ ]
332
+ }
333
+ },
334
+ {
335
+ name: "system_program",
336
+ address: "11111111111111111111111111111111"
337
+ }
338
+ ],
339
+ args: []
340
+ },
341
+ {
342
+ name: "initialize",
343
+ discriminator: [
344
+ 175,
345
+ 175,
346
+ 109,
347
+ 31,
348
+ 13,
349
+ 152,
350
+ 155,
351
+ 237
352
+ ],
353
+ accounts: [
354
+ {
355
+ name: "authority",
356
+ writable: true,
357
+ signer: true
358
+ },
359
+ {
360
+ name: "registry",
361
+ writable: true,
362
+ pda: {
363
+ seeds: [
364
+ {
365
+ kind: "const",
366
+ value: [
367
+ 114,
368
+ 101,
369
+ 103,
370
+ 105,
371
+ 115,
372
+ 116,
373
+ 114,
374
+ 121
375
+ ]
376
+ }
377
+ ]
378
+ }
379
+ },
380
+ {
381
+ name: "network_health",
382
+ writable: true,
383
+ pda: {
384
+ seeds: [
385
+ {
386
+ kind: "const",
387
+ value: [
388
+ 110,
389
+ 101,
390
+ 116,
391
+ 119,
392
+ 111,
393
+ 114,
394
+ 107,
395
+ 95,
396
+ 104,
397
+ 101,
398
+ 97,
399
+ 108,
400
+ 116,
401
+ 104
402
+ ]
403
+ }
404
+ ]
405
+ }
406
+ },
407
+ {
408
+ name: "system_program",
409
+ address: "11111111111111111111111111111111"
410
+ }
411
+ ],
412
+ args: [
413
+ {
414
+ name: "min_stake_lamports",
415
+ type: "u64"
416
+ },
417
+ {
418
+ name: "max_observers",
419
+ type: "u16"
420
+ }
421
+ ]
422
+ },
423
+ {
424
+ name: "propose_authority",
425
+ discriminator: [
426
+ 20,
427
+ 148,
428
+ 236,
429
+ 198,
430
+ 76,
431
+ 119,
432
+ 99,
433
+ 142
434
+ ],
435
+ accounts: [
436
+ {
437
+ name: "registry",
438
+ writable: true,
439
+ pda: {
440
+ seeds: [
441
+ {
442
+ kind: "const",
443
+ value: [
444
+ 114,
445
+ 101,
446
+ 103,
447
+ 105,
448
+ 115,
449
+ 116,
450
+ 114,
451
+ 121
452
+ ]
453
+ }
454
+ ]
455
+ }
456
+ },
457
+ {
458
+ name: "authority",
459
+ signer: true,
460
+ relations: [
461
+ "registry"
462
+ ]
463
+ }
464
+ ],
465
+ args: [
466
+ {
467
+ name: "new_authority",
468
+ type: "pubkey"
469
+ }
470
+ ]
471
+ },
472
+ {
473
+ name: "register_observer",
474
+ discriminator: [
475
+ 95,
476
+ 238,
477
+ 80,
478
+ 77,
479
+ 247,
480
+ 96,
481
+ 2,
482
+ 225
483
+ ],
484
+ accounts: [
485
+ {
486
+ name: "observer",
487
+ writable: true,
488
+ signer: true
489
+ },
490
+ {
491
+ name: "observer_account",
492
+ writable: true,
493
+ pda: {
494
+ seeds: [
495
+ {
496
+ kind: "const",
497
+ value: [
498
+ 111,
499
+ 98,
500
+ 115,
501
+ 101,
502
+ 114,
503
+ 118,
504
+ 101,
505
+ 114
506
+ ]
507
+ },
508
+ {
509
+ kind: "account",
510
+ path: "observer"
511
+ }
512
+ ]
513
+ }
514
+ },
515
+ {
516
+ name: "registry",
517
+ writable: true,
518
+ pda: {
519
+ seeds: [
520
+ {
521
+ kind: "const",
522
+ value: [
523
+ 114,
524
+ 101,
525
+ 103,
526
+ 105,
527
+ 115,
528
+ 116,
529
+ 114,
530
+ 121
531
+ ]
532
+ }
533
+ ]
534
+ }
535
+ },
536
+ {
537
+ name: "system_program",
538
+ address: "11111111111111111111111111111111"
539
+ }
540
+ ],
541
+ args: [
542
+ {
543
+ name: "region",
544
+ type: {
545
+ defined: {
546
+ name: "Region"
547
+ }
548
+ }
549
+ }
550
+ ]
551
+ },
552
+ {
553
+ name: "slash_observer",
554
+ discriminator: [
555
+ 203,
556
+ 50,
557
+ 98,
558
+ 246,
559
+ 173,
560
+ 53,
561
+ 118,
562
+ 177
563
+ ],
564
+ accounts: [
565
+ {
566
+ name: "authority",
567
+ signer: true,
568
+ relations: [
569
+ "registry"
570
+ ]
571
+ },
572
+ {
573
+ name: "observer_wallet"
574
+ },
575
+ {
576
+ name: "observer_account",
577
+ writable: true,
578
+ pda: {
579
+ seeds: [
580
+ {
581
+ kind: "const",
582
+ value: [
583
+ 111,
584
+ 98,
585
+ 115,
586
+ 101,
587
+ 114,
588
+ 118,
589
+ 101,
590
+ 114
591
+ ]
592
+ },
593
+ {
594
+ kind: "account",
595
+ path: "observer_wallet"
596
+ }
597
+ ]
598
+ }
599
+ },
600
+ {
601
+ name: "registry",
602
+ writable: true,
603
+ pda: {
604
+ seeds: [
605
+ {
606
+ kind: "const",
607
+ value: [
608
+ 114,
609
+ 101,
610
+ 103,
611
+ 105,
612
+ 115,
613
+ 116,
614
+ 114,
615
+ 121
616
+ ]
617
+ }
618
+ ]
619
+ }
620
+ },
621
+ {
622
+ name: "treasury",
623
+ writable: true
624
+ }
625
+ ],
626
+ args: [
627
+ {
628
+ name: "slash_bps",
629
+ type: "u16"
630
+ }
631
+ ]
632
+ },
633
+ {
634
+ name: "submit_attestation",
635
+ discriminator: [
636
+ 238,
637
+ 220,
638
+ 255,
639
+ 105,
640
+ 183,
641
+ 211,
642
+ 40,
643
+ 83
644
+ ],
645
+ accounts: [
646
+ {
647
+ name: "authority",
648
+ writable: true,
649
+ signer: true,
650
+ relations: [
651
+ "observer_account"
652
+ ]
653
+ },
654
+ {
655
+ name: "observer_account",
656
+ writable: true,
657
+ pda: {
658
+ seeds: [
659
+ {
660
+ kind: "const",
661
+ value: [
662
+ 111,
663
+ 98,
664
+ 115,
665
+ 101,
666
+ 114,
667
+ 118,
668
+ 101,
669
+ 114
670
+ ]
671
+ },
672
+ {
673
+ kind: "account",
674
+ path: "authority"
675
+ }
676
+ ]
677
+ }
678
+ },
679
+ {
680
+ name: "network_health",
681
+ writable: true,
682
+ pda: {
683
+ seeds: [
684
+ {
685
+ kind: "const",
686
+ value: [
687
+ 110,
688
+ 101,
689
+ 116,
690
+ 119,
691
+ 111,
692
+ 114,
693
+ 107,
694
+ 95,
695
+ 104,
696
+ 101,
697
+ 97,
698
+ 108,
699
+ 116,
700
+ 104
701
+ ]
702
+ }
703
+ ]
704
+ }
705
+ },
706
+ {
707
+ name: "registry",
708
+ pda: {
709
+ seeds: [
710
+ {
711
+ kind: "const",
712
+ value: [
713
+ 114,
714
+ 101,
715
+ 103,
716
+ 105,
717
+ 115,
718
+ 116,
719
+ 114,
720
+ 121
721
+ ]
722
+ }
723
+ ]
724
+ }
725
+ },
726
+ {
727
+ name: "clock",
728
+ address: "SysvarC1ock11111111111111111111111111111111"
729
+ }
730
+ ],
731
+ args: [
732
+ {
733
+ name: "tpu_reachable",
734
+ type: "u16"
735
+ },
736
+ {
737
+ name: "tpu_probed",
738
+ type: "u16"
739
+ },
740
+ {
741
+ name: "avg_rtt_us",
742
+ type: "u32"
743
+ },
744
+ {
745
+ name: "p95_rtt_us",
746
+ type: "u32"
747
+ },
748
+ {
749
+ name: "slot_latency_ms",
750
+ type: "u32"
751
+ },
752
+ {
753
+ name: "agave_count",
754
+ type: "u16"
755
+ },
756
+ {
757
+ name: "firedancer_count",
758
+ type: "u16"
759
+ },
760
+ {
761
+ name: "jito_count",
762
+ type: "u16"
763
+ },
764
+ {
765
+ name: "solana_labs_count",
766
+ type: "u16"
767
+ },
768
+ {
769
+ name: "other_count",
770
+ type: "u16"
771
+ },
772
+ {
773
+ name: "reachable_stake_pct",
774
+ type: "u8"
775
+ }
776
+ ]
777
+ },
778
+ {
779
+ name: "update_config",
780
+ discriminator: [
781
+ 29,
782
+ 158,
783
+ 252,
784
+ 191,
785
+ 10,
786
+ 83,
787
+ 219,
788
+ 99
789
+ ],
790
+ accounts: [
791
+ {
792
+ name: "authority",
793
+ signer: true,
794
+ relations: [
795
+ "registry"
796
+ ]
797
+ },
798
+ {
799
+ name: "registry",
800
+ writable: true,
801
+ pda: {
802
+ seeds: [
803
+ {
804
+ kind: "const",
805
+ value: [
806
+ 114,
807
+ 101,
808
+ 103,
809
+ 105,
810
+ 115,
811
+ 116,
812
+ 114,
813
+ 121
814
+ ]
815
+ }
816
+ ]
817
+ }
818
+ }
819
+ ],
820
+ args: [
821
+ {
822
+ name: "min_stake_lamports",
823
+ type: {
824
+ option: "u64"
825
+ }
826
+ },
827
+ {
828
+ name: "max_observers",
829
+ type: {
830
+ option: "u16"
831
+ }
832
+ },
833
+ {
834
+ name: "paused",
835
+ type: {
836
+ option: "bool"
837
+ }
838
+ }
839
+ ]
840
+ }
841
+ ],
842
+ accounts: [
843
+ {
844
+ name: "NetworkHealthAccount",
845
+ discriminator: [
846
+ 90,
847
+ 218,
848
+ 55,
849
+ 105,
850
+ 239,
851
+ 131,
852
+ 43,
853
+ 68
854
+ ]
855
+ },
856
+ {
857
+ name: "ObserverAccount",
858
+ discriminator: [
859
+ 119,
860
+ 24,
861
+ 204,
862
+ 152,
863
+ 164,
864
+ 169,
865
+ 5,
866
+ 101
867
+ ]
868
+ },
869
+ {
870
+ name: "RegistryAccount",
871
+ discriminator: [
872
+ 113,
873
+ 93,
874
+ 106,
875
+ 201,
876
+ 100,
877
+ 166,
878
+ 146,
879
+ 98
880
+ ]
881
+ }
882
+ ],
883
+ events: [
884
+ {
885
+ name: "AttestationSubmitted",
886
+ discriminator: [
887
+ 177,
888
+ 213,
889
+ 117,
890
+ 225,
891
+ 166,
892
+ 11,
893
+ 54,
894
+ 218
895
+ ]
896
+ },
897
+ {
898
+ name: "ConfigUpdated",
899
+ discriminator: [
900
+ 40,
901
+ 241,
902
+ 230,
903
+ 122,
904
+ 11,
905
+ 19,
906
+ 198,
907
+ 194
908
+ ]
909
+ },
910
+ {
911
+ name: "ObserverDeregistered",
912
+ discriminator: [
913
+ 78,
914
+ 251,
915
+ 104,
916
+ 71,
917
+ 5,
918
+ 145,
919
+ 253,
920
+ 95
921
+ ]
922
+ },
923
+ {
924
+ name: "ObserverRegistered",
925
+ discriminator: [
926
+ 33,
927
+ 248,
928
+ 190,
929
+ 137,
930
+ 191,
931
+ 38,
932
+ 49,
933
+ 56
934
+ ]
935
+ },
936
+ {
937
+ name: "ObserverSlashed",
938
+ discriminator: [
939
+ 132,
940
+ 0,
941
+ 84,
942
+ 231,
943
+ 39,
944
+ 179,
945
+ 32,
946
+ 211
947
+ ]
948
+ }
949
+ ],
950
+ errors: [
951
+ {
952
+ code: 6e3,
953
+ name: "ValueCannotBeZero",
954
+ msg: "Value cannot be zero"
955
+ },
956
+ {
957
+ code: 6001,
958
+ name: "RegistryPaused",
959
+ msg: "Registry is currently paused"
960
+ },
961
+ {
962
+ code: 6002,
963
+ name: "MaxObserversReached",
964
+ msg: "Maximum number of observers has been reached"
965
+ },
966
+ {
967
+ code: 6003,
968
+ name: "InsufficientLamports",
969
+ msg: "Insufficient Lamports"
970
+ },
971
+ {
972
+ code: 6004,
973
+ name: "UnauthorizedObserver",
974
+ msg: "Unauthorized Observer"
975
+ },
976
+ {
977
+ code: 6005,
978
+ name: "ObserverNotActive",
979
+ msg: "Observer is not active"
980
+ },
981
+ {
982
+ code: 6006,
983
+ name: "ZeroValidatorsProbed",
984
+ msg: "Zero validators probed"
985
+ },
986
+ {
987
+ code: 6007,
988
+ name: "InsufficientValidatorsProbed",
989
+ msg: "Insufficient validators probed"
990
+ },
991
+ {
992
+ code: 6008,
993
+ name: "InvalidReachabilityCount",
994
+ msg: "Invalid reachability count"
995
+ },
996
+ {
997
+ code: 6009,
998
+ name: "InvalidLatencyValue",
999
+ msg: "Invalid latency submitted"
1000
+ },
1001
+ {
1002
+ code: 6010,
1003
+ name: "StaleAttestation",
1004
+ msg: "Stale attestation"
1005
+ },
1006
+ {
1007
+ code: 6011,
1008
+ name: "NoActiveObservers",
1009
+ msg: "No active observers"
1010
+ },
1011
+ {
1012
+ code: 6012,
1013
+ name: "ObserverAlreadyInActive",
1014
+ msg: "Observer already inactive"
1015
+ },
1016
+ {
1017
+ code: 6013,
1018
+ name: "UnAuthorizedCaller",
1019
+ msg: "Unauthorized caller"
1020
+ },
1021
+ {
1022
+ code: 6014,
1023
+ name: "InsufficientBalanceForRefund",
1024
+ msg: "Insufficient balance in PDA for stake refund"
1025
+ },
1026
+ {
1027
+ code: 6015,
1028
+ name: "InvalidSlashBps",
1029
+ msg: "Invalid slash basis points - must be <= 10000"
1030
+ },
1031
+ {
1032
+ code: 6016,
1033
+ name: "ObserverNotFound",
1034
+ msg: "Observer not found"
1035
+ },
1036
+ {
1037
+ code: 6017,
1038
+ name: "InsufficientBalanceForSlash",
1039
+ msg: "Insufficient balance in PDA for slash"
1040
+ },
1041
+ {
1042
+ code: 6018,
1043
+ name: "InvalidPendingAuthority",
1044
+ msg: "Invalid or no pending authority for registry"
1045
+ },
1046
+ {
1047
+ code: 6019,
1048
+ name: "MaxObserversCannotBeLessThanActiveObservers",
1049
+ msg: "Max observers cannot be less than active observers"
1050
+ }
1051
+ ],
1052
+ types: [
1053
+ {
1054
+ name: "Attestation",
1055
+ docs: [
1056
+ "Single 10-second measurement from one observer node"
1057
+ ],
1058
+ type: {
1059
+ kind: "struct",
1060
+ fields: [
1061
+ {
1062
+ name: "slot",
1063
+ docs: [
1064
+ "Solana slot this measurement covers"
1065
+ ],
1066
+ type: "u64"
1067
+ },
1068
+ {
1069
+ name: "timestamp",
1070
+ docs: [
1071
+ "Timestamp of the measurement"
1072
+ ],
1073
+ type: "i64"
1074
+ },
1075
+ {
1076
+ name: "avg_rtt_us",
1077
+ docs: [
1078
+ "Average RTT of the QUIC probe"
1079
+ ],
1080
+ type: "u32"
1081
+ },
1082
+ {
1083
+ name: "p95_rtt_us",
1084
+ docs: [
1085
+ "P95 RTT of the QUIC probe"
1086
+ ],
1087
+ type: "u32"
1088
+ },
1089
+ {
1090
+ name: "slot_latency_ms",
1091
+ docs: [
1092
+ "Slot latency of the QUIC probe"
1093
+ ],
1094
+ type: "u32"
1095
+ },
1096
+ {
1097
+ name: "tpu_reachable",
1098
+ docs: [
1099
+ "Validators reachable via QUIC probe"
1100
+ ],
1101
+ type: "u16"
1102
+ },
1103
+ {
1104
+ name: "tpu_probed",
1105
+ docs: [
1106
+ "Total validators probed this round"
1107
+ ],
1108
+ type: "u16"
1109
+ },
1110
+ {
1111
+ name: "agave_count",
1112
+ docs: [
1113
+ "Client distributions"
1114
+ ],
1115
+ type: "u16"
1116
+ },
1117
+ {
1118
+ name: "firedancer_count",
1119
+ type: "u16"
1120
+ },
1121
+ {
1122
+ name: "jito_count",
1123
+ type: "u16"
1124
+ },
1125
+ {
1126
+ name: "solana_labs_count",
1127
+ type: "u16"
1128
+ },
1129
+ {
1130
+ name: "other_count",
1131
+ type: "u16"
1132
+ },
1133
+ {
1134
+ name: "reachable_stake_pct",
1135
+ docs: [
1136
+ "% of total stake (by lamports) reachable via QUIC probe"
1137
+ ],
1138
+ type: "u8"
1139
+ }
1140
+ ]
1141
+ }
1142
+ },
1143
+ {
1144
+ name: "AttestationSubmitted",
1145
+ type: {
1146
+ kind: "struct",
1147
+ fields: [
1148
+ {
1149
+ name: "observer",
1150
+ type: "pubkey"
1151
+ },
1152
+ {
1153
+ name: "region",
1154
+ type: {
1155
+ defined: {
1156
+ name: "Region"
1157
+ }
1158
+ }
1159
+ },
1160
+ {
1161
+ name: "score",
1162
+ type: "u8"
1163
+ },
1164
+ {
1165
+ name: "reachability_pct",
1166
+ type: "u8"
1167
+ },
1168
+ {
1169
+ name: "slot_latency_ms",
1170
+ type: "u32"
1171
+ },
1172
+ {
1173
+ name: "slot",
1174
+ type: "u64"
1175
+ },
1176
+ {
1177
+ name: "agave_count",
1178
+ type: "u16"
1179
+ },
1180
+ {
1181
+ name: "firedancer_count",
1182
+ type: "u16"
1183
+ },
1184
+ {
1185
+ name: "jito_count",
1186
+ type: "u16"
1187
+ },
1188
+ {
1189
+ name: "solana_labs_count",
1190
+ type: "u16"
1191
+ },
1192
+ {
1193
+ name: "other_count",
1194
+ type: "u16"
1195
+ },
1196
+ {
1197
+ name: "reachable_stake_pct",
1198
+ type: "u8"
1199
+ }
1200
+ ]
1201
+ }
1202
+ },
1203
+ {
1204
+ name: "ConfigUpdated",
1205
+ type: {
1206
+ kind: "struct",
1207
+ fields: [
1208
+ {
1209
+ name: "min_stake_lamports",
1210
+ type: {
1211
+ option: "u64"
1212
+ }
1213
+ },
1214
+ {
1215
+ name: "max_observers",
1216
+ type: {
1217
+ option: "u16"
1218
+ }
1219
+ },
1220
+ {
1221
+ name: "paused",
1222
+ type: {
1223
+ option: "bool"
1224
+ }
1225
+ }
1226
+ ]
1227
+ }
1228
+ },
1229
+ {
1230
+ name: "NetworkHealthAccount",
1231
+ docs: [
1232
+ "Global oracle account - the single source of truth for dApps and UI reads"
1233
+ ],
1234
+ type: {
1235
+ kind: "struct",
1236
+ fields: [
1237
+ {
1238
+ name: "health_score",
1239
+ docs: [
1240
+ "The health score of the network"
1241
+ ],
1242
+ type: "u8"
1243
+ },
1244
+ {
1245
+ name: "tpu_reachability_pct",
1246
+ docs: [
1247
+ "TPU reachability % averaged across all regions"
1248
+ ],
1249
+ type: "u8"
1250
+ },
1251
+ {
1252
+ name: "avg_slot_latency_ms",
1253
+ docs: [
1254
+ "Average slot latency in milliseconds"
1255
+ ],
1256
+ type: "u32"
1257
+ },
1258
+ {
1259
+ name: "active_observer_count",
1260
+ docs: [
1261
+ "Number of active observers that contributed to this score"
1262
+ ],
1263
+ type: "u16"
1264
+ },
1265
+ {
1266
+ name: "active_region_count",
1267
+ docs: [
1268
+ "Number of regions with fresh attestations"
1269
+ ],
1270
+ type: "u16"
1271
+ },
1272
+ {
1273
+ name: "last_updated_slot",
1274
+ docs: [
1275
+ "Slot of last aggregation \u2014 dApps check this for staleness"
1276
+ ],
1277
+ type: "u64"
1278
+ },
1279
+ {
1280
+ name: "last_updated_ts",
1281
+ docs: [
1282
+ "Unix timestamp of last update"
1283
+ ],
1284
+ type: "i64"
1285
+ },
1286
+ {
1287
+ name: "min_health_ever",
1288
+ docs: [
1289
+ "Lowest health score ever recorded",
1290
+ 'Note: Initialized to 255 (u8::MAX) which represents "no data yet".'
1291
+ ],
1292
+ type: "u8"
1293
+ },
1294
+ {
1295
+ name: "max_health_ever",
1296
+ docs: [
1297
+ "Highest health score ever recorded"
1298
+ ],
1299
+ type: "u8"
1300
+ },
1301
+ {
1302
+ name: "total_attestations",
1303
+ docs: [
1304
+ "Total attestations ever submitted across all observers"
1305
+ ],
1306
+ type: "u64"
1307
+ },
1308
+ {
1309
+ name: "region_scores",
1310
+ docs: [
1311
+ "One entry per region"
1312
+ ],
1313
+ type: {
1314
+ array: [
1315
+ {
1316
+ defined: {
1317
+ name: "RegionScore"
1318
+ }
1319
+ },
1320
+ 7
1321
+ ]
1322
+ }
1323
+ },
1324
+ {
1325
+ name: "agave_count",
1326
+ type: "u8"
1327
+ },
1328
+ {
1329
+ name: "firedancer_count",
1330
+ type: "u8"
1331
+ },
1332
+ {
1333
+ name: "jito_count",
1334
+ type: "u8"
1335
+ },
1336
+ {
1337
+ name: "solana_labs_count",
1338
+ type: "u8"
1339
+ },
1340
+ {
1341
+ name: "other_count",
1342
+ type: "u8"
1343
+ },
1344
+ {
1345
+ name: "bump",
1346
+ docs: [
1347
+ "PDA bump seed"
1348
+ ],
1349
+ type: "u8"
1350
+ }
1351
+ ]
1352
+ }
1353
+ },
1354
+ {
1355
+ name: "ObserverAccount",
1356
+ docs: [
1357
+ "Per-observer state - stores identity, region, stake and latest measurement"
1358
+ ],
1359
+ type: {
1360
+ kind: "struct",
1361
+ fields: [
1362
+ {
1363
+ name: "authority",
1364
+ docs: [
1365
+ "The authority of the observer"
1366
+ ],
1367
+ type: "pubkey"
1368
+ },
1369
+ {
1370
+ name: "region",
1371
+ docs: [
1372
+ "The region of the observer"
1373
+ ],
1374
+ type: {
1375
+ defined: {
1376
+ name: "Region"
1377
+ }
1378
+ }
1379
+ },
1380
+ {
1381
+ name: "stake_lamports",
1382
+ docs: [
1383
+ "The stake of the observer"
1384
+ ],
1385
+ type: "u64"
1386
+ },
1387
+ {
1388
+ name: "registered_at",
1389
+ docs: [
1390
+ "The timestamp when the observer was registered"
1391
+ ],
1392
+ type: "i64"
1393
+ },
1394
+ {
1395
+ name: "last_attestation_slot",
1396
+ docs: [
1397
+ "Solana slot of the most recent attestation submitted",
1398
+ "Used for staleness check in crank_aggregation"
1399
+ ],
1400
+ type: "u64"
1401
+ },
1402
+ {
1403
+ name: "attestation_count",
1404
+ docs: [
1405
+ "The number of attestations submitted by the observer"
1406
+ ],
1407
+ type: "u64"
1408
+ },
1409
+ {
1410
+ name: "latest_attestation",
1411
+ docs: [
1412
+ "The latest attestation submitted by the observer"
1413
+ ],
1414
+ type: {
1415
+ defined: {
1416
+ name: "Attestation"
1417
+ }
1418
+ }
1419
+ },
1420
+ {
1421
+ name: "is_active",
1422
+ docs: [
1423
+ "Whether the observer is active"
1424
+ ],
1425
+ type: "bool"
1426
+ },
1427
+ {
1428
+ name: "bump",
1429
+ docs: [
1430
+ "The bump seed for the PDA"
1431
+ ],
1432
+ type: "u8"
1433
+ }
1434
+ ]
1435
+ }
1436
+ },
1437
+ {
1438
+ name: "ObserverDeregistered",
1439
+ type: {
1440
+ kind: "struct",
1441
+ fields: [
1442
+ {
1443
+ name: "observer",
1444
+ type: "pubkey"
1445
+ }
1446
+ ]
1447
+ }
1448
+ },
1449
+ {
1450
+ name: "ObserverRegistered",
1451
+ type: {
1452
+ kind: "struct",
1453
+ fields: [
1454
+ {
1455
+ name: "observer",
1456
+ type: "pubkey"
1457
+ },
1458
+ {
1459
+ name: "region",
1460
+ type: {
1461
+ defined: {
1462
+ name: "Region"
1463
+ }
1464
+ }
1465
+ },
1466
+ {
1467
+ name: "stake_lamports",
1468
+ type: "u64"
1469
+ }
1470
+ ]
1471
+ }
1472
+ },
1473
+ {
1474
+ name: "ObserverSlashed",
1475
+ type: {
1476
+ kind: "struct",
1477
+ fields: [
1478
+ {
1479
+ name: "observer",
1480
+ type: "pubkey"
1481
+ },
1482
+ {
1483
+ name: "slash_bps",
1484
+ type: "u16"
1485
+ },
1486
+ {
1487
+ name: "amount_slashed",
1488
+ type: "u64"
1489
+ }
1490
+ ]
1491
+ }
1492
+ },
1493
+ {
1494
+ name: "Region",
1495
+ docs: [
1496
+ "Geographic region of an observer node - serializes as u8 on-chain"
1497
+ ],
1498
+ type: {
1499
+ kind: "enum",
1500
+ variants: [
1501
+ {
1502
+ name: "Asia"
1503
+ },
1504
+ {
1505
+ name: "US"
1506
+ },
1507
+ {
1508
+ name: "EU"
1509
+ },
1510
+ {
1511
+ name: "SouthAmerica"
1512
+ },
1513
+ {
1514
+ name: "Africa"
1515
+ },
1516
+ {
1517
+ name: "Oceania"
1518
+ },
1519
+ {
1520
+ name: "Other"
1521
+ }
1522
+ ]
1523
+ }
1524
+ },
1525
+ {
1526
+ name: "RegionScore",
1527
+ docs: [
1528
+ "Health snapshot for one geographic region - embedded in NetworkHealthAccount"
1529
+ ],
1530
+ type: {
1531
+ kind: "struct",
1532
+ fields: [
1533
+ {
1534
+ name: "region",
1535
+ docs: [
1536
+ "Which region this entry represents"
1537
+ ],
1538
+ type: {
1539
+ defined: {
1540
+ name: "Region"
1541
+ }
1542
+ }
1543
+ },
1544
+ {
1545
+ name: "observer_count",
1546
+ docs: [
1547
+ "Number of observer contributions currently represented in this region aggregate"
1548
+ ],
1549
+ type: "u16"
1550
+ },
1551
+ {
1552
+ name: "health_score",
1553
+ docs: [
1554
+ "Health score from this region"
1555
+ ],
1556
+ type: "u8"
1557
+ },
1558
+ {
1559
+ name: "reachability_pct",
1560
+ docs: [
1561
+ "TPU reachability % from this region"
1562
+ ],
1563
+ type: "u8"
1564
+ },
1565
+ {
1566
+ name: "avg_rtt_us",
1567
+ docs: [
1568
+ "Average RTT from this region in microseconds"
1569
+ ],
1570
+ type: "u32"
1571
+ },
1572
+ {
1573
+ name: "slot_latency_ms",
1574
+ docs: [
1575
+ "Slot propagation latency from this region (ms)"
1576
+ ],
1577
+ type: "u32"
1578
+ },
1579
+ {
1580
+ name: "last_updated_slot",
1581
+ docs: [
1582
+ "Slot when this region last reported"
1583
+ ],
1584
+ type: "u64"
1585
+ },
1586
+ {
1587
+ name: "total_health_score",
1588
+ docs: [
1589
+ "Running total of health scores for observers in this region"
1590
+ ],
1591
+ type: "u32"
1592
+ },
1593
+ {
1594
+ name: "total_reachability_pct",
1595
+ docs: [
1596
+ "Running total of reachability percentages for observers in this region"
1597
+ ],
1598
+ type: "u32"
1599
+ },
1600
+ {
1601
+ name: "total_avg_rtt_us",
1602
+ docs: [
1603
+ "Running total of RTT values for observers in this region"
1604
+ ],
1605
+ type: "u64"
1606
+ },
1607
+ {
1608
+ name: "total_slot_latency_ms",
1609
+ docs: [
1610
+ "Running total of slot latency values for observers in this region"
1611
+ ],
1612
+ type: "u64"
1613
+ },
1614
+ {
1615
+ name: "agave_count",
1616
+ docs: [
1617
+ "Client distribution counts for this region"
1618
+ ],
1619
+ type: "u16"
1620
+ },
1621
+ {
1622
+ name: "firedancer_count",
1623
+ type: "u16"
1624
+ },
1625
+ {
1626
+ name: "jito_count",
1627
+ type: "u16"
1628
+ },
1629
+ {
1630
+ name: "solana_labs_count",
1631
+ type: "u16"
1632
+ },
1633
+ {
1634
+ name: "other_count",
1635
+ type: "u16"
1636
+ },
1637
+ {
1638
+ name: "total_agave_count",
1639
+ docs: [
1640
+ "Running totals"
1641
+ ],
1642
+ type: "u32"
1643
+ },
1644
+ {
1645
+ name: "total_firedancer_count",
1646
+ type: "u32"
1647
+ },
1648
+ {
1649
+ name: "total_jito_count",
1650
+ type: "u32"
1651
+ },
1652
+ {
1653
+ name: "total_solana_labs_count",
1654
+ type: "u32"
1655
+ },
1656
+ {
1657
+ name: "total_other_count",
1658
+ type: "u32"
1659
+ },
1660
+ {
1661
+ name: "reachable_stake_pct",
1662
+ docs: [
1663
+ "Stake-weighted reachability for this region"
1664
+ ],
1665
+ type: "u8"
1666
+ },
1667
+ {
1668
+ name: "total_reachable_stake_pct",
1669
+ type: "u32"
1670
+ }
1671
+ ]
1672
+ }
1673
+ },
1674
+ {
1675
+ name: "RegistryAccount",
1676
+ docs: [
1677
+ "Global registry - tracks all observer nodes and program config"
1678
+ ],
1679
+ type: {
1680
+ kind: "struct",
1681
+ fields: [
1682
+ {
1683
+ name: "authority",
1684
+ docs: [
1685
+ "Admin key"
1686
+ ],
1687
+ type: "pubkey"
1688
+ },
1689
+ {
1690
+ name: "pending_authority",
1691
+ docs: [
1692
+ "Pending authority for handoff"
1693
+ ],
1694
+ type: {
1695
+ option: "pubkey"
1696
+ }
1697
+ },
1698
+ {
1699
+ name: "min_stake_lamports",
1700
+ docs: [
1701
+ "Minimum stake required to observe"
1702
+ ],
1703
+ type: "u64"
1704
+ },
1705
+ {
1706
+ name: "observer_count",
1707
+ docs: [
1708
+ "Number of observers"
1709
+ ],
1710
+ type: "u16"
1711
+ },
1712
+ {
1713
+ name: "active_count",
1714
+ docs: [
1715
+ "Currently active accounts"
1716
+ ],
1717
+ type: "u16"
1718
+ },
1719
+ {
1720
+ name: "max_observers",
1721
+ docs: [
1722
+ "Maximum number of observers"
1723
+ ],
1724
+ type: "u16"
1725
+ },
1726
+ {
1727
+ name: "paused",
1728
+ docs: [
1729
+ "Paused flag"
1730
+ ],
1731
+ type: "bool"
1732
+ },
1733
+ {
1734
+ name: "version",
1735
+ docs: [
1736
+ "Version of the registry"
1737
+ ],
1738
+ type: "u8"
1739
+ },
1740
+ {
1741
+ name: "bump",
1742
+ docs: [
1743
+ "Bump seed for the PDA"
1744
+ ],
1745
+ type: "u8"
1746
+ }
1747
+ ]
1748
+ }
1749
+ }
1750
+ ],
1751
+ constants: [
1752
+ {
1753
+ name: "SEED",
1754
+ type: "string",
1755
+ value: '"anchor"'
1756
+ }
1757
+ ]
1758
+ };
1759
+
1760
+ // src/instructions.ts
1761
+ import "@coral-xyz/anchor";
1762
+ import BN2 from "bn.js";
1763
+ import {
1764
+ PublicKey as PublicKey3
1765
+ } from "@solana/web3.js";
1766
+ var CLOCK_SYSVAR = new PublicKey3(
1767
+ "SysvarC1ock11111111111111111111111111111111"
1768
+ );
1769
+ function toAnchorRegion(region) {
1770
+ const map = {
1771
+ ["Asia" /* Asia */]: "asia",
1772
+ ["US" /* US */]: "us",
1773
+ ["EU" /* EU */]: "eu",
1774
+ ["SouthAmerica" /* SouthAmerica */]: "southAmerica",
1775
+ ["Africa" /* Africa */]: "africa",
1776
+ ["Oceania" /* Oceania */]: "oceania",
1777
+ ["Other" /* Other */]: "other"
1778
+ };
1779
+ return { [map[region]]: {} };
1780
+ }
1781
+ async function buildRegisterObserver(program, observer, region) {
1782
+ const programId = program.programId;
1783
+ const [observerAccount] = getObserverPDA(observer, programId);
1784
+ const [registry] = getRegistryPDA(programId);
1785
+ return program.methods["registerObserver"](
1786
+ toAnchorRegion(region)
1787
+ ).accounts({ observer, observerAccount, registry }).instruction();
1788
+ }
1789
+ async function buildSubmitAttestation(program, authority, params) {
1790
+ const programId = program.programId;
1791
+ const [observerAccount] = getObserverPDA(authority, programId);
1792
+ const [networkHealth] = getNetworkHealthPDA(programId);
1793
+ const [registry] = getRegistryPDA(programId);
1794
+ return program.methods["submitAttestation"](
1795
+ params.tpuReachable,
1796
+ params.tpuProbed,
1797
+ params.avgRttUs,
1798
+ params.p95RttUs,
1799
+ params.slotLatencyMs,
1800
+ params.agaveCount,
1801
+ params.firedancerCount,
1802
+ params.jitoCount,
1803
+ params.solanaLabsCount,
1804
+ params.otherCount,
1805
+ params.reachableStakePct
1806
+ ).accounts({
1807
+ authority,
1808
+ observerAccount,
1809
+ networkHealth,
1810
+ registry,
1811
+ clock: CLOCK_SYSVAR
1812
+ }).instruction();
1813
+ }
1814
+ async function buildDeregisterObserver(program, caller, observerWallet) {
1815
+ const programId = program.programId;
1816
+ const [observerAccount] = getObserverPDA(observerWallet, programId);
1817
+ const [registry] = getRegistryPDA(programId);
1818
+ return program.methods["deregisterObserver"]().accounts({ caller, observerWallet, observerAccount, registry }).instruction();
1819
+ }
1820
+ async function buildCrankAggregation(program, cranker, observerAccounts) {
1821
+ const programId = program.programId;
1822
+ const [networkHealth] = getNetworkHealthPDA(programId);
1823
+ const [registryAccount] = getRegistryPDA(programId);
1824
+ const remaining = observerAccounts.map((pubkey) => ({
1825
+ pubkey,
1826
+ isSigner: false,
1827
+ isWritable: false
1828
+ }));
1829
+ return program.methods["crankAggregation"]().accounts({
1830
+ cranker,
1831
+ networkHealth,
1832
+ registryAccount,
1833
+ clock: CLOCK_SYSVAR
1834
+ }).remainingAccounts(remaining).instruction();
1835
+ }
1836
+ async function buildInitialize(program, authority, minStakeLamports, maxObservers) {
1837
+ const programId = program.programId;
1838
+ const [registry] = getRegistryPDA(programId);
1839
+ const [networkHealth] = getNetworkHealthPDA(programId);
1840
+ return program.methods["initialize"](
1841
+ new BN2(minStakeLamports.toString()),
1842
+ maxObservers
1843
+ ).accounts({ authority, registry, networkHealth }).instruction();
1844
+ }
1845
+ async function buildSlashObserver(program, authority, observerWallet, treasury, slashBps) {
1846
+ const programId = program.programId;
1847
+ const [observerAccount] = getObserverPDA(observerWallet, programId);
1848
+ const [registry] = getRegistryPDA(programId);
1849
+ return program.methods["slashObserver"](
1850
+ slashBps
1851
+ ).accounts({
1852
+ authority,
1853
+ observerWallet,
1854
+ observerAccount,
1855
+ registry,
1856
+ treasury
1857
+ }).instruction();
1858
+ }
1859
+ async function buildUpdateConfig(program, authority, params) {
1860
+ const programId = program.programId;
1861
+ const [registry] = getRegistryPDA(programId);
1862
+ const minStake = params.minStakeLamports != null ? new BN2(params.minStakeLamports.toString()) : null;
1863
+ return program.methods["updateConfig"](
1864
+ minStake,
1865
+ params.maxObservers,
1866
+ params.paused
1867
+ ).accounts({ authority, registry }).instruction();
1868
+ }
1869
+ async function buildProposeAuthority(program, authority, newAuthority) {
1870
+ const programId = program.programId;
1871
+ const [registry] = getRegistryPDA(programId);
1872
+ return program.methods["proposeAuthority"](
1873
+ newAuthority
1874
+ ).accounts({ authority, registry }).instruction();
1875
+ }
1876
+ async function buildAcceptAuthority(program, newAuthority) {
1877
+ const programId = program.programId;
1878
+ const [registry] = getRegistryPDA(programId);
1879
+ return program.methods["acceptAuthority"]().accounts({ newAuthority, registry }).instruction();
1880
+ }
1881
+
1882
+ // src/events.ts
1883
+ import "@solana/web3.js";
1884
+ import "bn.js";
1885
+ function toBigInt2(val) {
1886
+ if (typeof val === "bigint") return val;
1887
+ if (typeof val === "number") return BigInt(val);
1888
+ return BigInt(val.toString());
1889
+ }
1890
+ function toRegion2(val) {
1891
+ const key = typeof val === "string" ? val : Object.keys(val)[0] ?? "other";
1892
+ const normalized = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
1893
+ const map = {
1894
+ Asia: "Asia",
1895
+ Us: "US",
1896
+ Eu: "EU",
1897
+ Southamerica: "SouthAmerica",
1898
+ Africa: "Africa",
1899
+ Oceania: "Oceania",
1900
+ Other: "Other"
1901
+ };
1902
+ return map[normalized] ?? "Other";
1903
+ }
1904
+ function decodeAttestationSubmitted(raw) {
1905
+ return {
1906
+ observer: raw["observer"],
1907
+ region: toRegion2(raw["region"]),
1908
+ score: raw["score"],
1909
+ reachabilityPct: raw["reachabilityPct"],
1910
+ slotLatencyMs: raw["slotLatencyMs"],
1911
+ slot: toBigInt2(raw["slot"]),
1912
+ agaveCount: raw["agaveCount"],
1913
+ firedancerCount: raw["firedancerCount"],
1914
+ jitoCount: raw["jitoCount"],
1915
+ solanaLabsCount: raw["solanaLabsCount"],
1916
+ otherCount: raw["otherCount"],
1917
+ reachableStakePct: raw["reachableStakePct"]
1918
+ };
1919
+ }
1920
+ function decodeObserverRegistered(raw) {
1921
+ return {
1922
+ observer: raw["observer"],
1923
+ region: toRegion2(raw["region"]),
1924
+ stakeLamports: toBigInt2(raw["stakeLamports"])
1925
+ };
1926
+ }
1927
+ function decodeObserverDeregistered(raw) {
1928
+ return {
1929
+ observer: raw["observer"]
1930
+ };
1931
+ }
1932
+ function decodeObserverSlashed(raw) {
1933
+ return {
1934
+ observer: raw["observer"],
1935
+ slashBps: raw["slashBps"],
1936
+ amountSlashed: toBigInt2(raw["amountSlashed"])
1937
+ };
1938
+ }
1939
+ function decodeConfigUpdated(raw) {
1940
+ const minStake = raw["minStakeLamports"];
1941
+ const maxObs = raw["maxObservers"];
1942
+ const paused = raw["paused"];
1943
+ return {
1944
+ minStakeLamports: minStake != null ? toBigInt2(minStake) : null,
1945
+ maxObservers: maxObs != null ? maxObs : null,
1946
+ paused: paused != null ? paused : null
1947
+ };
1948
+ }
1949
+
1950
+ // src/client.ts
1951
+ var READ_ONLY_PUBKEY = new PublicKey5("11111111111111111111111111111111");
1952
+ function createReadOnlyWallet() {
1953
+ return {
1954
+ publicKey: READ_ONLY_PUBKEY,
1955
+ payer: Keypair.generate(),
1956
+ signTransaction: async (tx) => tx,
1957
+ signAllTransactions: async (txs) => txs
1958
+ };
1959
+ }
1960
+ function createS0racleClient(opts) {
1961
+ const { connection } = opts;
1962
+ const programId = opts.programId ?? PROGRAM_ID;
1963
+ const wallet = opts.wallet ?? createReadOnlyWallet();
1964
+ const provider = new AnchorProvider(connection, wallet, {
1965
+ commitment: "confirmed"
1966
+ });
1967
+ const idl = { ...s0racle_program_default, address: programId.toBase58() };
1968
+ const program = new Program2(idl, provider);
1969
+ async function getNetworkHealth() {
1970
+ const [pda] = getNetworkHealthPDA(programId);
1971
+ const raw = await program.account["networkHealthAccount"].fetch(pda);
1972
+ return decodeNetworkHealth(raw);
1973
+ }
1974
+ async function getRegistry() {
1975
+ const [pda] = getRegistryPDA(programId);
1976
+ const raw = await program.account["registryAccount"].fetch(pda);
1977
+ return decodeRegistry(raw);
1978
+ }
1979
+ async function getObserver(observerPubkey) {
1980
+ const [pda] = getObserverPDA(observerPubkey, programId);
1981
+ const raw = await program.account["observerAccount"].fetch(pda);
1982
+ return decodeObserver(raw, pda);
1983
+ }
1984
+ async function getAllObservers() {
1985
+ const all = await program.account["observerAccount"].all();
1986
+ return all.map(
1987
+ (entry) => decodeObserver(entry.account, entry.publicKey)
1988
+ );
1989
+ }
1990
+ async function getObserversByRegion(region) {
1991
+ const all = await getAllObservers();
1992
+ return all.filter((o) => o.region === region);
1993
+ }
1994
+ return {
1995
+ programId,
1996
+ connection,
1997
+ // Reads
1998
+ getNetworkHealth,
1999
+ getRegistry,
2000
+ getObserver,
2001
+ getAllObservers,
2002
+ getObserversByRegion,
2003
+ // Instruction builders
2004
+ registerObserver: (observer, region) => buildRegisterObserver(program, observer, region),
2005
+ submitAttestation: (authority, params) => buildSubmitAttestation(program, authority, params),
2006
+ deregisterObserver: (caller, observerWallet) => buildDeregisterObserver(program, caller, observerWallet),
2007
+ crankAggregation: (cranker, observerAccounts) => buildCrankAggregation(program, cranker, observerAccounts),
2008
+ initialize: (authority, minStakeLamports, maxObservers) => buildInitialize(program, authority, minStakeLamports, maxObservers),
2009
+ slashObserver: (authority, observerWallet, treasury, slashBps) => buildSlashObserver(
2010
+ program,
2011
+ authority,
2012
+ observerWallet,
2013
+ treasury,
2014
+ slashBps
2015
+ ),
2016
+ updateConfig: (authority, params) => buildUpdateConfig(program, authority, params),
2017
+ proposeAuthority: (authority, newAuthority) => buildProposeAuthority(program, authority, newAuthority),
2018
+ acceptAuthority: (newAuthority) => buildAcceptAuthority(program, newAuthority),
2019
+ // Event subscriptions
2020
+ onAttestationSubmitted: (callback) => program.addEventListener(
2021
+ "attestationSubmitted",
2022
+ (raw, slot) => callback(
2023
+ decodeAttestationSubmitted(raw),
2024
+ slot
2025
+ )
2026
+ ),
2027
+ onObserverRegistered: (callback) => program.addEventListener(
2028
+ "observerRegistered",
2029
+ (raw, slot) => callback(
2030
+ decodeObserverRegistered(raw),
2031
+ slot
2032
+ )
2033
+ ),
2034
+ onObserverDeregistered: (callback) => program.addEventListener(
2035
+ "observerDeregistered",
2036
+ (raw, slot) => callback(
2037
+ decodeObserverDeregistered(raw),
2038
+ slot
2039
+ )
2040
+ ),
2041
+ onObserverSlashed: (callback) => program.addEventListener(
2042
+ "observerSlashed",
2043
+ (raw, slot) => callback(decodeObserverSlashed(raw), slot)
2044
+ ),
2045
+ onConfigUpdated: (callback) => program.addEventListener(
2046
+ "configUpdated",
2047
+ (raw, slot) => callback(decodeConfigUpdated(raw), slot)
2048
+ ),
2049
+ removeEventListener: (listenerId) => program.removeEventListener(listenerId)
2050
+ };
2051
+ }
2052
+
2053
+ // src/utils.ts
2054
+ var DEFAULT_DEGRADED_THRESHOLD = 70;
2055
+ var DEFAULT_CRITICAL_THRESHOLD = 40;
2056
+ function isStale(networkHealth, currentSlot) {
2057
+ return currentSlot - networkHealth.lastUpdatedSlot > STALE_SLOTS;
2058
+ }
2059
+ function isObserverStale(observer, currentSlot) {
2060
+ return currentSlot - observer.lastAttestationSlot > STALE_SLOTS;
2061
+ }
2062
+ function isDegraded(networkHealth, threshold = DEFAULT_DEGRADED_THRESHOLD) {
2063
+ return networkHealth.healthScore < threshold;
2064
+ }
2065
+ function healthStatus(networkHealth, currentSlot) {
2066
+ if (currentSlot !== void 0 && isStale(networkHealth, currentSlot)) {
2067
+ return "stale";
2068
+ }
2069
+ if (networkHealth.healthScore < DEFAULT_CRITICAL_THRESHOLD) return "critical";
2070
+ if (networkHealth.healthScore < DEFAULT_DEGRADED_THRESHOLD) return "degraded";
2071
+ return "healthy";
2072
+ }
2073
+ function regionLabel(region) {
2074
+ switch (region) {
2075
+ case "Asia" /* Asia */:
2076
+ return "Asia";
2077
+ case "US" /* US */:
2078
+ return "United States";
2079
+ case "EU" /* EU */:
2080
+ return "Europe";
2081
+ case "SouthAmerica" /* SouthAmerica */:
2082
+ return "South America";
2083
+ case "Africa" /* Africa */:
2084
+ return "Africa";
2085
+ case "Oceania" /* Oceania */:
2086
+ return "Oceania";
2087
+ case "Other" /* Other */:
2088
+ return "Other";
2089
+ }
2090
+ }
2091
+ function lamportsToSol(lamports) {
2092
+ return Number(lamports) / 1e9;
2093
+ }
2094
+ function latencyScore(slotLatencyMs) {
2095
+ if (slotLatencyMs >= 400) return 0;
2096
+ return Math.floor((400 - slotLatencyMs) * 100 / 400);
2097
+ }
2098
+ function isConsensusCritical(reachableStakePct) {
2099
+ return reachableStakePct < 67;
2100
+ }
2101
+ function stakeReachStatus(reachableStakePct) {
2102
+ if (reachableStakePct >= 80) return "healthy";
2103
+ if (reachableStakePct >= 67) return "degraded";
2104
+ return "critical";
2105
+ }
2106
+ function dominantClient(region) {
2107
+ const counts = {
2108
+ agave: region.agaveCount,
2109
+ firedancer: region.firedancerCount,
2110
+ jito: region.jitoCount,
2111
+ other: region.otherCount + region.solanaLabsCount
2112
+ };
2113
+ return Object.entries(counts).sort((a, b) => b[1] - a[1])[0][0];
2114
+ }
2115
+ function clientDiversityIndex(region) {
2116
+ const total = region.agaveCount + region.firedancerCount + region.jitoCount + region.solanaLabsCount + region.otherCount;
2117
+ if (total === 0) return 0;
2118
+ const shares = [
2119
+ region.agaveCount,
2120
+ region.firedancerCount,
2121
+ region.jitoCount,
2122
+ region.solanaLabsCount,
2123
+ region.otherCount
2124
+ ].map((c) => c / total);
2125
+ const hhi = shares.reduce((sum, s) => sum + s * s, 0);
2126
+ return Math.round((1 - hhi) * 125);
2127
+ }
2128
+ export {
2129
+ PROGRAM_ID,
2130
+ Region,
2131
+ clientDiversityIndex,
2132
+ createS0racleClient,
2133
+ dominantClient,
2134
+ getNetworkHealthPDA,
2135
+ getObserverPDA,
2136
+ getRegistryPDA,
2137
+ healthStatus,
2138
+ isConsensusCritical,
2139
+ isDegraded,
2140
+ isObserverStale,
2141
+ isStale,
2142
+ lamportsToSol,
2143
+ latencyScore,
2144
+ regionLabel,
2145
+ stakeReachStatus
2146
+ };
2147
+ //# sourceMappingURL=index.mjs.map