Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neon: Solana transactions #2483

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
32 changes: 32 additions & 0 deletions configs/app/features/externalTxs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { Feature } from './types';

import { getEnvValue, parseEnvJson } from '../utils';

type TxExternalTransactionsConfig = {
chain_name: string;
chain_logo_url: string;
explorer_url_template: string;
};

const externalTransactionsConfig = parseEnvJson<TxExternalTransactionsConfig>(getEnvValue('NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG'));

const title = 'External transactions';

const config: Feature<{ chainName: string; chainLogoUrl: string; explorerUrlTemplate: string }> = (() => {
if (externalTransactionsConfig) {
return Object.freeze({
title,
isEnabled: true,
chainName: externalTransactionsConfig.chain_name,
chainLogoUrl: externalTransactionsConfig.chain_logo_url,
explorerUrlTemplate: externalTransactionsConfig.explorer_url_template,
});
}

return Object.freeze({
title,
isEnabled: false,
});
})();

export default config;
1 change: 1 addition & 0 deletions configs/app/features/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export { default as csvExport } from './csvExport';
export { default as dataAvailability } from './dataAvailability';
export { default as deFiDropdown } from './deFiDropdown';
export { default as easterEggBadge } from './easterEggBadge';
export { default as externalTxs } from './externalTxs';
export { default as faultProofSystem } from './faultProofSystem';
export { default as gasTracker } from './gasTracker';
export { default as getGasButton } from './getGasButton';
Expand Down
8 changes: 8 additions & 0 deletions docs/ENVS.md
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,14 @@ This feature is **enabled by default** with the `['metamask']` value. To switch

&nbsp;

### External transactions

| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
| --- | --- | --- | --- | --- | --- | --- |
| NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG | `{ chain_name: string; chain_logo_url: string; explorer_url_template: string; }` | Configuration of the external transactions links that should be added to the transaction details. | - | - | `{ chain_name: 'ethereum', chain_logo_url: 'https://example.com/logo.png', explorer_url_template: 'https://explorer.com/tx/{hash}' }` | v1.38.0+ |

&nbsp;

### Verified tokens info

| Variable | Type| Description | Compulsoriness | Default value | Example value | Version |
Expand Down
5 changes: 5 additions & 0 deletions lib/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ export const RESOURCES = {
path: '/api/v2/transactions/:hash/summary',
pathParams: [ 'hash' as const ],
},
tx_external_transactions: {
path: '/api/v2/transactions/:hash/external_transactions',
pathParams: [ 'hash' as const ],
},
withdrawals: {
path: '/api/v2/withdrawals',
filterFields: [],
Expand Down Expand Up @@ -1296,6 +1300,7 @@ Q extends 'tx_raw_trace' ? RawTracesResponse :
Q extends 'tx_state_changes' ? TxStateChanges :
Q extends 'tx_blobs' ? TxBlobs :
Q extends 'tx_interpretation' ? TxInterpretationResponse :
Q extends 'tx_external_transactions' ? Array<string> :
Q extends 'addresses' ? AddressesResponse :
Q extends 'addresses_metadata_search' ? AddressesMetadataSearchResult :
Q extends 'address' ? Address :
Expand Down
5 changes: 5 additions & 0 deletions types/client/externalTxsConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type TxExternalTxsConfig = {
chain_name: string;
chain_logo_url: string;
explorer_url_template: string;
};
24 changes: 24 additions & 0 deletions ui/tx/TxExternalTxs.pw.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

import { test, expect } from 'playwright/lib';

import TxExternalTxs from './TxExternalTxs';

const EXT_TX_HASH = '2uwpB95K9ae8yrpxxVXJ27ivvHXqrmy82jsamgNtdWJrYDGkCHsRwd2LKXubrQUzXMaojGxZmHZ85XVJN8EJ3LW8';
const CONFIG = {
chain_name: 'Solana',
chain_logo_url: 'http://example.url',
explorer_url_template: 'https://scan.io/tx/{hash}',
};

test('base view', async({ page, render, mockEnvs, mockAssetResponse }) => {
await mockEnvs([
[ 'NEXT_PUBLIC_TX_EXTERNAL_TRANSACTIONS_CONFIG', JSON.stringify(CONFIG) ],
]);
await mockAssetResponse(CONFIG.chain_logo_url, './playwright/mocks/image_s.jpg');
await render(<TxExternalTxs data={ Array(13).fill(EXT_TX_HASH) }/>);
await page.getByText('13 Solana txs').hover();
const popover = page.locator('.chakra-popover__content');
await expect(popover).toBeVisible();
await expect(popover).toHaveScreenshot();
});
61 changes: 61 additions & 0 deletions ui/tx/TxExternalTxs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
PopoverTrigger,
PopoverBody,
PopoverContent,
Flex,
Link,
Image,
} from '@chakra-ui/react';
import React from 'react';

import config from 'configs/app';
import Popover from 'ui/shared/chakra/Popover';
import TxEntity from 'ui/shared/entities/tx/TxEntity';

const externalTxFeature = config.features.externalTxs;

interface Props {
data: Array<string>;
}

const TxExternalTxs: React.FC<Props> = ({ data }) => {
if (!externalTxFeature.isEnabled) {
return null;
}

return (
<Popover placement="bottom-end" openDelay={ 300 } isLazy trigger="hover">
<PopoverTrigger>
<Link
_hover={{ textDecoration: 'none', color: 'link_hovered' }}
display="inline-flex"
alignItems="center"
gap={ 2 }
>
<Image src={ externalTxFeature.chainLogoUrl } alt={ externalTxFeature.chainName } width={ 5 } height={ 5 }/>
{ data.length } { externalTxFeature.chainName } tx{ data.length > 1 ? 's' : '' }
</Link>
</PopoverTrigger>
<PopoverContent border="1px solid" borderColor="divider" w="460px">
<PopoverBody fontWeight={ 400 } fontSize="sm">
<Flex alignItems="center" gap={ 2 } fontSize="md" mb={ 3 }>
<Image src={ externalTxFeature.chainLogoUrl } alt={ externalTxFeature.chainName } width={ 5 } height={ 5 }/>
{ externalTxFeature.chainName } transaction{ data.length > 1 ? 's' : '' }
</Flex>
<Flex flexDirection="column" gap={ 2 } w="100%" maxHeight="460px" overflowY="auto">
{ data.map((txHash) => (
<TxEntity
key={ txHash }
hash={ txHash }
href={ externalTxFeature.explorerUrlTemplate.replace('{hash}', txHash) }
isExternal
/>
)) }
</Flex>
</PopoverBody>
</PopoverContent>
</Popover>
);
};

export default TxExternalTxs;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions ui/tx/details/TxInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ZKSYNC_L2_TX_BATCH_STATUSES } from 'types/api/zkSyncL2';
import { route } from 'nextjs-routes';

import config from 'configs/app';
import useApiQuery from 'lib/api/useApiQuery';
import { WEI, WEI_IN_GWEI } from 'lib/consts';
import getNetworkValidatorTitle from 'lib/networks/getNetworkValidatorTitle';
import * as arbitrum from 'lib/rollups/arbitrum';
Expand Down Expand Up @@ -61,6 +62,7 @@ import TxDetailsTokenTransfers from 'ui/tx/details/TxDetailsTokenTransfers';
import TxDetailsWithdrawalStatus from 'ui/tx/details/TxDetailsWithdrawalStatus';
import TxRevertReason from 'ui/tx/details/TxRevertReason';
import TxAllowedPeekers from 'ui/tx/TxAllowedPeekers';
import TxExternalTxs from 'ui/tx/TxExternalTxs';
import TxSocketAlert from 'ui/tx/TxSocketAlert';
import ZkSyncL2TxnBatchHashesInfo from 'ui/txnBatches/zkSyncL2/ZkSyncL2TxnBatchHashesInfo';

Expand All @@ -74,9 +76,21 @@ interface Props {
socketStatus?: 'close' | 'error';
}

const externalTxFeature = config.features.externalTxs;

const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
const [ isExpanded, setIsExpanded ] = React.useState(false);

const externalTxsQuery = useApiQuery('tx_external_transactions', {
pathParams: {
hash: data?.hash,
},
queryOptions: {
enabled: externalTxFeature.isEnabled,
placeholderData: [ '1', '2', '3' ],
},
});

const handleCutClick = React.useCallback(() => {
setIsExpanded((flag) => !flag);
scroller.scrollTo('TxInfo__cutLink', {
Expand Down Expand Up @@ -162,6 +176,13 @@ const TxInfo = ({ data, isLoading, socketStatus }: Props) => {
<Box display="none" flexShrink={ 0 } id="meta-suites__tx-explorer-link"/>
</>
) }

{ config.features.externalTxs.isEnabled && externalTxsQuery.data && externalTxsQuery.data.length > 0 && (
<Skeleton isLoaded={ !isLoading && !externalTxsQuery.isPlaceholderData } display="inline-flex" alignItems="center">
<TextSeparator color="gray.500" flexShrink={ 0 }/>
<TxExternalTxs data={ externalTxsQuery.data }/>
</Skeleton>
) }
</DetailsInfoItem.Value>

<DetailsInfoItem.Label
Expand Down
Loading