Skip to content
Open
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
12 changes: 8 additions & 4 deletions configs/essential-dapps-chains/config.edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ async function fetchConfig() {

const url = baseUrl + '/assets/essential-dapps/chains.json';
const response = await fetch(url);
const json = await response.json();

value = json as MultichainConfig;
return value;
if (response.ok) {
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
const json = await response.json();
value = json as MultichainConfig;
return value;
}
}
}

export async function load() {
Expand Down
12 changes: 8 additions & 4 deletions configs/multichain/config.edge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ async function fetchConfig() {

const url = baseUrl + '/assets/multichain/config.json';
const response = await fetch(url);
const json = await response.json();

value = json as MultichainConfig;
return value;
if (response.ok) {
const contentType = response.headers.get('content-type');
if (contentType?.includes('application/json')) {
const json = await response.json();
value = json as MultichainConfig;
return value;
}
}
}

export async function load() {
Expand Down
38 changes: 26 additions & 12 deletions deploy/tools/essential-dapps-chains-config-generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { dirname, resolve as resolvePath } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Worker } from 'node:worker_threads';
import * as viemChains from 'viem/chains';
import { pick } from 'es-toolkit';
import { pick, uniq, delay } from 'es-toolkit';

import { EssentialDappsConfig } from 'types/client/marketplace';
import { getEnvValue, parseEnvJson } from 'configs/app/utils';
import { uniq } from 'es-toolkit';
import currentChainConfig from 'configs/app';
import appConfig from 'configs/app';
import { EssentialDappsChainConfig } from 'types/client/marketplace';
Expand Down Expand Up @@ -61,14 +60,15 @@ function trimChainConfig(config: typeof appConfig, logoUrl: string | undefined)
}

async function computeChainConfig(url: string): Promise<unknown> {
return new Promise((resolve, reject) => {
const workerPath = resolvePath(currentDir, 'worker.js');
const workerPath = resolvePath(currentDir, 'worker.js');

const worker = new Worker(workerPath, {
workerData: { url },
env: {} // Start with empty environment
});
const worker = new Worker(workerPath, {
workerData: { url },
env: {} // Start with empty environment
});
const controller = new AbortController();

const configPromise = new Promise((resolve, reject) => {
worker.on('message', (config) => {
resolve(config);
});
Expand All @@ -79,11 +79,17 @@ async function computeChainConfig(url: string): Promise<unknown> {
});

worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${ code }`));
}
controller.abort();
reject(new Error(`Worker stopped with exit code ${ code }`));
});
});

return Promise.race([
configPromise,
delay(30_000, { signal: controller.signal })
]).finally(() => {
worker.terminate();
})
}

async function run() {
Expand Down Expand Up @@ -116,7 +122,15 @@ async function run() {
const explorerUrls = chainscoutInfo.externals.map(({ explorerUrl }) => explorerUrl).filter(Boolean);
console.log(`ℹ️ For ${ explorerUrls.length } chains explorer url was found in static config. Fetching parameters for each chain...`);

const chainConfigs = await Promise.all(explorerUrls.map(computeChainConfig)) as Array<typeof appConfig>;
const chainConfigs: Array<typeof appConfig> = [];

for (const explorerUrl of explorerUrls) {
const chainConfig = (await computeChainConfig(explorerUrl)) as typeof appConfig | undefined;
if (!chainConfig) {
throw new Error(`Failed to fetch chain config for ${ explorerUrl }`);
}
chainConfigs.push(chainConfig);
}

const result = {
chains: [ currentChainConfig, ...chainConfigs ].map((config) => {
Expand Down
46 changes: 34 additions & 12 deletions deploy/tools/multichain-config-generator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { mkdirSync, writeFileSync } from 'node:fs';
import { dirname, resolve as resolvePath } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Worker } from 'node:worker_threads';
import { delay } from 'es-toolkit';

import { ClusterChainConfig } from 'types/multichain';
import appConfig from 'configs/app';

const currentFilePath = fileURLToPath(import.meta.url);
const currentDir = dirname(currentFilePath);
Expand Down Expand Up @@ -48,14 +50,15 @@ async function getChainscoutInfo(chainIds: Array<string>) {
}

async function computeChainConfig(url: string): Promise<unknown> {
return new Promise((resolve, reject) => {
const workerPath = resolvePath(currentDir, 'worker.js');
const workerPath = resolvePath(currentDir, 'worker.js');

const worker = new Worker(workerPath, {
workerData: { url },
env: {} // Start with empty environment
});
const worker = new Worker(workerPath, {
workerData: { url },
env: {} // Start with empty environment
});
const controller = new AbortController();

const configPromise = new Promise((resolve, reject) => {
worker.on('message', (config) => {
resolve(config);
});
Expand All @@ -66,11 +69,18 @@ async function computeChainConfig(url: string): Promise<unknown> {
});

worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${ code }`));
}
controller.abort();
reject(new Error(`Worker stopped with exit code ${ code }`));
});

});

return Promise.race([
configPromise,
delay(30_000, { signal: controller.signal })
]).finally(() => {
worker.terminate();
})
}

async function getExplorerUrls() {
Expand Down Expand Up @@ -105,15 +115,27 @@ async function run() {
throw new Error('No chains found in the cluster.');
}

const configs = await Promise.all(explorerUrls.map(computeChainConfig));
const chainscoutInfo = await getChainscoutInfo(configs.map((config) => config.chain.id));
const configs: Array<typeof appConfig> = [];
for (const url of explorerUrls) {
const chainConfig = (await computeChainConfig(url)) as typeof appConfig | undefined;
if (!chainConfig) {
throw new Error(`Failed to fetch chain config for ${ url }`);
}
configs.push(chainConfig);
}

const chainscoutInfo = await getChainscoutInfo(
configs
.map((config) => config.chain.id)
.filter((chainId) => chainId !== undefined)
);

const config = {
chains: configs.map((config, index) => {
const chainId = config.chain.id;
const chainName = (config as { chain: { name: string } })?.chain?.name ?? `Chain ${ chainId }`;
return {
id: chainId,
id: chainId || '',
name: chainName,
logo: chainscoutInfo.find((chain) => chain.id === chainId)?.logoUrl,
explorer_url: explorerUrls[index],
Expand Down
4 changes: 2 additions & 2 deletions global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ declare global {
};
abkw: string;
__envs: Record<string, string>;
__multichainConfig: MultichainConfig;
__essentialDappsChains: { chains: Array<EssentialDappsChainConfig> };
__multichainConfig?: MultichainConfig;
__essentialDappsChains?: { chains: Array<EssentialDappsChainConfig> };
}

namespace NodeJS {
Expand Down
8 changes: 8 additions & 0 deletions lib/errors/getErrorStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function getErrorStack(error: Error | undefined): string | undefined {
return (
error && 'stack' in error &&
typeof error.stack === 'string' && error.stack !== null &&
error.stack
) ||
undefined;
}
2 changes: 2 additions & 0 deletions lib/rollbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,6 @@ export const clientConfig: Configuration | undefined = feature.isEnabled ? {
'cancelled navigation',
],
maxItems: 10, // Max items per page load
captureUncaught: true,
captureUnhandledRejections: true,
} : undefined;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
"magic-bytes.js": "1.8.0",
"mixpanel-browser": "2.67.0",
"monaco-editor": "^0.34.1",
"next": "15.5.9",
"next": "15.5.10",
"next-themes": "0.4.4",
"nextjs-routes": "^1.0.8",
"node-fetch": "^3.2.9",
Expand Down
37 changes: 37 additions & 0 deletions pages/_error.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import type { NextPageContext } from 'next';
import NextErrorComponent from 'next/error';
import React from 'react';
import Rollbar from 'rollbar';

import type { Props as ServerSidePropsCommon } from 'nextjs/getServerSideProps/handlers';

import config from 'configs/app';
import * as cookies from 'lib/cookies';

const rollbarFeature = config.features.rollbar;
const rollbar = rollbarFeature.isEnabled ? new Rollbar({
accessToken: rollbarFeature.clientToken,
environment: rollbarFeature.environment,
payload: {
code_version: rollbarFeature.codeVersion,
app_instance: rollbarFeature.instance,
},
maxItems: 10,
captureUncaught: true,
captureUnhandledRejections: true,
}) : undefined;

type Props = ServerSidePropsCommon & {
statusCode: number;
};
Expand All @@ -14,4 +30,25 @@ const CustomErrorComponent = (props: Props) => {
return <NextErrorComponent statusCode={ props.statusCode } withDarkMode={ colorModeCookie === 'dark' }/>;
};

CustomErrorComponent.getInitialProps = async(context: NextPageContext) => {
const { res, err, req } = context;

const baseProps = await NextErrorComponent.getInitialProps(context); // Extract cookies from the request headers
const statusCode = res?.statusCode ?? err?.statusCode;
const cookies = req?.headers?.cookie || '';

if (rollbar) {
rollbar.error(err?.message ?? 'Unknown error', {
cause: err?.cause,
stack: err?.stack,
});
}

return {
...baseProps,
statusCode,
cookies,
};
};

export default CustomErrorComponent;
1 change: 1 addition & 0 deletions toolkit/theme/recipes/tag.recipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const recipe = defineSlotRecipe({
px: '6px',
py: '6px',
minH: '8',
minW: '8',
gap: '1',
'--tag-avatar-size': 'spacing.4',
'--tag-element-size': 'spacing.3',
Expand Down
10 changes: 8 additions & 2 deletions ui/address/AddressTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,19 @@ const AddressTokens = ({ shouldRender = true, isQueryEnabled = true }: Props) =>
const tab = getQueryParamString(router.query.tab);
const hash = getQueryParamString(router.query.hash);

// on address details we have tokens requests for all token types, separately
// react query can behave unexpectedly, when it already has data for ERC-20 (string type)
// and we fetch it again with the array type
// so if it's just one token type, we heed to keep it a string for queries compatibility
const tokenTypesFilter = config.chain.additionalTokenTypes.length > 0 ? [ 'ERC-20', ...config.chain.additionalTokenTypes.map(item => item.id) ] : 'ERC-20';

const erc20Query = useQueryWithPages({
resourceName: 'general:address_tokens',
pathParams: { hash },
filters: { type: [ 'ERC-20', ...config.chain.additionalTokenTypes.map(item => item.id) ] },
filters: { type: tokenTypesFilter },
scrollRef,
options: {
enabled: isQueryEnabled && (!tab || tab === 'tokens' || tab === 'tokens_erc20'),
enabled: isQueryEnabled && (tab === 'tokens' || tab === 'tokens_erc20'),
refetchOnMount: false,
placeholderData: generateListStub<'general:address_tokens'>(ADDRESS_TOKEN_BALANCE_ERC_20, 10, { next_page_params: null }),
},
Expand Down
6 changes: 2 additions & 4 deletions ui/hotContracts/HotContractsIntervalSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type { HotContractsInterval } from 'types/api/contracts';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import type { SelectOption } from 'toolkit/chakra/select';
import { Select } from 'toolkit/chakra/select';
import type { TagProps } from 'toolkit/chakra/tag';
import TagGroupSelect from 'ui/shared/tagGroupSelect/TagGroupSelect';

import { INTERVAL_ITEMS } from './utils';
Expand All @@ -27,10 +26,9 @@ interface Props {
interval: HotContractsInterval;
onIntervalChange: (newInterval: HotContractsInterval) => void;
isLoading?: boolean;
selectTagSize?: TagProps['size'];
};

const HotContractsIntervalSelect = ({ interval, onIntervalChange, isLoading, selectTagSize }: Props) => {
const HotContractsIntervalSelect = ({ interval, onIntervalChange, isLoading }: Props) => {

const isInitialLoading = useIsInitialLoading(isLoading);

Expand All @@ -44,7 +42,7 @@ const HotContractsIntervalSelect = ({ interval, onIntervalChange, isLoading, sel
items={ intervalItems }
onChange={ onIntervalChange }
value={ interval }
tagSize={ selectTagSize }
tagSize="lg"
loading={ isInitialLoading }
disabled={ isLoading }
hideBelow="lg"
Expand Down
8 changes: 4 additions & 4 deletions ui/hotContracts/HotContractsTableItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const HotContractsTableItem = ({

return (
<TableRow>
<TableCell verticalAlign="middle">
<TableCell>
<HStack>
<AddressEntity
address={ data.contract_address }
Expand All @@ -42,13 +42,13 @@ const HotContractsTableItem = ({
/>
) }
</TableCell>
<TableCell isNumeric verticalAlign="middle">
<TableCell isNumeric>
<TruncatedText text={ Number(data.transactions_count).toLocaleString() } loading={ isLoading } maxW="100%"/>
</TableCell>
<TableCell isNumeric verticalAlign="middle">
<TableCell isNumeric>
<TruncatedText text={ BigNumber(data.total_gas_used || 0).toFormat() } loading={ isLoading } maxW="100%"/>
</TableCell>
<TableCell isNumeric verticalAlign="middle">
<TableCell isNumeric>
<NativeCoinValue
amount={ data.balance }
loading={ isLoading }
Expand Down
11 changes: 9 additions & 2 deletions ui/marketplace/useMarketplaceWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,21 @@ export default function useMarketplaceWallet(appId: string, isEssentialDapp = fa

const sendTransaction = useCallback(async(transaction: SendTransactionArgs) => {
await checkAndSwitchChain();
const activityResponse = await trackTransaction(address ?? '', transaction.to ?? '');
const activityResponse = await trackTransaction(
address ?? '',
transaction.to ?? '',
isEssentialDapp ? String(chainId) : undefined,
);
const tx = await sendTransactionAsync(transaction);
if (activityResponse?.token) {
await trackTransactionConfirm(tx, activityResponse.token);
}
logEvent('Send Transaction');
return tx;
}, [ sendTransactionAsync, logEvent, trackTransaction, trackTransactionConfirm, address, checkAndSwitchChain ]);
}, [
sendTransactionAsync, logEvent, trackTransaction, trackTransactionConfirm,
address, checkAndSwitchChain, chainId, isEssentialDapp,
]);

const signMessage = useCallback(async(message: string) => {
await checkAndSwitchChain();
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading