@berachain/berajs 0.2.9 → 0.2.10

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.
Files changed (59) hide show
  1. package/dist/actions/clients/exports.d.ts +77 -1
  2. package/dist/actions/clients/exports.mjs +13 -4
  3. package/dist/actions/exports.d.ts +59 -27
  4. package/dist/actions/exports.mjs +23 -23
  5. package/dist/actions/governance/exports.mjs +3 -3
  6. package/dist/actions/server/exports.mjs +4 -4
  7. package/dist/{chunk-4Z4AK6SH.mjs → chunk-3JJLQ2JX.mjs} +3 -3
  8. package/dist/{chunk-EXIUPSFN.mjs → chunk-7YVNSDXG.mjs} +2 -2
  9. package/dist/{chunk-WXXOISTU.mjs → chunk-AUOPN6NK.mjs} +1 -1
  10. package/dist/{chunk-75M6TF7M.mjs → chunk-DQRH5VE3.mjs} +1 -1
  11. package/dist/{chunk-CDFWPU2R.mjs → chunk-E7YFXBBQ.mjs} +0 -124
  12. package/dist/{chunk-AFN4CVD3.mjs → chunk-GUURQAME.mjs} +1 -1
  13. package/dist/{chunk-KQUMKB66.mjs → chunk-GY6B3PD5.mjs} +1 -1
  14. package/dist/{chunk-HSSJKHZ4.mjs → chunk-HYDP32P6.mjs} +3 -3
  15. package/dist/{chunk-NPBQLVL3.mjs → chunk-IXIBY5FP.mjs} +2 -2
  16. package/dist/{chunk-J5I45WGQ.mjs → chunk-KHXJDYA4.mjs} +7 -0
  17. package/dist/chunk-P5WXXULM.mjs +54 -0
  18. package/dist/{chunk-QJIXTYTZ.mjs → chunk-QBBOWFMH.mjs} +105 -30
  19. package/dist/{chunk-FFB5LFDW.mjs → chunk-QVHEM4BG.mjs} +2 -2
  20. package/dist/{chunk-3EARVV7K.mjs → chunk-WNBWX23Q.mjs} +17 -5
  21. package/dist/chunk-Y6THHG77.mjs +126 -0
  22. package/dist/{chunk-XIYN6AL6.mjs → chunk-ZLTMIFCZ.mjs} +10 -5
  23. package/dist/contexts/exports.mjs +8 -8
  24. package/dist/errors/exports.mjs +5 -5
  25. package/dist/{getValidatorQueuedOperatorAddress-Dw5KN5sh.d.ts → getValidatorQueuedOperatorAddress-DphU3qhE.d.ts} +1 -1
  26. package/dist/hooks/exports.d.ts +17 -18
  27. package/dist/hooks/exports.mjs +161 -132
  28. package/dist/hooks/governance/exports.mjs +4 -4
  29. package/dist/{pol.d-CqPA9K6m.d.ts → pol.d-Dw5SQcRX.d.ts} +15 -4
  30. package/dist/types/exports.d.ts +1 -1
  31. package/dist/utils/exports.mjs +11 -9
  32. package/package.json +4 -4
  33. package/src/actions/clients/exports.ts +3 -0
  34. package/src/actions/clients/fetchBeep.ts +34 -0
  35. package/src/actions/clients/fetchOpenApi.ts +93 -0
  36. package/src/actions/clients/fetchOpenApi.unit.test.ts +223 -0
  37. package/src/actions/clients/fetchRailwayBackend.ts +34 -0
  38. package/src/actions/enso/getEnsoUserTokensWithBalances.ts +18 -0
  39. package/src/actions/exports.ts +1 -0
  40. package/src/actions/honey/getPythLatestPrices.ts +7 -0
  41. package/src/actions/pol/__tests__/rewardVaults.integration.test.ts +1 -1
  42. package/src/actions/pol/getAutoclaimedIncentives.ts +21 -10
  43. package/src/actions/pol/getAutoclaimedIncentivesTxHash.ts +41 -0
  44. package/src/actions/pol/getBgtIncentiveDistributorPaused.ts +5 -12
  45. package/src/actions/pol/getEarnedStakedBeraVault.ts +20 -16
  46. package/src/actions/pol/getRewardVaults.ts +4 -4
  47. package/src/actions/pol/getStakingDailyAssets.ts +18 -14
  48. package/src/actions/validators/utils/getValidatorBoostApy.ts +1 -1
  49. package/src/errors/RequestError.ts +12 -3
  50. package/src/errors/RequestError.unit.test.ts +55 -0
  51. package/src/errors/errorMap.ts +8 -0
  52. package/src/hooks/exports.ts +1 -0
  53. package/src/hooks/pol/useAutoclaimedIncentives.ts +1 -10
  54. package/src/hooks/pol/useAutoclaimedIncentivesTxHash.ts +45 -0
  55. package/src/hooks/validators/useValidator.ts +4 -8
  56. package/src/types/bribe-boost.d.ts +14 -3
  57. package/src/utils/polyfillAbortSignalAny.ts +53 -0
  58. package/src/utils/polyfillAbortSignalAny.unit.test.ts +81 -0
  59. /package/dist/{exports-BcUTGFUb.d.ts → getApolloClient-BcUTGFUb.d.ts} +0 -0
@@ -5,16 +5,16 @@ import {
5
5
  getProposalDetails,
6
6
  getProposalFromTx,
7
7
  getProposalVotes
8
- } from "../../chunk-AFN4CVD3.mjs";
8
+ } from "../../chunk-GUURQAME.mjs";
9
9
  import "../../chunk-DKMAIU74.mjs";
10
10
  import "../../chunk-SGIJVHZO.mjs";
11
11
  import {
12
12
  assertPublicClient
13
- } from "../../chunk-KQUMKB66.mjs";
13
+ } from "../../chunk-GY6B3PD5.mjs";
14
14
  import {
15
15
  parseBaseArgs
16
- } from "../../chunk-75M6TF7M.mjs";
17
- import "../../chunk-J5I45WGQ.mjs";
16
+ } from "../../chunk-DQRH5VE3.mjs";
17
+ import "../../chunk-KHXJDYA4.mjs";
18
18
 
19
19
  // src/hooks/governance/useCreateProposal.ts
20
20
  import { useCallback, useEffect, useState } from "react";
@@ -35,11 +35,22 @@ interface BribeBoostRewardProof {
35
35
  }
36
36
 
37
37
  // Response shape of GET /api/v1/wallets/:wallet/autoclaimed:
38
- // { [wallet]: { [validatorPubkey]: { [tokenAddress]: rawAmount (stringified bigint) } } }
39
- // Top-level wallet key is the lowercase wallet address that was queried.
38
+ // { [wallet]: { [validatorPubkey]: { [tokenAddress]: { amount } } } }
39
+ // `amount` is a stringified bigint in token base units. The backend now
40
+ // aggregates across validators under a single fake pubkey (`0x000…000`),
41
+ // so consumers can collapse the validator dimension blindly.
40
42
  type AutoclaimedIncentivesResponse = Record<
41
43
  Address,
42
- Record<Address, Record<Address, string>>
44
+ Record<Address, Record<Address, { amount: string }>>
45
+ >;
46
+
47
+ // Response shape of GET /api/v1/wallets/:wallet/autoclaimed/txhash: same
48
+ // nesting, but each token leaf is `{ txhash }`. Tokens still pending are
49
+ // simply absent — when a token from `/autoclaimed` has no entry here, the
50
+ // bot hasn't distributed it yet.
51
+ type AutoclaimedIncentivesTxHashResponse = Record<
52
+ Address,
53
+ Record<Address, Record<Address, { txhash: string }>>
43
54
  >;
44
55
 
45
56
  interface BribeBoostValidatorApr {
@@ -139,4 +150,4 @@ type StakedBeraAsset = {
139
150
  isLoadingEarnings?: boolean;
140
151
  };
141
152
 
142
- export type { AutoclaimedIncentivesResponse as A, BribeBoostRewardProof as B, ClaimedIncentives as C, IncentiveReward as I, ProtocolMetadata as P, StakingConfig as S, UserVaultInfo as U, BribeBoostRewardItem as a, StakedBeraAsset as b, AggregatedTokenTotals as c, BribeBoostReward as d, BribeBoostValidatorApr as e, ClaimedIncentivesLocalState as f, IncentiveRewardChunksWithValidatorData as g };
153
+ export type { AutoclaimedIncentivesResponse as A, BribeBoostRewardProof as B, ClaimedIncentives as C, IncentiveReward as I, ProtocolMetadata as P, StakingConfig as S, UserVaultInfo as U, AutoclaimedIncentivesTxHashResponse as a, BribeBoostRewardItem as b, StakedBeraAsset as c, AggregatedTokenTotals as d, BribeBoostReward as e, BribeBoostValidatorApr as f, ClaimedIncentivesLocalState as g, IncentiveRewardChunksWithValidatorData as h };
@@ -1,4 +1,4 @@
1
- export { c as AggregatedTokenTotals, A as AutoclaimedIncentivesResponse, d as BribeBoostReward, a as BribeBoostRewardItem, B as BribeBoostRewardProof, e as BribeBoostValidatorApr, C as ClaimedIncentives, f as ClaimedIncentivesLocalState, I as IncentiveReward, g as IncentiveRewardChunksWithValidatorData, P as ProtocolMetadata, b as StakedBeraAsset, S as StakingConfig, U as UserVaultInfo } from '../pol.d-CqPA9K6m.js';
1
+ export { d as AggregatedTokenTotals, A as AutoclaimedIncentivesResponse, a as AutoclaimedIncentivesTxHashResponse, e as BribeBoostReward, b as BribeBoostRewardItem, B as BribeBoostRewardProof, f as BribeBoostValidatorApr, C as ClaimedIncentives, g as ClaimedIncentivesLocalState, I as IncentiveReward, h as IncentiveRewardChunksWithValidatorData, P as ProtocolMetadata, c as StakedBeraAsset, S as StakingConfig, U as UserVaultInfo } from '../pol.d-Dw5SQcRX.js';
2
2
  import { M as MinimalERC20 } from '../HoneyConfigProvider-Dkj-_a5x.js';
3
3
  export { A as AllowanceQueryItem, j as AllowanceToken, B as BalanceToken, y as BexStatus, x as Calldata, H as HoneySwapActions, I as IAggregatorArgs, z as IAggregatorQuote, g as IRawAggregatorQuote, v as IUserPosition, h as PythPriceFeedMap, S as SwapRequest, b as Token, i as TokenCurrentPriceMap, w as TokenPriceInfo, t as TokenWithAmount, a as TokenWithMetadata, D as TokenWithPrice } from '../HoneyConfigProvider-Dkj-_a5x.js';
4
4
  import { BundleData, EnsoClient } from '@ensofinance/sdk';
@@ -2,7 +2,7 @@ import {
2
2
  computePriceImpact,
3
3
  wrapNativeToken,
4
4
  wrapNativeTokens
5
- } from "../chunk-FFB5LFDW.mjs";
5
+ } from "../chunk-QVHEM4BG.mjs";
6
6
  import {
7
7
  CAP_LIMIT_BUFFER,
8
8
  DEFAULT_METAMASK_GAS_LIMIT,
@@ -10,8 +10,6 @@ import {
10
10
  MAX_CUSTOM_SLIPPAGE,
11
11
  MIN_CUSTOM_DEADLINE,
12
12
  MIN_CUSTOM_SLIPPAGE,
13
- beraFetch,
14
- beraFetchJson,
15
13
  bignumber_js_default,
16
14
  calculateTimestampFromDays,
17
15
  days,
@@ -29,14 +27,18 @@ import {
29
27
  months,
30
28
  monthsInSeconds,
31
29
  msToSeconds,
32
- sanitizeRpcUrl,
33
30
  seconds,
34
31
  truncateDecimal,
35
32
  weeks,
36
33
  weeksInSeconds,
37
34
  years,
38
35
  yearsInSeconds
39
- } from "../chunk-CDFWPU2R.mjs";
36
+ } from "../chunk-E7YFXBBQ.mjs";
37
+ import {
38
+ beraFetch,
39
+ beraFetchJson,
40
+ sanitizeRpcUrl
41
+ } from "../chunk-Y6THHG77.mjs";
40
42
  import {
41
43
  defaultFlags
42
44
  } from "../chunk-BGMRHTBQ.mjs";
@@ -45,20 +47,20 @@ import {
45
47
  getHoneyToken,
46
48
  isToken,
47
49
  wBeraToken
48
- } from "../chunk-NPBQLVL3.mjs";
50
+ } from "../chunk-IXIBY5FP.mjs";
49
51
  import {
50
52
  parseBaseArgs
51
- } from "../chunk-75M6TF7M.mjs";
53
+ } from "../chunk-DQRH5VE3.mjs";
52
54
  import {
53
55
  BeraTracing
54
56
  } from "../chunk-SZ5C44L5.mjs";
55
57
  import {
56
58
  RequestError
57
- } from "../chunk-XIYN6AL6.mjs";
59
+ } from "../chunk-ZLTMIFCZ.mjs";
58
60
  import {
59
61
  BeraError,
60
62
  InvalidArgumentError
61
- } from "../chunk-J5I45WGQ.mjs";
63
+ } from "../chunk-KHXJDYA4.mjs";
62
64
 
63
65
  // src/utils/formatInputTokenValue.ts
64
66
  var formatInputTokenValue = (inputValue) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@berachain/berajs",
3
- "version": "0.2.9",
3
+ "version": "0.2.10",
4
4
  "sideEffects": false,
5
5
  "files": [
6
6
  "dist",
@@ -12,9 +12,9 @@
12
12
  "dependencies": {
13
13
  "@apollo/client": "4.1.6",
14
14
  "@berachain-foundation/berancer-sdk": "1.1.7",
15
- "@berachain/abis": "0.1.2",
16
- "@berachain/config": "0.1.16",
17
- "@berachain/graphql": "0.4.17",
15
+ "@berachain/abis": "0.1.3-beta.0",
16
+ "@berachain/config": "0.1.17",
17
+ "@berachain/graphql": "0.5.0",
18
18
  "@ensofinance/sdk": "2.3.1",
19
19
  "@pythnetwork/hermes-client": "2.0.0",
20
20
  "@wagmi/core": "2.22.1",
@@ -1,3 +1,6 @@
1
+ export { fetchBeep } from "./fetchBeep";
2
+ export { fetchOpenApi } from "./fetchOpenApi";
3
+ export { fetchRailwayBackend } from "./fetchRailwayBackend";
1
4
  export {
2
5
  getApolloClient,
3
6
  gql,
@@ -0,0 +1,34 @@
1
+ import type { paths } from "@berachain/graphql/beep";
2
+
3
+ import { parseBaseArgs } from "../../utils/parseBaseArgs";
4
+ import {
5
+ type DefinedParams,
6
+ fetchOpenApi,
7
+ type GetPath,
8
+ type GetResponse,
9
+ } from "./fetchOpenApi";
10
+
11
+ /**
12
+ * Typed GET against the next-gen "beep" backend.
13
+ *
14
+ * The `path`, its path/query params and the response are all type-checked
15
+ * against the beep schema (`@berachain/graphql/beep`), so route shapes (e.g.
16
+ * the `/v0/stake/*` namespace) can never drift out of sync with the backend.
17
+ * The base URL is resolved from `config.beep`.
18
+ *
19
+ * @throws if `config.beep` is not set for the active chain. Callers should only
20
+ * reach for beep when `config.beep` is non-null.
21
+ */
22
+ export function fetchBeep<P extends GetPath<paths>>(
23
+ path: P,
24
+ params: DefinedParams<paths, P>,
25
+ args: BeraJS.BaseFunctionArgs = {},
26
+ ): Promise<GetResponse<paths, P>> {
27
+ const { config } = parseBaseArgs(args);
28
+
29
+ if (!config.beep) {
30
+ throw new Error("fetchBeep: `beep` backend is not configured.");
31
+ }
32
+
33
+ return fetchOpenApi<paths, P>(config.beep, path, params, "beep");
34
+ }
@@ -0,0 +1,93 @@
1
+ import type { HttpLink } from "@berachain/config";
2
+ import { getUriFromLink } from "@berachain/config";
3
+
4
+ import { beraFetchJson } from "../../utils/beraFetch";
5
+
6
+ /**
7
+ * A minimal, fully-typed GET helper for our `openapi-typescript`-generated
8
+ * backends. Both backends serve only GETs, so rather than a full OpenAPI client
9
+ * we type a single `fetchOpenApi` against a schema's `paths` and wrap it per
10
+ * backend (`fetchBeep`, `fetchRailwayBackend`).
11
+ *
12
+ * The `path`, its path/query params and the response body are all type-checked
13
+ * against the schema, so route shapes can never drift out of sync with the
14
+ * backend. Requests go through {@link beraFetchJson} so they keep the same
15
+ * Sentry tracing, `RequestError` handling and URL sanitization as the rest of
16
+ * the codebase.
17
+ */
18
+
19
+ /** Any `openapi-typescript` `paths` object. */
20
+ // biome-ignore lint/suspicious/noExplicitAny: schema `paths` are structurally diverse; constrained at call sites by the concrete schema.
21
+ type AnyPaths = Record<string, any>;
22
+
23
+ /** Paths in a schema that expose a JSON GET response. */
24
+ export type GetPath<Paths extends AnyPaths> = {
25
+ [P in keyof Paths]: Paths[P] extends {
26
+ get: { responses: { 200: { content: { "application/json": unknown } } } };
27
+ }
28
+ ? P
29
+ : never;
30
+ }[keyof Paths];
31
+
32
+ type GetParams<
33
+ Paths extends AnyPaths,
34
+ P extends GetPath<Paths>,
35
+ > = Paths[P]["get"] extends { parameters: infer Params } ? Params : never;
36
+
37
+ export type GetResponse<
38
+ Paths extends AnyPaths,
39
+ P extends GetPath<Paths>,
40
+ > = Paths[P]["get"]["responses"][200]["content"]["application/json"];
41
+
42
+ // Drop the params keys that are `never` (e.g. an endpoint with no query) so
43
+ // callers only pass what an endpoint actually accepts.
44
+ export type DefinedParams<Paths extends AnyPaths, P extends GetPath<Paths>> = {
45
+ [K in keyof GetParams<Paths, P> as GetParams<Paths, P>[K] extends never
46
+ ? never
47
+ : K]: GetParams<Paths, P>[K];
48
+ };
49
+
50
+ /**
51
+ * Perform a typed GET against an OpenAPI backend.
52
+ *
53
+ * @param link The backend `HttpLink`. The base URL and monitoring name are
54
+ * read from it.
55
+ * @param path An OpenAPI path template, e.g. `/v0/stake/{vault}/stats-by-day`.
56
+ * @param params Path and query params, typed from the schema for `path`.
57
+ * @param fallbackName Monitoring name to use when `link` is a bare string.
58
+ */
59
+ // biome-ignore lint/complexity/useMaxParams: internal helper; the wrappers (fetchBeep / fetchRailwayBackend) are the public surface.
60
+ export function fetchOpenApi<Paths extends AnyPaths, P extends GetPath<Paths>>(
61
+ link: HttpLink,
62
+ path: P,
63
+ params: DefinedParams<Paths, P>,
64
+ fallbackName: string,
65
+ ): Promise<GetResponse<Paths, P>> {
66
+ const { path: pathParams, query } = params as {
67
+ path?: Record<string, string | number>;
68
+ query?: Record<string, string | number>;
69
+ };
70
+
71
+ // Substitute `{param}` placeholders from the typed path params.
72
+ const pathStr = path as string;
73
+ const resolvedPath = pathStr.replace(/\{([^}]+)\}/g, (_, key) => {
74
+ const value = pathParams?.[key];
75
+ if (value === undefined) {
76
+ throw new Error(
77
+ `fetchOpenApi: missing path param "${key}" for "${pathStr}"`,
78
+ );
79
+ }
80
+ return encodeURIComponent(String(value));
81
+ });
82
+
83
+ const search = query
84
+ ? new URLSearchParams(
85
+ Object.entries(query).map(([k, v]) => [k, String(v)]),
86
+ ).toString()
87
+ : "";
88
+
89
+ const url = `${getUriFromLink(link)}${resolvedPath}${search ? `?${search}` : ""}`;
90
+ const name = typeof link === "string" ? fallbackName : link.name;
91
+
92
+ return beraFetchJson<GetResponse<Paths, P>>({ url, name, type: "rest" });
93
+ }
@@ -0,0 +1,223 @@
1
+ import {
2
+ assert,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ expectTypeOf,
7
+ it,
8
+ vi,
9
+ } from "vitest";
10
+
11
+ import type { HttpLink } from "@berachain/config";
12
+ import { bepolia } from "@berachain/config/bepolia";
13
+ import { mainnet } from "@berachain/config/mainnet";
14
+
15
+ import type {
16
+ paths as beepPaths,
17
+ StakeEarningResponse,
18
+ StakeStatsByDayResponse,
19
+ ValidatorsResponse,
20
+ } from "@berachain/graphql/beep";
21
+
22
+ import { fetchBeep } from "./fetchBeep";
23
+ import { fetchOpenApi } from "./fetchOpenApi";
24
+
25
+ // Mock the network layer so we can assert exactly what the helpers build and
26
+ // pass through, without hitting a backend.
27
+ const beraFetchJson = vi.fn();
28
+ vi.mock("../../utils/beraFetch", () => ({
29
+ beraFetchJson: (...args: unknown[]) => beraFetchJson(...args),
30
+ }));
31
+
32
+ const BASE = "https://beep.testnet.berachain.com";
33
+ const NAMED_LINK: HttpLink = { name: "Berachain Beep", uri: BASE };
34
+
35
+ const VAULT = "0x1111111111111111111111111111111111111111" as const;
36
+ const OWNER = "0x2222222222222222222222222222222222222222" as const;
37
+
38
+ describe("fetchOpenApi", () => {
39
+ beforeEach(() => {
40
+ beraFetchJson.mockReset();
41
+ beraFetchJson.mockResolvedValue(undefined);
42
+ });
43
+
44
+ it("substitutes path params and serializes the query", async () => {
45
+ await fetchOpenApi<beepPaths, "/v0/stake/{vault}/stats-by-day">(
46
+ NAMED_LINK,
47
+ "/v0/stake/{vault}/stats-by-day",
48
+ { path: { vault: VAULT }, query: { days: "30" } },
49
+ "beep",
50
+ );
51
+
52
+ expect(beraFetchJson).toHaveBeenCalledTimes(1);
53
+ expect(beraFetchJson).toHaveBeenCalledWith({
54
+ url: `${BASE}/v0/stake/${VAULT}/stats-by-day?days=30`,
55
+ name: "Berachain Beep",
56
+ type: "rest",
57
+ });
58
+ });
59
+
60
+ it("substitutes multiple path params and omits the query when absent", async () => {
61
+ await fetchOpenApi<beepPaths, "/v0/stake/{vault}/earnings/{owner}">(
62
+ NAMED_LINK,
63
+ "/v0/stake/{vault}/earnings/{owner}",
64
+ { path: { vault: VAULT, owner: OWNER } },
65
+ "beep",
66
+ );
67
+
68
+ expect(beraFetchJson).toHaveBeenCalledWith({
69
+ url: `${BASE}/v0/stake/${VAULT}/earnings/${OWNER}`,
70
+ name: "Berachain Beep",
71
+ type: "rest",
72
+ });
73
+ });
74
+
75
+ it("url-encodes path param values", async () => {
76
+ // `pubkey` is a free-form string segment; reserved chars must be encoded.
77
+ await fetchOpenApi<beepPaths, "/v0/validators/{pubkey}/incentives">(
78
+ NAMED_LINK,
79
+ "/v0/validators/{pubkey}/incentives",
80
+ { path: { pubkey: "a/b c" } },
81
+ "beep",
82
+ );
83
+
84
+ expect(beraFetchJson).toHaveBeenCalledWith(
85
+ expect.objectContaining({
86
+ url: `${BASE}/v0/validators/a%2Fb%20c/incentives`,
87
+ }),
88
+ );
89
+ });
90
+
91
+ it("falls back to the provided name for a string link", async () => {
92
+ await fetchOpenApi<beepPaths, "/v0/validators">(
93
+ BASE,
94
+ "/v0/validators",
95
+ {},
96
+ "beep",
97
+ );
98
+
99
+ expect(beraFetchJson).toHaveBeenCalledWith(
100
+ expect.objectContaining({ name: "beep" }),
101
+ );
102
+ });
103
+
104
+ it("throws when a required path param is missing", () => {
105
+ expect(() =>
106
+ fetchOpenApi<beepPaths, "/v0/stake/{vault}/earnings/{owner}">(
107
+ NAMED_LINK,
108
+ "/v0/stake/{vault}/earnings/{owner}",
109
+ // @ts-expect-error — `vault` is required by the schema for this path.
110
+ { path: { owner: OWNER } },
111
+ "beep",
112
+ ),
113
+ ).toThrow(/missing path param "vault"/);
114
+ });
115
+
116
+ it("returns the response from beraFetchJson", async () => {
117
+ const response = [{ total_assets: "1", total_supply: "2", timestamp: 3 }];
118
+ beraFetchJson.mockResolvedValue(response);
119
+
120
+ const result = await fetchOpenApi<
121
+ beepPaths,
122
+ "/v0/stake/{vault}/stats-by-day"
123
+ >(
124
+ NAMED_LINK,
125
+ "/v0/stake/{vault}/stats-by-day",
126
+ { path: { vault: VAULT }, query: { days: "30" } },
127
+ "beep",
128
+ );
129
+
130
+ expect(result).toBe(response);
131
+ });
132
+ });
133
+
134
+ describe("fetchBeep", () => {
135
+ beforeEach(() => {
136
+ beraFetchJson.mockReset();
137
+ beraFetchJson.mockResolvedValue(undefined);
138
+ });
139
+
140
+ it("resolves the base URL from config.beep", async () => {
141
+ await fetchBeep(
142
+ "/v0/stake/{vault}/stats-by-day",
143
+ { path: { vault: VAULT }, query: { days: "30" } },
144
+ { config: bepolia },
145
+ );
146
+
147
+ expect(beraFetchJson).toHaveBeenCalledWith(
148
+ expect.objectContaining({
149
+ url: `${BASE}/v0/stake/${VAULT}/stats-by-day?days=30`,
150
+ name: "Berachain Beep",
151
+ type: "rest",
152
+ }),
153
+ );
154
+ });
155
+
156
+ it("throws when config.beep is not set (e.g. mainnet)", () => {
157
+ expect(() =>
158
+ fetchBeep(
159
+ "/v0/stake/{vault}/earnings/{owner}",
160
+ { path: { vault: VAULT, owner: OWNER } },
161
+ { config: mainnet },
162
+ ),
163
+ ).toThrow(/`beep` backend is not configured/);
164
+ });
165
+ });
166
+
167
+ // Type-only assertions. The bodies are never executed at runtime (they would
168
+ // hit the `beep` not-configured throw); they exist so `tsc` validates inference
169
+ // and the `@ts-expect-error` lines. `expectTypeOf` is purely compile-time.
170
+ describe("fetchBeep types", () => {
171
+ it("infers the response type per path", () => {
172
+ const _assert = () => {
173
+ expectTypeOf(
174
+ fetchBeep("/v0/stake/{vault}/stats-by-day", {
175
+ path: { vault: VAULT },
176
+ query: { days: "30" },
177
+ }),
178
+ ).resolves.toEqualTypeOf<StakeStatsByDayResponse>();
179
+
180
+ expectTypeOf(
181
+ fetchBeep("/v0/stake/{vault}/earnings/{owner}", {
182
+ path: { vault: VAULT, owner: OWNER },
183
+ }),
184
+ ).resolves.toEqualTypeOf<StakeEarningResponse>();
185
+
186
+ expectTypeOf(
187
+ fetchBeep("/v0/validators", {}),
188
+ ).resolves.toEqualTypeOf<ValidatorsResponse>();
189
+ };
190
+ assert.ok(_assert);
191
+ });
192
+
193
+ it("rejects unknown paths", () => {
194
+ const _assert = () => {
195
+ // @ts-expect-error — not a path in the beep schema.
196
+ fetchBeep("/v0/not/a/real/path", { path: {} });
197
+ };
198
+ assert.ok(_assert);
199
+ });
200
+
201
+ it("rejects unknown path params", () => {
202
+ const _assert = () => {
203
+ fetchBeep("/v0/stake/{vault}/stats-by-day", {
204
+ // @ts-expect-error — `owner` is not a param of stats-by-day.
205
+ path: { vault: VAULT, owner: OWNER },
206
+ query: { days: "30" },
207
+ });
208
+ };
209
+ assert.ok(_assert);
210
+ });
211
+
212
+ it("constrains query values to the schema enum", () => {
213
+ const _assert = () => {
214
+ fetchBeep("/v0/stake/{vault}/stats-by-day", {
215
+ path: { vault: VAULT },
216
+ // @ts-expect-error — `7` is not an allowed `days` value.
217
+ query: { days: "7" },
218
+ });
219
+ };
220
+
221
+ assert.ok(_assert);
222
+ });
223
+ });
@@ -0,0 +1,34 @@
1
+ import type { paths } from "@berachain/graphql/api";
2
+
3
+ import { parseBaseArgs } from "../../utils/parseBaseArgs";
4
+ import {
5
+ type DefinedParams,
6
+ fetchOpenApi,
7
+ type GetPath,
8
+ type GetResponse,
9
+ } from "./fetchOpenApi";
10
+
11
+ /**
12
+ * Typed GET against the legacy Railway backend.
13
+ *
14
+ * The `path`, its path/query params and the response are all type-checked
15
+ * against the Railway schema (`@berachain/graphql/api`). The base URL is
16
+ * resolved from `config.backend`.
17
+ *
18
+ * @deprecated The Railway backend is being replaced by beep; prefer
19
+ * {@link fetchBeep} where `config.beep` is available.
20
+ */
21
+ export function fetchRailwayBackend<P extends GetPath<paths>>(
22
+ path: P,
23
+ params: DefinedParams<paths, P>,
24
+ args: BeraJS.BaseFunctionArgs = {},
25
+ ): Promise<GetResponse<paths, P>> {
26
+ const { config } = parseBaseArgs(args);
27
+
28
+ return fetchOpenApi<paths, P>(
29
+ config.backend,
30
+ path,
31
+ params,
32
+ "backend-railway",
33
+ );
34
+ }
@@ -31,6 +31,24 @@ export async function getEnsoUserTokensWithBalances({
31
31
  eoaAddress: account,
32
32
  });
33
33
 
34
+ // The Enso SDK can return a non-array (e.g. an error object) on a malformed
35
+ // or empty response. Guard before mapping to avoid the unactionable minified
36
+ // "(...).map is not a function" crash — but still report it as a typed,
37
+ // stably-grouped warning so we don't silently swallow an Enso API problem
38
+ // (an empty portfolio would otherwise look like "user has no tokens").
39
+ if (!Array.isArray(ensoBalances)) {
40
+ BeraMonitoring.captureException(
41
+ new InvalidArgumentError({
42
+ property: "ensoBalances",
43
+ value: ensoBalances,
44
+ expected: "BalanceToken[]",
45
+ chainId: typeof chainId === "number" ? chainId : undefined,
46
+ extra: { account, chainId },
47
+ }),
48
+ );
49
+ return [];
50
+ }
51
+
34
52
  const tokens: BalanceToken[] = ensoBalances.map(
35
53
  (balance) =>
36
54
  ({
@@ -43,6 +43,7 @@ export * from "./honey/isBadCollateralAsset";
43
43
  export * from "./honey/isBasketModeEnabled";
44
44
  export * from "./misc/getBlockTimestamp";
45
45
  export * from "./pol/getAutoclaimedIncentives";
46
+ export * from "./pol/getAutoclaimedIncentivesTxHash";
46
47
  export * from "./pol/getBeraTokenTotalSupply";
47
48
  export * from "./pol/getBgtAprSimulation";
48
49
  export * from "./pol/getBgtIncentiveDistributorPaused";
@@ -1,8 +1,15 @@
1
1
  import { HermesClient } from "@pythnetwork/hermes-client";
2
2
  import { formatUnits } from "viem";
3
3
 
4
+ import { polyfillAbortSignalAny } from "../../utils/polyfillAbortSignalAny";
5
+
4
6
  const pythEndpoint = "https://hermes.pyth.network";
5
7
 
8
+ // @pythnetwork/hermes-client uses AbortSignal.any, which is missing on older
9
+ // browsers/WebViews (e.g. Chrome Mobile WebView 114). Install the polyfill
10
+ // before the client runs so getLatestPriceUpdates doesn't throw. See HONEY-188.
11
+ polyfillAbortSignalAny();
12
+
6
13
  /**
7
14
  * Interface representing the latest Pyth price updates
8
15
  * @interface PythLatestUpdates
@@ -25,7 +25,7 @@ describe("rewardVaults", async () => {
25
25
  it("should have consistent data", () => {
26
26
  const totalBgtCatpure = gaugeList.reduce(
27
27
  (acc, vault) =>
28
- acc + Number(vault.dynamicData?.bgtCapturePercentage ?? 0),
28
+ acc + Number(vault.dynamicData?.rewardCapturePercentage ?? 0),
29
29
  0,
30
30
  );
31
31
 
@@ -1,5 +1,6 @@
1
1
  import type { Address } from "viem";
2
2
 
3
+ import { RequestError } from "../../errors/RequestError";
3
4
  import type { AutoclaimedIncentivesResponse } from "../../types/bribe-boost";
4
5
  import { beraFetchJson } from "../../utils/beraFetch";
5
6
  import { parseBaseArgs } from "../../utils/parseBaseArgs";
@@ -8,6 +9,9 @@ import { parseBaseArgs } from "../../utils/parseBaseArgs";
8
9
  * Server-side function to fetch the incentives the bot autoclaimed on behalf
9
10
  * of a wallet during the BGT → BERA cutover. Only valid to call once the
10
11
  * bgtIncentiveDistributor contract is paused.
12
+ *
13
+ * Returns `null` on 404 — the endpoint 404s when the bot has not yet processed
14
+ * the wallet, which is a normal "no data yet" state rather than an error.
11
15
  */
12
16
  export async function getAutoclaimedIncentives({
13
17
  account,
@@ -17,14 +21,21 @@ export async function getAutoclaimedIncentives({
17
21
  } & BeraJS.BaseFunctionArgs): Promise<AutoclaimedIncentivesResponse | null> {
18
22
  const { config } = parseBaseArgs(args);
19
23
 
20
- return beraFetchJson<AutoclaimedIncentivesResponse>(
21
- {
22
- url: `${
23
- config.pol.bribeBoostApi
24
- }/api/v1/wallets/${account.toLowerCase()}/autoclaimed`,
25
- name: "pol-autoclaimed-incentives",
26
- type: "rest",
27
- },
28
- { cache: "no-store" },
29
- );
24
+ try {
25
+ return await beraFetchJson<AutoclaimedIncentivesResponse>(
26
+ {
27
+ url: `${
28
+ config.pol.bribeBoostApi
29
+ }/api/v1/wallets/${account.toLowerCase()}/autoclaimed`,
30
+ name: "pol-autoclaimed-incentives",
31
+ type: "rest",
32
+ },
33
+ { cache: "no-store" },
34
+ );
35
+ } catch (error) {
36
+ if (error instanceof RequestError && error.statusCode === 404) {
37
+ return null;
38
+ }
39
+ throw error;
40
+ }
30
41
  }