Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [Added dynamic gas station metadata fetch](https://github.com/multiversx/mx-sdk-dapp/pull/1404)

## [[v4.1.2](https://github.com/multiversx/mx-sdk-dapp/pull/1403)] - 2025-04-15

- [Upgrade passkey provider](https://github.com/multiversx/mx-sdk-dapp/pull/1403)

## [[v4.1.1](https://github.com/multiversx/mx-sdk-dapp/pull/1402)] - 2025-04-14

- [Upgrade passkey provider](https://github.com/multiversx/mx-sdk-dapp/pull/1400)

## [[v4.1.0](https://github.com/multiversx/mx-sdk-dapp/pull/1399)] - 2025-04-09

- [Upgrade passkey provider and add `extrasApiAddress` to network](https://github.com/multiversx/mx-sdk-dapp/pull/1399)
- [Added shard data to the Ledger address table](https://github.com/multiversx/mx-sdk-dapp/pull/1396)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ describe('getGasPriceDetails', () => {
it('should return the correct gas price details', () => {
const gasPriceDetails = getGasPriceDetails({
shard: 1,
gasStationMetadata: [
{
gasStationMetadata: {
0: {
fast: 11_760_000,
faster: 19_287_760
},
{
1: {
fast: 11_760_000,
faster: 19_287_760
},
{
2: {
fast: 11_760_000,
faster: 19_287_760
}
],
},
transaction: secondTx,
initialGasPrice: 1_000_000_000
});
Expand Down
40 changes: 40 additions & 0 deletions src/apiCalls/configuration/getGasStationMetadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import axios from 'axios';
import { getCleanApiAddress } from 'apiCalls/utils';
import { BaseNetworkType } from 'types/network.types';

interface IGasStationApiResponse {
lastBlock: number;
fast: number;
faster: number;
}

const GAS_STATION_ENDPOINT = 'transactions/ppu';

export async function getGasStationMetadataFromApi(
shard: number
): Promise<BaseNetworkType['gasStationMetadata'] | null> {
const apiAddress = getCleanApiAddress();
const gasStationUrl = `${apiAddress}/${GAS_STATION_ENDPOINT}/${shard}`;

try {
const { data } = await axios.get<IGasStationApiResponse>(gasStationUrl);

if (data) {
return {
[shard]: {
lastBlock: data.lastBlock,
fast: data.fast,
faster: data.faster
}
};
}
return null;
} catch (err) {
console.error(
'Error fetching gas station metadata from:',
gasStationUrl,
err
);
return null;
}
}
1 change: 1 addition & 0 deletions src/apiCalls/configuration/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './getServerConfigurationForEnvironment';
export * from './getServerConfiguration';
export * from './getNetworkConfig';
export * from './getGasStationMetadata';
export * from './getApiAddressForChainId';
export * from './getEnvironmentForChainId';
export * from './useGetNetworkConfigFromApi';
Expand Down
78 changes: 61 additions & 17 deletions src/components/ProviderInitializer/ProviderInitializer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useEffect, useRef } from 'react';

import { getNetworkConfigFromApi, useGetAccountFromApi } from 'apiCalls';
import {
getNetworkConfigFromApi,
getGasStationMetadataFromApi,
useGetAccountFromApi
} from 'apiCalls';
import {
DEVNET_CHAIN_ID,
MAINNET_CHAIN_ID,
Expand Down Expand Up @@ -46,6 +50,7 @@ import {
} from 'reduxStore/slices';
import { decodeNativeAuthToken } from 'services/nativeAuth/helpers';
import { LoginMethodsEnum } from 'types/enums.types';
import { NetworkType, ApiNetworkConfigType } from 'types/network.types';
import {
getAddress,
getLatestNonce,
Expand All @@ -67,7 +72,6 @@ import {
handleGuardianWarning
} from './helpers';
import { useSetLedgerProvider } from './hooks';

let initalizingLedger = false;

export function ProviderInitializer() {
Expand Down Expand Up @@ -136,6 +140,39 @@ export function ProviderInitializer() {
}
}, [isLoggedIn, userAccount]);

useEffect(() => {
checkGasMetadata();
}, [userAccount.shard, userAccount.address, isLoggedIn]);

async function checkGasMetadata() {
const hasAccountShard = isLoggedIn && userAccount.shard != null;

if (!hasAccountShard) {
return;
}

const shard = Number(userAccount.shard);
const fetchedGasMetadata = await getGasStationMetadataFromApi(shard);

if (!fetchedGasMetadata?.[shard]?.lastBlock) {
return;
}

const hasDifferentGasStationMetadata =
!network.gasStationMetadata ||
!network.gasStationMetadata[shard] ||
network.gasStationMetadata[shard].lastBlock !==
fetchedGasMetadata[shard].lastBlock;

if (hasDifferentGasStationMetadata) {
dispatch(
updateNetworkConfig({
gasStationMetadata: fetchedGasMetadata
})
);
}
}

// We need to get the roundDuration for networks that do not support websocket (e.g. sovereign)
// The round duration is used for polling interval
async function refreshNetworkConfig() {
Expand All @@ -146,30 +183,37 @@ export function ProviderInitializer() {
) &&
!network.roundDuration;

const shouldGetConfig =
const shouldGetBaseConfig =
!network.chainId || needsRoundDurationForPollingInterval;

if (!shouldGetConfig) {
if (!shouldGetBaseConfig) {
return;
}

try {
const networkConfig = await getNetworkConfigFromApi();
const hasDifferentNetworkConfig =
networkConfig &&
(network.chainId !== networkConfig.erd_chain_id ||
network.roundDuration !== networkConfig.erd_round_duration);
const fetchedNetworkConfig = await getNetworkConfigFromApi();

if (hasDifferentNetworkConfig) {
dispatch(
updateNetworkConfig({
chainId: networkConfig.erd_chain_id,
roundDuration: networkConfig.erd_round_duration
})
);
const updates: Partial<NetworkType> = {};

if (fetchedNetworkConfig) {
const networkConfigResult =
fetchedNetworkConfig as ApiNetworkConfigType;

const hasDifferentNetworkConfig =
network.chainId !== networkConfigResult.erd_chain_id ||
network.roundDuration !== networkConfigResult.erd_round_duration;

if (hasDifferentNetworkConfig) {
updates.chainId = networkConfigResult.erd_chain_id;
updates.roundDuration = networkConfigResult.erd_round_duration;
}
}

if (Object.keys(updates).length > 0) {
dispatch(updateNetworkConfig(updates));
}
} catch (err) {
console.error('failed refreshing chainId ', err);
console.error('Failed refreshing network config:', err);
}
}

Expand Down
9 changes: 4 additions & 5 deletions src/types/network.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ export interface BaseNetworkType {
roundDuration: number;
metamaskSnapWalletAddress?: string;
websocketUrl?: string;
gasStationMetadata?: {
fast: number;
faster: number;
excellentJustLikeMoveBalance?: number;
}[];
gasStationMetadata?: Record<
number,
{ lastBlock?: number; fast: number; faster: number }
>;
}

export interface AccountInfoSliceNetworkType extends BaseNetworkType {
Expand Down