Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
93 changes: 75 additions & 18 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 { useEffect, useRef, useState } 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 All @@ -85,6 +89,7 @@ export function ProviderInitializer() {
const tokenLogin = useSelector(tokenLoginSelector);
const userAccount = useSelector(accountSelector);
const nativeAuthConfig = tokenLogin?.nativeAuthConfig;
const [lastChainId, setLastChainId] = useState<string>(chainID);

const loginService = useLoginService(
nativeAuthConfig ? nativeAuthConfig : false
Expand Down Expand Up @@ -136,6 +141,51 @@ export function ProviderInitializer() {
}
}, [isLoggedIn, userAccount]);

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

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;

const hasDifferentChainId = lastChainId !== chainID;

if (hasDifferentChainId) {
setLastChainId(chainID);
}

if (hasDifferentGasStationMetadata || hasDifferentChainId) {
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 +196,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