@glowlabs-org/utils 0.2.149 → 0.2.151

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/esm/index.js CHANGED
@@ -13,8 +13,8 @@ import { parseUnits, formatUnits } from 'viem';
13
13
  import { MerkleTree } from 'merkletreejs';
14
14
  import { solidityPackedKeccak256, keccak256 } from 'ethers';
15
15
  import Decimal from 'decimal.js';
16
- import { H as HUB_URL, U as USDG_WEIGHT_DECIMAL_PRECISION, G as GLOW_WEIGHT_DECIMAL_PRECISION, M as MAX_WEIGHT } from './farms-router-C2Pkoecj.js';
17
- export { C as ControlRouter, F as FarmsRouter, d as KICKSTARTER_STATUS, K as KickstarterRouter, O as OFF_CHAIN_PAYMENT_CURRENCIES, P as PAYMENT_CURRENCIES, b as REGIONS, R as RegionRouter, S as STAKING_DIRECTIONS, T as TRANSFER_TYPES, W as WalletsRouter, c as configureSentry, u as useForwarder, a as useOffchainFractions } from './farms-router-C2Pkoecj.js';
16
+ import { H as HUB_URL, U as USDG_WEIGHT_DECIMAL_PRECISION, G as GLOW_WEIGHT_DECIMAL_PRECISION, M as MAX_WEIGHT } from './farms-router-Cpy_o4_u.js';
17
+ export { C as ControlRouter, F as FarmsRouter, d as KICKSTARTER_STATUS, K as KickstarterRouter, O as OFF_CHAIN_PAYMENT_CURRENCIES, P as PAYMENT_CURRENCIES, b as REGIONS, R as RegionRouter, S as STAKING_DIRECTIONS, T as TRANSFER_TYPES, W as WalletsRouter, c as configureSentry, u as useForwarder, a as useOffchainFractions } from './farms-router-Cpy_o4_u.js';
18
18
 
19
19
  const GENESIS_TIMESTAMP = 1700352000;
20
20
 
@@ -1,4 +1,4 @@
1
- import type { StakeRequest, RegionStake, WalletRegionStake, WalletRegionUnlocked, WalletRegionCommittedBalance, TransferDetails, GlwRegionRewardsResponse, MintedEventsResponse, StakeEventsResponse, FailedOperationsResponse, PendingTransfersResponse, PendingTransferType, RestakeRequest, MigrationAmountResponse } from "../types";
1
+ import type { StakeRequest, RegionStake, WalletRegionStake, WalletRegionUnlocked, WalletRegionCommittedBalance, TransferDetails, GlwRegionRewardsResponse, MintedEventsResponse, StakeEventsResponse, FailedOperationsResponse, PendingTransfersResponse, PendingTransferType, RestakeRequest, MigrationAmountResponse, RetryFailedOperationResponse, FarmRewardSplit } from "../types";
2
2
  export interface PayProtocolDepositUsingStakedControlRequest {
3
3
  wallet: string;
4
4
  regionId: number;
@@ -28,6 +28,7 @@ export declare function ControlRouter(baseUrl: string): {
28
28
  readonly fetchGctlPrice: () => Promise<string>;
29
29
  readonly fetchGlwPrice: () => Promise<string>;
30
30
  readonly fetchCirculatingSupply: () => Promise<string>;
31
+ readonly fetchHoldersCount: () => Promise<number>;
31
32
  readonly fetchLastNonce: (wallet: string) => Promise<string>;
32
33
  readonly fetchMintedEvents: (page?: number, limit?: number) => Promise<MintedEventsResponse>;
33
34
  readonly fetchStakeEvents: (page?: number, limit?: number, regionId?: number) => Promise<StakeEventsResponse>;
@@ -39,11 +40,12 @@ export declare function ControlRouter(baseUrl: string): {
39
40
  readonly fetchWalletRegionCommittedBalance: (wallet: string, regionId: number) => Promise<WalletRegionCommittedBalance>;
40
41
  readonly fetchTransferDetails: (txId: string) => Promise<TransferDetails>;
41
42
  readonly fetchGlwRegionRewards: () => Promise<GlwRegionRewardsResponse>;
43
+ readonly fetchFarmRewardSplits: (farmId: string) => Promise<FarmRewardSplit[]>;
42
44
  readonly fetchMigrationAmount: (wallet: string) => Promise<MigrationAmountResponse>;
43
45
  readonly stakeGctl: (stakeRequest: StakeRequest) => Promise<boolean>;
44
46
  readonly unstakeGctl: (unstakeRequest: StakeRequest) => Promise<boolean>;
45
47
  readonly restakeGctl: (restakeRequest: RestakeRequest) => Promise<boolean>;
46
- readonly retryFailedOperation: (operationId: string) => Promise<boolean>;
48
+ readonly retryFailedOperation: (operationId: string) => Promise<RetryFailedOperationResponse>;
47
49
  readonly payProtocolDepositUsingStakedControl: (paymentRequest: PayProtocolDepositUsingStakedControlRequest) => Promise<PayProtocolDepositUsingStakedControlResponse>;
48
50
  readonly migrateUser: (migrateRequest: MigrateUserRequest) => Promise<MigrateUserResponse>;
49
51
  readonly isStaking: boolean;
@@ -1,4 +1,4 @@
1
- import type { RegionWithMetadata, ActivationConfig, ActivationEvent, RegionDetails, SponsoredFarm } from "../types";
1
+ import type { RegionWithMetadata, ActivationConfig, ActivationEvent, RegionDetails, SponsoredFarm, ActiveRegionsSummaryResponse, InstallerApplicationPayload, InstallerApplicationResponse } from "../types";
2
2
  export declare function RegionRouter(baseUrl: string): {
3
3
  readonly fetchRegions: (params?: {
4
4
  isActive?: boolean;
@@ -7,7 +7,9 @@ export declare function RegionRouter(baseUrl: string): {
7
7
  readonly fetchActivationEvents: (regionId?: number) => Promise<ActivationEvent[]>;
8
8
  readonly fetchRegionByIdOrSlug: (idOrSlug: string) => Promise<RegionDetails>;
9
9
  readonly fetchRegionSolarFarms: (regionId: number) => Promise<SponsoredFarm[]>;
10
+ readonly fetchActiveSummary: () => Promise<ActiveRegionsSummaryResponse>;
10
11
  readonly getRegionByCode: (code: string) => RegionWithMetadata | null;
12
+ readonly applyInstallerCertification: (payload: InstallerApplicationPayload) => Promise<InstallerApplicationResponse>;
11
13
  readonly regions: RegionWithMetadata[];
12
14
  readonly isLoading: boolean;
13
15
  };
@@ -262,6 +262,9 @@ export interface ActivationEvent {
262
262
  id: string;
263
263
  regionId: number;
264
264
  epoch: number;
265
+ stakeThresholdMet: boolean;
266
+ solarFarmRequirementMet: boolean;
267
+ installerRequirementMet: boolean;
265
268
  activated: boolean;
266
269
  ts: string;
267
270
  }
@@ -308,6 +311,46 @@ export interface RegionDetails extends RegionWithMetadata {
308
311
  carbonCreditsIssued: number;
309
312
  carbonCreditsPerWeek: number;
310
313
  }
314
+ export interface ActiveRegionSnapshot {
315
+ epoch: number;
316
+ totals: {
317
+ gctlStaked: string;
318
+ pendingUnstake: string;
319
+ pendingRestakeOut: string;
320
+ pendingRestakeIn: string;
321
+ };
322
+ }
323
+ export interface ActiveRegionSummary {
324
+ id: number;
325
+ name: string;
326
+ code: string;
327
+ slug: string;
328
+ isUs: boolean;
329
+ gctlStaked: string;
330
+ glwRewardPerWeek: string;
331
+ rewardShare: string;
332
+ pendingUnstake: string;
333
+ pendingRestakeOut: string;
334
+ pendingRestakeIn: string;
335
+ snapshots: ActiveRegionSnapshot[];
336
+ }
337
+ export interface ActiveRegionsSummaryResponse {
338
+ total: {
339
+ totalGctlStaked: string;
340
+ totalGlwRewards: string;
341
+ };
342
+ regions: ActiveRegionSummary[];
343
+ }
344
+ export interface InstallerApplicationPayload {
345
+ wallet: string;
346
+ signature: string;
347
+ nonce: string;
348
+ regionId: string;
349
+ deadline: string;
350
+ }
351
+ export interface InstallerApplicationResponse {
352
+ certified: boolean;
353
+ }
311
354
  export interface RegionsResponse {
312
355
  regions: RegionWithMetadata[];
313
356
  }
@@ -530,4 +573,11 @@ export interface FarmRewardSplitsResponse {
530
573
  export interface FarmRewardSplitsErrorResponse {
531
574
  error: string;
532
575
  }
576
+ export interface HoldersCountResponse {
577
+ holders: number;
578
+ }
579
+ export interface RetryFailedOperationResponse {
580
+ success?: boolean;
581
+ queued?: boolean;
582
+ }
533
583
  export type { MintedEvent as ControlMintedEvent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glowlabs-org/utils",
3
- "version": "0.2.149",
3
+ "version": "0.2.151",
4
4
  "description": "A library containing all typechain types and addresses relating to the glow guarded launch",
5
5
  "keywords": [],
6
6
  "author": "",
@@ -19,7 +19,20 @@ import type {
19
19
  PendingTransferType,
20
20
  RestakeRequest,
21
21
  MigrationAmountResponse,
22
+ HoldersCountResponse,
23
+ RetryFailedOperationResponse,
24
+ FarmRewardSplitsResponse,
25
+ FarmRewardSplitsErrorResponse,
26
+ FarmRewardSplit,
22
27
  } from "../types";
28
+
29
+ interface FetchGctlBalanceResponse {
30
+ gctl_balance: string;
31
+ }
32
+
33
+ interface FetchCommittedBalanceResponse {
34
+ gctl_committed_balance: string;
35
+ }
23
36
  import {
24
37
  sentryAddBreadcrumb,
25
38
  sentryCaptureException,
@@ -69,6 +82,9 @@ function parseApiError(error: unknown): string {
69
82
  // --------------------------------------------------------------------------
70
83
 
71
84
  export function ControlRouter(baseUrl: string) {
85
+ if (!baseUrl) {
86
+ throw new Error("CONTROL API base URL is not set");
87
+ }
72
88
  // ----------------------- Internal helpers --------------------------------
73
89
  const request = async <T>(path: string, init?: RequestInit): Promise<T> => {
74
90
  const res = await fetch(`${baseUrl}${path}`, init);
@@ -82,7 +98,7 @@ export function ControlRouter(baseUrl: string) {
82
98
  // ----------------------- GETters -----------------------------------------
83
99
  const fetchGctlBalance = async (wallet: string): Promise<string> => {
84
100
  try {
85
- const data = await request<{ gctl_balance: string }>(
101
+ const data = await request<FetchGctlBalanceResponse>(
86
102
  `/balance/${wallet}`
87
103
  );
88
104
  return (data?.gctl_balance ?? "0").toString();
@@ -93,7 +109,7 @@ export function ControlRouter(baseUrl: string) {
93
109
 
94
110
  const fetchCommittedBalance = async (wallet: string): Promise<string> => {
95
111
  try {
96
- const data = await request<{ gctl_committed_balance: string }>(
112
+ const data = await request<FetchCommittedBalanceResponse>(
97
113
  `/committed-balance/${wallet}`
98
114
  );
99
115
  return (data?.gctl_committed_balance ?? "0").toString();
@@ -138,6 +154,15 @@ export function ControlRouter(baseUrl: string) {
138
154
  }
139
155
  };
140
156
 
157
+ const fetchHoldersCount = async (): Promise<number> => {
158
+ try {
159
+ const data = await request<HoldersCountResponse>(`/holders/count`);
160
+ return data.holders;
161
+ } catch (error) {
162
+ throw new Error(parseApiError(error));
163
+ }
164
+ };
165
+
141
166
  // Build pagination query helper
142
167
  const buildPaginationQuery = (page?: number, limit?: number) => {
143
168
  const p = page ?? 1;
@@ -225,6 +250,21 @@ export function ControlRouter(baseUrl: string) {
225
250
  }
226
251
  };
227
252
 
253
+ const fetchFarmRewardSplits = async (
254
+ farmId: string
255
+ ): Promise<FarmRewardSplit[]> => {
256
+ try {
257
+ if (!farmId) throw new Error("Farm ID is required");
258
+ const data = await request<
259
+ FarmRewardSplitsResponse | FarmRewardSplitsErrorResponse
260
+ >(`/farms/${encodeURIComponent(farmId)}/reward-splits`);
261
+ if ("error" in data) throw new Error(data.error);
262
+ return data.rewardSplits ?? [];
263
+ } catch (error) {
264
+ throw new Error(parseApiError(error));
265
+ }
266
+ };
267
+
228
268
  // Exposed query with error parsing
229
269
  const getTransferDetails = async (txId: string): Promise<TransferDetails> => {
230
270
  try {
@@ -399,7 +439,7 @@ export function ControlRouter(baseUrl: string) {
399
439
 
400
440
  const retryFailedOperation = async (
401
441
  operationId: string
402
- ): Promise<boolean> => {
442
+ ): Promise<RetryFailedOperationResponse> => {
403
443
  isRetryingFailedOperation = true;
404
444
  try {
405
445
  sentryAddBreadcrumb({
@@ -408,11 +448,14 @@ export function ControlRouter(baseUrl: string) {
408
448
  level: "info",
409
449
  data: { baseUrl, operationId },
410
450
  });
411
- await request(`/operations/failed/${operationId}/retry`, {
412
- method: "POST",
413
- headers: { "Content-Type": "application/json" },
414
- });
415
- return true;
451
+ const response = await request<RetryFailedOperationResponse>(
452
+ `/operations/failed/${operationId}/retry`,
453
+ {
454
+ method: "POST",
455
+ headers: { "Content-Type": "application/json" },
456
+ }
457
+ );
458
+ return response;
416
459
  } catch (error) {
417
460
  sentryCaptureException(error, {
418
461
  action: "retryFailedOperation",
@@ -520,6 +563,7 @@ export function ControlRouter(baseUrl: string) {
520
563
  fetchGctlPrice,
521
564
  fetchGlwPrice,
522
565
  fetchCirculatingSupply,
566
+ fetchHoldersCount,
523
567
  fetchLastNonce,
524
568
  fetchMintedEvents,
525
569
  fetchStakeEvents,
@@ -531,6 +575,7 @@ export function ControlRouter(baseUrl: string) {
531
575
  fetchWalletRegionCommittedBalance,
532
576
  fetchTransferDetails: getTransferDetails,
533
577
  fetchGlwRegionRewards,
578
+ fetchFarmRewardSplits,
534
579
  fetchMigrationAmount,
535
580
 
536
581
  // Mutations
@@ -1,5 +1,58 @@
1
1
  "use strict";
2
2
 
3
+ /**
4
+ * # Regions Router
5
+ *
6
+ * This router wraps the Control API endpoints that power Kickstarter-style
7
+ * activation across geographic regions. It surfaces read APIs for activation
8
+ * progress plus the installer certification mutation.
9
+ *
10
+ * ## Components
11
+ * - **router.ts**: exports the `RegionRouter` factory with helpers for each
12
+ * endpoint:
13
+ * - `GET /regions/all` – list regions with activation progress (stake, farms,
14
+ * installers). Accepts optional `?isActive=true|false` filter.
15
+ * - `GET /regions/:idOrSlug` – return region VCR view.
16
+ * - `GET /regions/active/summary` – aggregate GLW/week distribution, pending
17
+ * restakes, and recent epoch snapshots for active regions.
18
+ * - `GET /regions/solar-farms/:regionId` – list sponsored farms for a region.
19
+ * - `POST /regions/installers/apply` – certify an installer via signature.
20
+ * - `GET /regions/activation-events` – activation-event timeline, optional
21
+ * `regionId` filter.
22
+ * - `GET /regions/activation-config` – static activation thresholds and
23
+ * campaign duration for a region code.
24
+ * - **getters.ts**: shared data aggregation utilities used by the router
25
+ * implementation.
26
+ * - **db/schema.ts**: Drizzle schema for `regions`, `region_kickstarters`,
27
+ * `solar_farms`, `certified_installers`, and `region_activation_events`.
28
+ * - **constants**: activation thresholds imported from `@src/constants`:
29
+ * ```ts
30
+ * import {
31
+ * MIN_FARMS,
32
+ * US_STAKE_THRESHOLD,
33
+ * NON_US_STAKE_THRESHOLD,
34
+ * CAMPAIGN_DURATION_DAYS,
35
+ * MIN_INSTALLERS,
36
+ * US_STAKED_TRESHOLD_BIGINT,
37
+ * NON_US_STAKED_TRESHOLD_BIGINT,
38
+ * } from "@src/constants";
39
+ * ```
40
+ * - Region metadata: `regionMetadata` from `@glowlabs-org/utils/browser` feeds
41
+ * `/activation-config` responses.
42
+ * - Kickstarter endpoints live in `KickstarterRouter`.
43
+ *
44
+ * ## Activation Criteria
45
+ * A region activates when it satisfies all of:
46
+ * 1. Stake threshold met (≥ 20 000 GCTL for US, ≥ 200 000 GCTL otherwise).
47
+ * 2. At least 10 solar farms registered.
48
+ * 3. At least 1 certified installer.
49
+ *
50
+ * ## API Responses
51
+ * All numeric stake values are returned as strings (atomic units) to avoid
52
+ * precision loss. Dates are ISO 8601 strings. The router exposes typed helpers
53
+ * for the full dataset described above.
54
+ */
55
+
3
56
  import { generateSlug } from "src/utils/generate-slug";
4
57
  import { regionMetadata } from "../region-metadata";
5
58
  import type {
@@ -8,6 +61,9 @@ import type {
8
61
  ActivationEvent,
9
62
  RegionDetails,
10
63
  SponsoredFarm,
64
+ ActiveRegionsSummaryResponse,
65
+ InstallerApplicationPayload,
66
+ InstallerApplicationResponse,
11
67
  } from "../types";
12
68
 
13
69
  // ---------------------------------------------------------------------------
@@ -119,6 +175,34 @@ export function RegionRouter(baseUrl: string) {
119
175
  }
120
176
  };
121
177
 
178
+ const fetchActiveSummary =
179
+ async (): Promise<ActiveRegionsSummaryResponse> => {
180
+ try {
181
+ return await request<ActiveRegionsSummaryResponse>(
182
+ `/regions/active/summary`
183
+ );
184
+ } catch (error) {
185
+ throw new Error(parseApiError(error));
186
+ }
187
+ };
188
+
189
+ const applyInstallerCertification = async (
190
+ payload: InstallerApplicationPayload
191
+ ): Promise<InstallerApplicationResponse> => {
192
+ try {
193
+ return await request<InstallerApplicationResponse>(
194
+ `/regions/installers/apply`,
195
+ {
196
+ method: "POST",
197
+ headers: { "Content-Type": "application/json" },
198
+ body: JSON.stringify(payload),
199
+ }
200
+ );
201
+ } catch (error) {
202
+ throw new Error(parseApiError(error));
203
+ }
204
+ };
205
+
122
206
  // Kickstarter-related logic moved to kickstarter-router.ts
123
207
 
124
208
  // -------------------------------------------------------------------------
@@ -173,7 +257,9 @@ export function RegionRouter(baseUrl: string) {
173
257
  fetchActivationEvents,
174
258
  fetchRegionByIdOrSlug,
175
259
  fetchRegionSolarFarms,
260
+ fetchActiveSummary,
176
261
  getRegionByCode,
262
+ applyInstallerCertification,
177
263
 
178
264
  // Cached data & flags
179
265
  get regions() {
@@ -321,6 +321,9 @@ export interface ActivationEvent {
321
321
  id: string;
322
322
  regionId: number;
323
323
  epoch: number;
324
+ stakeThresholdMet: boolean;
325
+ solarFarmRequirementMet: boolean;
326
+ installerRequirementMet: boolean;
324
327
  activated: boolean;
325
328
  ts: string; // ISO 8601
326
329
  }
@@ -374,6 +377,51 @@ export interface RegionDetails extends RegionWithMetadata {
374
377
  carbonCreditsPerWeek: number;
375
378
  }
376
379
 
380
+ export interface ActiveRegionSnapshot {
381
+ epoch: number;
382
+ totals: {
383
+ gctlStaked: string;
384
+ pendingUnstake: string;
385
+ pendingRestakeOut: string;
386
+ pendingRestakeIn: string;
387
+ };
388
+ }
389
+
390
+ export interface ActiveRegionSummary {
391
+ id: number;
392
+ name: string;
393
+ code: string;
394
+ slug: string;
395
+ isUs: boolean;
396
+ gctlStaked: string;
397
+ glwRewardPerWeek: string;
398
+ rewardShare: string;
399
+ pendingUnstake: string;
400
+ pendingRestakeOut: string;
401
+ pendingRestakeIn: string;
402
+ snapshots: ActiveRegionSnapshot[];
403
+ }
404
+
405
+ export interface ActiveRegionsSummaryResponse {
406
+ total: {
407
+ totalGctlStaked: string;
408
+ totalGlwRewards: string;
409
+ };
410
+ regions: ActiveRegionSummary[];
411
+ }
412
+
413
+ export interface InstallerApplicationPayload {
414
+ wallet: string;
415
+ signature: string;
416
+ nonce: string;
417
+ regionId: string;
418
+ deadline: string;
419
+ }
420
+
421
+ export interface InstallerApplicationResponse {
422
+ certified: boolean;
423
+ }
424
+
377
425
  // ----------------------------- API Responses --------------------------------
378
426
  export interface RegionsResponse {
379
427
  regions: RegionWithMetadata[];
@@ -651,6 +699,15 @@ export interface FarmRewardSplitsErrorResponse {
651
699
  error: string;
652
700
  }
653
701
 
702
+ export interface HoldersCountResponse {
703
+ holders: number;
704
+ }
705
+
706
+ export interface RetryFailedOperationResponse {
707
+ success?: boolean;
708
+ queued?: boolean;
709
+ }
710
+
654
711
  // ---------------------------------------------------------------------------
655
712
  // Barrel exports (convenience)
656
713
  // ---------------------------------------------------------------------------