@berachain/berajs 0.2.9 → 0.2.11
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/{HoneyConfigProvider-Dkj-_a5x.d.ts → HoneyConfigProvider-COOuDNra.d.ts} +1 -1
- package/dist/actions/clients/exports.d.ts +77 -1
- package/dist/actions/clients/exports.mjs +13 -4
- package/dist/actions/exports.d.ts +121 -39
- package/dist/actions/exports.mjs +75 -34
- package/dist/actions/governance/exports.d.ts +3 -11
- package/dist/actions/governance/exports.mjs +3 -3
- package/dist/actions/server/exports.mjs +4 -4
- package/dist/{chunk-4Z4AK6SH.mjs → chunk-3JJLQ2JX.mjs} +3 -3
- package/dist/{chunk-EXIUPSFN.mjs → chunk-7YVNSDXG.mjs} +2 -2
- package/dist/{chunk-WXXOISTU.mjs → chunk-AUOPN6NK.mjs} +1 -1
- package/dist/{chunk-75M6TF7M.mjs → chunk-DQRH5VE3.mjs} +1 -1
- package/dist/{chunk-CDFWPU2R.mjs → chunk-E7YFXBBQ.mjs} +0 -124
- package/dist/{chunk-AFN4CVD3.mjs → chunk-GUURQAME.mjs} +1 -1
- package/dist/{chunk-KQUMKB66.mjs → chunk-GY6B3PD5.mjs} +1 -1
- package/dist/{chunk-HSSJKHZ4.mjs → chunk-HYDP32P6.mjs} +3 -3
- package/dist/{chunk-NPBQLVL3.mjs → chunk-IXIBY5FP.mjs} +2 -2
- package/dist/{chunk-J5I45WGQ.mjs → chunk-KHXJDYA4.mjs} +7 -0
- package/dist/chunk-MRQGHXAN.mjs +54 -0
- package/dist/{chunk-FFB5LFDW.mjs → chunk-QVHEM4BG.mjs} +2 -2
- package/dist/{chunk-3EARVV7K.mjs → chunk-SXUNCX5E.mjs} +22 -9
- package/dist/chunk-UD5IUNCW.mjs +34 -0
- package/dist/{chunk-QJIXTYTZ.mjs → chunk-VAA2FVPP.mjs} +162 -41
- package/dist/chunk-Y6THHG77.mjs +126 -0
- package/dist/{chunk-XIYN6AL6.mjs → chunk-ZLTMIFCZ.mjs} +10 -5
- package/dist/contexts/exports.d.ts +2 -2
- package/dist/contexts/exports.mjs +11 -9
- package/dist/enum/exports.d.ts +8 -2
- package/dist/errors/exports.mjs +5 -5
- package/dist/getProposalVotes-DAUrdX2n.d.ts +12 -0
- package/dist/{getValidatorQueuedOperatorAddress-Dw5KN5sh.d.ts → getValidatorQueuedOperatorAddress-Cxt-DlL_.d.ts} +2 -2
- package/dist/{global.d-BuGDKh4k.d.ts → global.d-q_LQWQqs.d.ts} +2 -4
- package/dist/hooks/exports.d.ts +114 -164
- package/dist/hooks/exports.mjs +535 -522
- package/dist/hooks/governance/exports.d.ts +20 -15
- package/dist/hooks/governance/exports.mjs +43 -41
- package/dist/{pol.d-CqPA9K6m.d.ts → pol.d-CeRgXBL8.d.ts} +33 -8
- package/dist/types/exports.d.ts +5 -5
- package/dist/{useHoneySwapState-vFmuFF0g.d.ts → useHoneySwapState-twi7NTaO.d.ts} +1 -1
- package/dist/utils/exports.d.ts +2 -2
- package/dist/utils/exports.mjs +15 -9
- package/package.json +9 -8
- package/src/actions/bend/getMaxDeposit.ts +28 -2
- package/src/actions/clients/exports.ts +3 -0
- package/src/actions/clients/fetchBeep.ts +34 -0
- package/src/actions/clients/fetchOpenApi.ts +97 -0
- package/src/actions/clients/fetchOpenApi.unit.test.ts +245 -0
- package/src/actions/clients/fetchRailwayBackend.ts +34 -0
- package/src/actions/enso/getEnsoUserTokensWithBalances.ts +18 -0
- package/src/actions/exports.ts +2 -0
- package/src/actions/honey/getChartData.ts +53 -12
- package/src/actions/honey/getHoney24hVolume.ts +34 -6
- package/src/actions/honey/getHoneyTxns.ts +93 -0
- package/src/actions/honey/getPythLatestPrices.ts +7 -0
- package/src/actions/pol/__tests__/rewardVaults.integration.test.ts +1 -1
- package/src/actions/pol/getAutoclaimedIncentives.ts +21 -10
- package/src/actions/pol/getAutoclaimedIncentivesTxHash.ts +41 -0
- package/src/actions/pol/getBgtIncentiveDistributorPaused.ts +5 -12
- package/src/actions/pol/getEarnedStakedBeraVault.ts +20 -16
- package/src/actions/pol/getRewardVaults.ts +4 -4
- package/src/actions/pol/getStakingDailyAssets.ts +18 -14
- package/src/actions/validators/getValidatorIncentiveDistribution.ts +64 -12
- package/src/actions/validators/utils/getValidatorBoostApy.ts +1 -1
- package/src/contexts/SwrFallback.tsx +2 -1
- package/src/data/contracts.ts +4 -0
- package/src/errors/RequestError.ts +12 -3
- package/src/errors/RequestError.unit.test.ts +55 -0
- package/src/errors/errorMap.ts +8 -0
- package/src/errors/getRevertReason.integration.test.ts +5 -1
- package/src/hooks/bend/useGetConvertToAssets.ts +2 -3
- package/src/hooks/dex/useAggregatorsQuotes.ts +10 -10
- package/src/hooks/dex/useAggregatorsRouterFeeBps.ts +1 -1
- package/src/hooks/dex/useAllUserPools.ts +7 -5
- package/src/hooks/dex/useApiPool.ts +1 -1
- package/src/hooks/dex/useGlobalLiquidityAndSwapVolume.ts +1 -1
- package/src/hooks/dex/useOnChainPoolData.ts +1 -1
- package/src/hooks/dex/usePollPoolCreationRelayerApproval.ts +2 -2
- package/src/hooks/dex/usePoolEvents.ts +1 -2
- package/src/hooks/dex/usePoolHistoricalData.ts +2 -3
- package/src/hooks/dex/usePools.ts +4 -2
- package/src/hooks/dex/useSingleAggregatorQuote.ts +6 -18
- package/src/hooks/enso/useBendDemultiply.ts +3 -4
- package/src/hooks/enso/useBendMultiply.ts +3 -4
- package/src/hooks/enso/useBendZapSupply.ts +3 -4
- package/src/hooks/enso/useEnsoSwapBundle.ts +1 -1
- package/src/hooks/enso/useEnsoUserTokensWithBalances.ts +3 -5
- package/src/hooks/enso/useEnsoWalletV2Address.ts +1 -1
- package/src/hooks/enso/useIsBendAuthorized.ts +1 -1
- package/src/hooks/enso/useZapStakeBera.ts +2 -2
- package/src/hooks/exports.ts +2 -0
- package/src/hooks/governance/useGetPastVotes.ts +1 -1
- package/src/hooks/governance/useHasVoted.ts +1 -1
- package/src/hooks/governance/useIsCanceller.ts +1 -1
- package/src/hooks/governance/usePollAllProposals.ts +13 -12
- package/src/hooks/governance/usePollProposal.ts +3 -3
- package/src/hooks/governance/usePollProposalThreshold.ts +1 -1
- package/src/hooks/governance/usePollProposalVotes.ts +23 -5
- package/src/hooks/governance/usePollUserDelegates.ts +3 -3
- package/src/hooks/governance/useProposalFromTx.ts +2 -1
- package/src/hooks/governance/useProposalSnapshot.ts +2 -3
- package/src/hooks/governance/useProposalState.ts +2 -2
- package/src/hooks/governance/useProposalTimelockState.ts +2 -1
- package/src/hooks/governance/useQuorum.ts +1 -2
- package/src/hooks/honey/useCappedGlobally.ts +3 -6
- package/src/hooks/honey/useCappedRelatively.ts +2 -2
- package/src/hooks/honey/useCollateralWeights.ts +3 -3
- package/src/hooks/honey/useHoney24hVolume.ts +1 -1
- package/src/hooks/honey/useHoneyBalances.ts +1 -1
- package/src/hooks/honey/useHoneyChartData.ts +1 -1
- package/src/hooks/honey/useHoneyVaultsBalance.ts +2 -3
- package/src/hooks/honey/useIsBadCollateralAsset.ts +4 -7
- package/src/hooks/honey/useIsBasketModeEnabled.ts +4 -7
- package/src/hooks/honey/usePythLatestPrices.ts +13 -9
- package/src/hooks/perps/usePythUpdateFee.ts +13 -11
- package/src/hooks/pol/useAutoclaimedIncentives.ts +2 -12
- package/src/hooks/pol/useAutoclaimedIncentivesTxHash.ts +44 -0
- package/src/hooks/pol/useBgtIncentiveDistributorPaused.ts +39 -0
- package/src/hooks/pol/useBgtUnstakedBalance.ts +2 -2
- package/src/hooks/pol/useClaimableFees.ts +1 -1
- package/src/hooks/pol/useHighestVaultsAPR.ts +4 -6
- package/src/hooks/pol/useOnChainRewardVault.ts +77 -72
- package/src/hooks/pol/usePollGlobalData.ts +2 -3
- package/src/hooks/pol/usePollMarkets.ts +2 -2
- package/src/hooks/pol/useQueuedBeraUnlock.ts +2 -2
- package/src/hooks/pol/useRewardTokenToBeraRate.ts +2 -2
- package/src/hooks/pol/useRewardVault.ts +7 -6
- package/src/hooks/pol/useRewardVaultBalanceFromStakingToken.ts +4 -4
- package/src/hooks/pol/useRewardVaultFromToken.ts +1 -1
- package/src/hooks/pol/useRewardVaultIncentives.ts +1 -1
- package/src/hooks/pol/useRewardVaultRewards.ts +2 -2
- package/src/hooks/pol/useRewardVaults.ts +3 -6
- package/src/hooks/pol/useStakedAPR.ts +1 -2
- package/src/hooks/pol/useStakedData.ts +90 -41
- package/src/hooks/pol/useStakedSnapshots.ts +1 -1
- package/src/hooks/pol/useStakingVaultsMetadata.ts +1 -1
- package/src/hooks/pol/useTotalStakedAmount.ts +1 -2
- package/src/hooks/pol/useUserVaultInfo.ts +2 -2
- package/src/hooks/pol/useUserVaults.ts +2 -3
- package/src/hooks/pol/useVaultAddress.ts +1 -1
- package/src/hooks/pol/useVaultHistory.ts +1 -2
- package/src/hooks/pol/useVaultValidators.ts +2 -2
- package/src/hooks/tokens/useMultipleTokenInformation.ts +2 -2
- package/src/hooks/tokens/usePollAllowances.ts +3 -4
- package/src/hooks/tokens/usePollBalance.ts +2 -2
- package/src/hooks/tokens/usePollWalletBalances.ts +2 -2
- package/src/hooks/tokens/useStakingTokenInformation.ts +2 -2
- package/src/hooks/tokens/useTokenCurrentPrices.ts +11 -13
- package/src/hooks/tokens/useTokenInformation.ts +2 -2
- package/src/hooks/tokens/useTokenPrice.ts +2 -1
- package/src/hooks/tokens/useTokenPrices.ts +3 -4
- package/src/hooks/tokens/useTotalSupply.ts +1 -1
- package/src/hooks/tokens/useUnderlyingAsset.ts +1 -2
- package/src/hooks/useBlockToTimestamp.ts +1 -2
- package/src/hooks/useGetVerifiedAbi.ts +2 -1
- package/src/hooks/validators/useAllValidators.ts +2 -3
- package/src/hooks/validators/useApiEnrichedAllocation.ts +1 -1
- package/src/hooks/validators/useApiValidator.ts +3 -6
- package/src/hooks/validators/useBaselineRewardAllocation.ts +1 -2
- package/src/hooks/validators/useDailyValidatorBlockStats.ts +1 -1
- package/src/hooks/validators/useDefaultRewardAllocation.ts +2 -3
- package/src/hooks/validators/useManagedValidatorRole.ts +2 -2
- package/src/hooks/validators/useOnChainValidator.ts +3 -3
- package/src/hooks/validators/useStakingPoolBatch.ts +3 -4
- package/src/hooks/validators/useUserActiveValidators.ts +2 -3
- package/src/hooks/validators/useUserBoostsOnValidator.ts +1 -1
- package/src/hooks/validators/useUserClaimableIncentives.ts +1 -1
- package/src/hooks/validators/useUserStakingPositions.ts +2 -1
- package/src/hooks/validators/useValidator.ts +4 -8
- package/src/hooks/validators/useValidatorAnalytics.ts +1 -1
- package/src/hooks/validators/useValidatorCommission.ts +2 -2
- package/src/hooks/validators/useValidatorIncentiveDistribution.ts +3 -3
- package/src/hooks/validators/useValidatorQueuedCommission.ts +2 -2
- package/src/hooks/validators/useValidatorQueuedOperatorAddress.ts +2 -2
- package/src/hooks/validators/useValidatorQueuedRewardAllocation.ts +2 -2
- package/src/hooks/validators/useValidatorRewardAllocation.ts +2 -2
- package/src/types/bribe-boost.d.ts +14 -3
- package/src/types/global.d.ts +3 -4
- package/src/types/pol.d.ts +17 -3
- package/src/utils/polyfillAbortSignalAny.ts +53 -0
- package/src/utils/polyfillAbortSignalAny.unit.test.ts +81 -0
- package/dist/polling-BKnyavLI.d.ts +0 -8
- /package/dist/{exports-BcUTGFUb.d.ts → getApolloClient-BcUTGFUb.d.ts} +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { HoneyTransactionsLatestResponse } from "@berachain/graphql/beep";
|
|
2
|
+
import type { GetHoneyTxnQuery } from "@berachain/graphql/honey";
|
|
3
|
+
|
|
4
|
+
import { BeraError } from "../../errors/BeraError";
|
|
5
|
+
import { beraFetchJson } from "../../utils/beraFetch";
|
|
6
|
+
import { parseBaseArgs } from "../../utils/parseBaseArgs";
|
|
7
|
+
import { fetchBeep } from "../clients/fetchBeep";
|
|
8
|
+
|
|
9
|
+
type HoneyTxn = HoneyTransactionsLatestResponse[number];
|
|
10
|
+
/** beep's transaction type discriminator (`"MINT" | "REDEEM"`). */
|
|
11
|
+
type HoneyTxnType = HoneyTxn["type"];
|
|
12
|
+
|
|
13
|
+
interface GetHoneyTxnsArgs {
|
|
14
|
+
/** Zero-based page index (the page size is applied internally). */
|
|
15
|
+
index: number;
|
|
16
|
+
/** Page size. */
|
|
17
|
+
limit: number;
|
|
18
|
+
/** Optional MINT/REDEEM filter; omit for all transactions. */
|
|
19
|
+
txnType?: HoneyTxnType;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Browser-only. Returns a page of honey transactions in the `beep` response
|
|
24
|
+
* shape (`HoneyTransactionsLatestResponse`) the transactions table consumes.
|
|
25
|
+
*
|
|
26
|
+
* Reads from the `beep` backend (`fetchBeep`) when configured, falling back to
|
|
27
|
+
* the legacy honey subgraph proxied through `/api/honey/txns` (whose GraphQL
|
|
28
|
+
* result is adapted into the beep shape) otherwise.
|
|
29
|
+
*/
|
|
30
|
+
export async function getHoneyTxns({
|
|
31
|
+
index,
|
|
32
|
+
limit,
|
|
33
|
+
txnType,
|
|
34
|
+
}: GetHoneyTxnsArgs): Promise<HoneyTransactionsLatestResponse> {
|
|
35
|
+
if (typeof window === "undefined") {
|
|
36
|
+
throw new BeraError({
|
|
37
|
+
message:
|
|
38
|
+
"getHoneyTxns is browser-only; call from a Client Component / SWR hook",
|
|
39
|
+
level: "error",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { config } = parseBaseArgs({});
|
|
44
|
+
|
|
45
|
+
// TODO(beep): remove this entire block once `beep` is deployed on every
|
|
46
|
+
// chain. It is the only legacy honey-subgraph code left here; deleting it
|
|
47
|
+
// leaves the `beep` path below as the sole implementation.
|
|
48
|
+
if (!config.beep) {
|
|
49
|
+
const params = new URLSearchParams({
|
|
50
|
+
skip: String(index * limit),
|
|
51
|
+
limit: String(limit),
|
|
52
|
+
});
|
|
53
|
+
if (txnType) params.set("type", txnType);
|
|
54
|
+
|
|
55
|
+
const txns = await beraFetchJson<GetHoneyTxnQuery["honeyTxns"]>({
|
|
56
|
+
url: `/api/honey/txns?${params}`,
|
|
57
|
+
name: "honey-txns",
|
|
58
|
+
type: "rest",
|
|
59
|
+
});
|
|
60
|
+
return txns.map(fromHoneyTxn);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// beep backend (next-gen), typed against `@berachain/graphql/beep`. Queried
|
|
64
|
+
// directly from the browser — no Vercel proxy. beep's `page` is 1-indexed and
|
|
65
|
+
// filters server-side by `type`; `fetchOpenApi` omits `type` when `txnType`
|
|
66
|
+
// is undefined (the "all" tab), so each tab paginates its own filtered feed.
|
|
67
|
+
return fetchBeep("/v0/honey/transactions/latest", {
|
|
68
|
+
query: { page: String(index + 1), perPage: String(limit), type: txnType },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Adapt a legacy subgraph transaction into the beep `HoneyTransactionsLatest`
|
|
74
|
+
* shape: nested `chainTransaction.txHash` → flat `txHash`, `collateral` →
|
|
75
|
+
* `collaterals` (`collateral`/`collateralAmount` → `address`/`amount`).
|
|
76
|
+
*
|
|
77
|
+
* The subgraph reports `timestamp` in seconds (matching beep), so it passes
|
|
78
|
+
* through as a number. A subgraph txn always carries a `type` in practice.
|
|
79
|
+
*/
|
|
80
|
+
function fromHoneyTxn(txn: GetHoneyTxnQuery["honeyTxns"][number]): HoneyTxn {
|
|
81
|
+
return {
|
|
82
|
+
txHash: txn.chainTransaction.txHash,
|
|
83
|
+
type: (txn.type ?? "MINT") as HoneyTxnType,
|
|
84
|
+
collaterals: txn.collateral.map((collateral) => ({
|
|
85
|
+
address: collateral.collateral,
|
|
86
|
+
amount: collateral.collateralAmount,
|
|
87
|
+
})),
|
|
88
|
+
to: txn.to,
|
|
89
|
+
from: txn.from,
|
|
90
|
+
honeyAmount: txn.honeyAmount,
|
|
91
|
+
timestamp: Number(txn.timestamp),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -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?.
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
|
|
3
|
+
import { RequestError } from "../../errors/RequestError";
|
|
4
|
+
import type { AutoclaimedIncentivesTxHashResponse } from "../../types/bribe-boost";
|
|
5
|
+
import { beraFetchJson } from "../../utils/beraFetch";
|
|
6
|
+
import { parseBaseArgs } from "../../utils/parseBaseArgs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Companion to `getAutoclaimedIncentives` that fetches the on-chain tx hash
|
|
10
|
+
* for each autoclaimed token. Same nested shape as `/autoclaimed`, but each
|
|
11
|
+
* token leaf is `{ txhash }` where the hash may be absent or empty while the
|
|
12
|
+
* bot is still in flight.
|
|
13
|
+
*
|
|
14
|
+
* Returns `null` on 404 (bot has not run yet for this wallet).
|
|
15
|
+
*/
|
|
16
|
+
export async function getAutoclaimedIncentivesTxHash({
|
|
17
|
+
account,
|
|
18
|
+
...args
|
|
19
|
+
}: {
|
|
20
|
+
account: Address;
|
|
21
|
+
} & BeraJS.BaseFunctionArgs): Promise<AutoclaimedIncentivesTxHashResponse | null> {
|
|
22
|
+
const { config } = parseBaseArgs(args);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
return await beraFetchJson<AutoclaimedIncentivesTxHashResponse>(
|
|
26
|
+
{
|
|
27
|
+
url: `${
|
|
28
|
+
config.pol.bribeBoostApi
|
|
29
|
+
}/api/v1/wallets/${account.toLowerCase()}/autoclaimed/txhash`,
|
|
30
|
+
name: "pol-autoclaimed-incentives-txhash",
|
|
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
|
+
}
|
|
41
|
+
}
|
|
@@ -9,8 +9,6 @@ import { parseBaseArgs } from "../../utils/parseBaseArgs";
|
|
|
9
9
|
* Reads `paused()` on the BGT incentive distributor. Used to gate the
|
|
10
10
|
* autoclaim banner — when `true`, users can no longer self-claim and the
|
|
11
11
|
* bot is responsible for distributing incentives on their behalf.
|
|
12
|
-
*
|
|
13
|
-
* Fails closed: any read error returns `false` so the banner stays hidden.
|
|
14
12
|
*/
|
|
15
13
|
export async function getBgtIncentiveDistributorPaused({
|
|
16
14
|
publicClient,
|
|
@@ -22,14 +20,9 @@ export async function getBgtIncentiveDistributorPaused({
|
|
|
22
20
|
|
|
23
21
|
const { config } = parseBaseArgs(args);
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
return paused;
|
|
32
|
-
} catch {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
23
|
+
return publicClient.readContract({
|
|
24
|
+
address: config.pol.bgtIncentiveDistributor,
|
|
25
|
+
abi: bgtIncentiveDistributorAbi,
|
|
26
|
+
functionName: "paused",
|
|
27
|
+
});
|
|
35
28
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type { Address } from "viem";
|
|
2
2
|
|
|
3
|
-
import { getUriFromLink } from "@berachain/config";
|
|
4
|
-
|
|
5
|
-
import type { VaultEarningResponse } from "@berachain/graphql/api";
|
|
6
|
-
|
|
7
|
-
import { beraFetchJson } from "../../utils/beraFetch";
|
|
8
3
|
import { parseBaseArgs } from "../../utils/parseBaseArgs";
|
|
4
|
+
import { fetchBeep } from "../clients/fetchBeep";
|
|
5
|
+
import { fetchRailwayBackend } from "../clients/fetchRailwayBackend";
|
|
9
6
|
|
|
10
7
|
type GetEarnedStakedBeraVaultArgs = {
|
|
11
8
|
/**
|
|
@@ -21,17 +18,24 @@ export async function getEarnedStakedBeraVault({
|
|
|
21
18
|
address,
|
|
22
19
|
account,
|
|
23
20
|
...args
|
|
24
|
-
}: BeraJS.BaseFunctionArgs &
|
|
25
|
-
GetEarnedStakedBeraVaultArgs): Promise<VaultEarningResponse> {
|
|
21
|
+
}: BeraJS.BaseFunctionArgs & GetEarnedStakedBeraVaultArgs) {
|
|
26
22
|
const { config } = parseBaseArgs(args);
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
24
|
+
// TODO(beep): remove this entire block once `beep` is deployed on every
|
|
25
|
+
// chain. It is the only legacy-Railway-specific code left here; deleting it
|
|
26
|
+
// leaves the `beep` path below as the sole implementation.
|
|
27
|
+
if (!config.beep) {
|
|
28
|
+
return fetchRailwayBackend(
|
|
29
|
+
"/vaults/{vault}/earnings/{owner}",
|
|
30
|
+
{ path: { vault: address, owner: account } },
|
|
31
|
+
args,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// beep backend (next-gen), typed against `@berachain/graphql/beep`.
|
|
36
|
+
return fetchBeep(
|
|
37
|
+
"/v0/stake/{vault}/earnings/{owner}",
|
|
38
|
+
{ path: { vault: address, owner: account } },
|
|
39
|
+
args,
|
|
40
|
+
);
|
|
37
41
|
}
|
|
@@ -87,13 +87,13 @@ export async function getRewardVaults({
|
|
|
87
87
|
...vault,
|
|
88
88
|
dynamicData: {
|
|
89
89
|
...vault.dynamicData,
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
allTimeRewards: vault.dynamicData?.allTimeRewards ?? "0",
|
|
91
|
+
rewardCapturePercentage:
|
|
92
|
+
vault.dynamicData?.rewardCapturePercentage ?? 0,
|
|
93
93
|
activeIncentivesValueUsd: totalIncentiveInUsdc.toString(),
|
|
94
94
|
activeIncentivesRateUsd:
|
|
95
95
|
vault.dynamicData?.activeIncentivesRateUsd ?? "0",
|
|
96
|
-
|
|
96
|
+
rewardCapturePerBlock: vault.dynamicData?.rewardCapturePerBlock ?? 0,
|
|
97
97
|
},
|
|
98
98
|
activeIncentives: incentivesArray,
|
|
99
99
|
} satisfies typeof vault;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type { Address } from "viem";
|
|
2
2
|
|
|
3
|
-
import { getUriFromLink } from "@berachain/config";
|
|
4
|
-
|
|
5
|
-
import type { VaultStatsByDayResponse } from "@berachain/graphql/api";
|
|
6
|
-
|
|
7
|
-
import { beraFetchJson } from "../../utils/beraFetch";
|
|
8
3
|
import { parseBaseArgs } from "../../utils/parseBaseArgs";
|
|
4
|
+
import { fetchBeep } from "../clients/fetchBeep";
|
|
5
|
+
import { fetchRailwayBackend } from "../clients/fetchRailwayBackend";
|
|
9
6
|
|
|
10
7
|
export async function getStakingDailyAssets({
|
|
11
8
|
address,
|
|
@@ -13,15 +10,22 @@ export async function getStakingDailyAssets({
|
|
|
13
10
|
}: {
|
|
14
11
|
address: Address;
|
|
15
12
|
range: 30 | 60 | 90;
|
|
16
|
-
})
|
|
13
|
+
}) {
|
|
17
14
|
const { config } = parseBaseArgs({});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
|
|
16
|
+
// TODO(beep): remove this entire block once `beep` is deployed on every
|
|
17
|
+
// chain. It is the only legacy-Railway-specific code left here; deleting it
|
|
18
|
+
// leaves the `beep` path below as the sole implementation.
|
|
19
|
+
if (!config.beep) {
|
|
20
|
+
return fetchRailwayBackend("/vaults/{vault}/stats-by-day", {
|
|
21
|
+
path: { vault: address },
|
|
22
|
+
query: { days: `${range}` },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// beep backend (next-gen), typed against `@berachain/graphql/beep`.
|
|
27
|
+
return fetchBeep("/v0/stake/{vault}/stats-by-day", {
|
|
28
|
+
path: { vault: address },
|
|
29
|
+
query: { days: `${range}` },
|
|
26
30
|
});
|
|
27
31
|
}
|
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import type { Address } from "viem";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { ValidatorIncentivesResponse } from "@berachain/graphql/beep";
|
|
4
4
|
|
|
5
5
|
import { BeraError } from "../../errors/BeraError";
|
|
6
6
|
import { beraFetchJson } from "../../utils/beraFetch";
|
|
7
|
+
import { calculateTimestampFromDays } from "../../utils/formatTimestamps";
|
|
8
|
+
import { parseBaseArgs } from "../../utils/parseBaseArgs";
|
|
9
|
+
import { fetchBeep } from "../clients/fetchBeep";
|
|
7
10
|
|
|
8
|
-
/** Browser-only.
|
|
11
|
+
/** Browser-only. Reads from beep when configured, else the subgraph proxy. */
|
|
9
12
|
export async function getValidatorIncentiveDistribution({
|
|
10
13
|
pubkey,
|
|
11
14
|
dayRange,
|
|
12
15
|
}: {
|
|
13
16
|
pubkey: Address;
|
|
14
17
|
dayRange: number;
|
|
15
|
-
}): Promise<
|
|
18
|
+
}): Promise<ValidatorIncentivesResponse | undefined> {
|
|
16
19
|
if (typeof window === "undefined") {
|
|
17
20
|
throw new BeraError({
|
|
18
21
|
message:
|
|
@@ -21,13 +24,62 @@ export async function getValidatorIncentiveDistribution({
|
|
|
21
24
|
});
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const { config } = parseBaseArgs({});
|
|
28
|
+
|
|
29
|
+
// TODO(beep): remove this entire block (and the subgraph proxy route at
|
|
30
|
+
// `/api/pol/validator-incentive-distribution`) once `beep` is deployed on
|
|
31
|
+
// every chain. beep then becomes the sole implementation. The route already
|
|
32
|
+
// maps the subgraph response into beep's `ValidatorIncentivesResponse` shape,
|
|
33
|
+
// so the client consumes a single (beep) type regardless of source.
|
|
34
|
+
if (!config.beep) {
|
|
35
|
+
const params = new URLSearchParams({
|
|
36
|
+
pubkey,
|
|
37
|
+
dayRange: String(dayRange),
|
|
38
|
+
});
|
|
39
|
+
return beraFetchJson<ValidatorIncentivesResponse>({
|
|
40
|
+
url: `/api/pol/validator-incentive-distribution?${params}`,
|
|
41
|
+
name: "pol-validator-incentive-distribution",
|
|
42
|
+
type: "rest",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// beep backend (next-gen). It is a public (`NEXT_PUBLIC_*`) URL, so it can be
|
|
47
|
+
// called directly from the browser; the subgraph above stays behind the proxy
|
|
48
|
+
// because it needs server-side auth headers.
|
|
49
|
+
//
|
|
50
|
+
// beep's `after` cursor is in seconds; `calculateTimestampFromDays` returns
|
|
51
|
+
// microseconds, matching the subgraph's `timestamp_gte`. Pin it to the start
|
|
52
|
+
// of the target day (UTC) so the cursor is stable within a day instead of
|
|
53
|
+
// sliding every second — beep buckets by `interval: "day"` anyway, and a
|
|
54
|
+
// stable value keeps requests cacheable.
|
|
55
|
+
const SECONDS_PER_DAY = 86_400;
|
|
56
|
+
const after = String(
|
|
57
|
+
Math.floor(
|
|
58
|
+
calculateTimestampFromDays(dayRange) / 1_000_000 / SECONDS_PER_DAY,
|
|
59
|
+
) * SECONDS_PER_DAY,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// beep defaults to 100 rows/page, so page until a short page to gather the
|
|
63
|
+
// full range (matches the subgraph's `first: 1000`). In practice this is a
|
|
64
|
+
// single request — a busy validator is ~325 rows over the UI's max 90-day
|
|
65
|
+
// window, well under one page.
|
|
66
|
+
const PER_PAGE = 1000;
|
|
67
|
+
const rows: ValidatorIncentivesResponse = [];
|
|
68
|
+
let page = 1;
|
|
69
|
+
while (true) {
|
|
70
|
+
const pageRows = await fetchBeep("/v0/validators/{pubkey}/incentives", {
|
|
71
|
+
path: { pubkey },
|
|
72
|
+
query: {
|
|
73
|
+
interval: "day",
|
|
74
|
+
after,
|
|
75
|
+
page: String(page),
|
|
76
|
+
perPage: String(PER_PAGE),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
rows.push(...pageRows);
|
|
80
|
+
if (pageRows.length < PER_PAGE) break;
|
|
81
|
+
page++;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return rows;
|
|
33
85
|
}
|
|
@@ -51,7 +51,7 @@ export function getValidatorBoostApy({
|
|
|
51
51
|
|
|
52
52
|
const boostApy =
|
|
53
53
|
(returnPerBgt *
|
|
54
|
-
Number(validator.dynamicData?.
|
|
54
|
+
Number(validator.dynamicData?.lastDayDistributedRewards ?? 0) *
|
|
55
55
|
365) /
|
|
56
56
|
(validatorActiveBoostAmount * bgtPrice);
|
|
57
57
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { SWRConfig } from "@berachain/utils/pkg/swr";
|
|
4
5
|
|
|
5
6
|
import { BeraError } from "../errors/BeraError";
|
|
6
7
|
import { BeraMonitoring } from "../errors/BeraMonitoring";
|
package/src/data/contracts.ts
CHANGED
|
@@ -377,6 +377,10 @@ export const contracts = [
|
|
|
377
377
|
"80094": "0x2880aB155794e7179c9eE2e38200202908C17B43",
|
|
378
378
|
name: "external.pyth",
|
|
379
379
|
},
|
|
380
|
+
{
|
|
381
|
+
"80094": "0xB5f473c4b7F402d8f7bED42b6D516f5ff3306B01",
|
|
382
|
+
name: "bend.production.supportedVaults.1",
|
|
383
|
+
},
|
|
380
384
|
{
|
|
381
385
|
"80069": null,
|
|
382
386
|
"80094": "0xC5FabF3a7E98a2ed89f5d5057Ab010634Ca7e71f",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ErrorLike } from "@apollo/client";
|
|
2
2
|
|
|
3
3
|
import { getUriFromLink } from "@berachain/config";
|
|
4
|
+
import { bepolia } from "@berachain/config/bepolia";
|
|
4
5
|
import { mainnet } from "@berachain/config/mainnet";
|
|
5
6
|
|
|
6
7
|
import { BeraError, type IBeraErrorArgs } from "./BeraError";
|
|
@@ -33,12 +34,16 @@ export class RequestError extends BeraError {
|
|
|
33
34
|
*/
|
|
34
35
|
private queryRichfulDomains = [
|
|
35
36
|
mainnet.bex.aggregatorsProxyUrl,
|
|
36
|
-
|
|
37
|
+
// covers both /aggregator/quote and /aggregator/transaction?quoteId=...
|
|
38
|
+
// (the transaction endpoint's quoteId would otherwise fragment grouping)
|
|
39
|
+
"https://api.fly.trade/aggregator",
|
|
37
40
|
"https://open-api.openocean.finance/v4/bera/swap",
|
|
38
41
|
// vercel internal proxy
|
|
39
42
|
"/api/aggregators?aggregator",
|
|
40
43
|
// bonder endpoints have date-based query params (e.g. ?start=2025-11-28)
|
|
41
44
|
mainnet.backend,
|
|
45
|
+
// beep (next-gen backend) on testnet — same date-based bonder query params
|
|
46
|
+
bepolia.beep,
|
|
42
47
|
];
|
|
43
48
|
|
|
44
49
|
statusCode: number | undefined;
|
|
@@ -208,14 +213,18 @@ export class RequestError extends BeraError {
|
|
|
208
213
|
// - have a network error
|
|
209
214
|
// - have CORS errors
|
|
210
215
|
// the list is empirically collected from sentry error logs.
|
|
216
|
+
// The trailing "(host)" suffix is optional: some browsers/transports omit
|
|
217
|
+
// it (e.g. bare "Failed to fetch" / "Load failed"). We normalize all of
|
|
218
|
+
// them to a single NETWORK_ERROR reason so the variants group together,
|
|
219
|
+
// falling back to the endpoint url when no host is present in the message.
|
|
211
220
|
const failedToFetchMatch = message.match(
|
|
212
|
-
/^(?:NetworkError when attempting to fetch resource\.|Failed to fetch|Load failed) \(([a-z0-9-.]+)\)
|
|
221
|
+
/^(?:NetworkError when attempting to fetch resource\.|Failed to fetch|Load failed)(?: \(([a-z0-9-.]+)\))?$/,
|
|
213
222
|
);
|
|
214
223
|
if (failedToFetchMatch) {
|
|
215
224
|
return {
|
|
216
225
|
reason: RequestError.REASON_MAP.NETWORK_ERROR,
|
|
217
226
|
message: message,
|
|
218
|
-
url: failedToFetchMatch[1],
|
|
227
|
+
url: failedToFetchMatch[1] ?? this.endpoint.url,
|
|
219
228
|
};
|
|
220
229
|
}
|
|
221
230
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { RequestError } from "./RequestError.js";
|
|
4
|
+
|
|
5
|
+
describe("RequestError endpoint normalization (grouping)", () => {
|
|
6
|
+
it("strips query params from the fly.trade transaction endpoint so quoteId no longer fragments groups", () => {
|
|
7
|
+
const base = "https://api.fly.trade/aggregator/transaction";
|
|
8
|
+
const a = new RequestError({
|
|
9
|
+
response: undefined,
|
|
10
|
+
endpoint: {
|
|
11
|
+
url: `${base}?quoteId=aaaa-1111&estimateGas=false`,
|
|
12
|
+
type: "rest",
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
const b = new RequestError({
|
|
16
|
+
response: undefined,
|
|
17
|
+
endpoint: {
|
|
18
|
+
url: `${base}?quoteId=bbbb-2222&estimateGas=false`,
|
|
19
|
+
type: "rest",
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
expect(a.endpoint.url).toBe(base);
|
|
24
|
+
expect(b.endpoint.url).toBe(base);
|
|
25
|
+
expect(a.endpoint.url).toBe(b.endpoint.url);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("RequestError network-error normalization (grouping)", () => {
|
|
30
|
+
it.each([
|
|
31
|
+
"Failed to fetch",
|
|
32
|
+
"Load failed",
|
|
33
|
+
"NetworkError when attempting to fetch resource.",
|
|
34
|
+
"Failed to fetch (api.berachain.com)",
|
|
35
|
+
])("normalizes %s to NETWORK_ERROR reason", (message) => {
|
|
36
|
+
const err = new RequestError({
|
|
37
|
+
response: undefined,
|
|
38
|
+
cause: new TypeError(message),
|
|
39
|
+
endpoint: { url: "https://api.berachain.com", type: "graphql" },
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(err.reason).toBe(RequestError.REASON_MAP.NETWORK_ERROR);
|
|
43
|
+
expect(err.level).toBe("warning");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("does not treat unrelated messages as network errors", () => {
|
|
47
|
+
const err = new RequestError({
|
|
48
|
+
response: undefined,
|
|
49
|
+
cause: new TypeError("Cannot read properties of undefined"),
|
|
50
|
+
endpoint: { url: "https://api.berachain.com", type: "graphql" },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
expect(err.reason).not.toBe(RequestError.REASON_MAP.NETWORK_ERROR);
|
|
54
|
+
});
|
|
55
|
+
});
|
package/src/errors/errorMap.ts
CHANGED
|
@@ -89,6 +89,14 @@ export const errorMsgMap = {
|
|
|
89
89
|
errorMSG:
|
|
90
90
|
"Nonce is too low. Your wallet might have unconfirmed transactions.",
|
|
91
91
|
},
|
|
92
|
+
REPLACEMENT_UNDERPRICED: {
|
|
93
|
+
// Transient resubmission issue (gas bump too low). Not function-specific —
|
|
94
|
+
// normalizing the raw RPC string to this key collapses the per-function
|
|
95
|
+
// Sentry groups (functionName is still kept as a tag).
|
|
96
|
+
keywords: ["replacement transaction underpriced"],
|
|
97
|
+
errorMSG:
|
|
98
|
+
"Your wallet tried to replace a pending transaction without raising the gas enough. Please wait for the previous transaction to finish, or try again.",
|
|
99
|
+
},
|
|
92
100
|
GAS_PRICE: {
|
|
93
101
|
keywords: ["gasLimit"],
|
|
94
102
|
errorMSG:
|
|
@@ -78,8 +78,12 @@ const expectedRevertReasons = [
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
functionName: "revertNoReasonAssembly",
|
|
81
|
+
// A revert with no data has nothing to decode. The two replay paths surface it
|
|
82
|
+
// differently: simulateContract (getRevertReason) yields the RPC "execution
|
|
83
|
+
// reverted", while sendCalls/simulateCalls (eip5972) yields viem's zero-data
|
|
84
|
+
// error chain whose innermost name is "AbiDecodingZeroDataError".
|
|
81
85
|
onChainReason: "execution reverted",
|
|
82
|
-
revertReason: "
|
|
86
|
+
revertReason: "AbiDecodingZeroDataError",
|
|
83
87
|
txHashWith5972:
|
|
84
88
|
"0xcd9af1bca5ac53d1fd6bd33796a087f15895943047ae0d20f51331d78945a7a8",
|
|
85
89
|
},
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import useSWR from "swr";
|
|
2
|
-
|
|
3
1
|
import { usePublicClient } from "@berachain/wagmi/hooks";
|
|
2
|
+
import { useSWR } from "@berachain/utils/pkg/swr";
|
|
4
3
|
|
|
5
4
|
import type { GetConvertToAssetsProps } from "../../actions/bend/getConvertToAssets";
|
|
6
5
|
import { getConvertToAssets } from "../../actions/bend/getConvertToAssets";
|
|
@@ -30,7 +29,7 @@ export const useGetConvertToAssets = (
|
|
|
30
29
|
});
|
|
31
30
|
},
|
|
32
31
|
{
|
|
33
|
-
...options
|
|
32
|
+
...options,
|
|
34
33
|
},
|
|
35
34
|
);
|
|
36
35
|
|