Skip to content

Commit 4dd3804

Browse files
committed
start UI work
1 parent 6b0840a commit 4dd3804

File tree

5 files changed

+119
-84
lines changed

5 files changed

+119
-84
lines changed

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.next
22
pnpm-lock.yaml
33
public/prism-light.css
4-
public/prism-dark.css
4+
public/prism-dark.css
5+
script/

script/checks/evm-stack-addresses.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { type Address, type Hex, type PublicClient, keccak256 } from 'viem';
22

3-
type EVMStack = 'OP' | 'Orbit';
3+
export type EVMStack = 'OP' | 'Orbit';
44

55
type Predeploy = {
66
name: string;
77
address: Address;
88
kind: 'Predeploy' | 'Precompile';
99
};
1010

11-
type Result = Predeploy & {
11+
export type EVMStackResult = Predeploy & {
1212
codeHash: Hex;
1313
exists: boolean;
1414
};
@@ -18,8 +18,8 @@ const INVALID_CODE_HASH = keccak256('0xfe');
1818

1919
export async function checkEvmStackAddresses(
2020
client: PublicClient,
21-
): Promise<Record<EVMStack, Result[]>> {
22-
const result: Record<EVMStack, Result[]> = {
21+
): Promise<Record<EVMStack, EVMStackResult[]>> {
22+
const result: Record<EVMStack, EVMStackResult[]> = {
2323
OP: [],
2424
Orbit: [],
2525
};

script/index.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
import { http, fallback, createPublicClient } from 'viem';
22
import { checkDeployedContracts } from './checks/deployed-contracts';
3-
import { checkEvmStackAddresses } from './checks/evm-stack-addresses';
3+
import { checkEvmStackAddresses, EVMStack, EVMStackResult } from './checks/evm-stack-addresses';
44
import { checkOpcodes } from './checks/opcodes';
55
import { checkPrecompiles } from './checks/precompiles';
66
import type { Metadata } from './types';
77

8+
export type Chain = {
9+
metadata: Metadata;
10+
opcodes: {
11+
number: `0x${string}`;
12+
name: string;
13+
supported: string | boolean;
14+
}[];
15+
deployedContracts: {
16+
name: string;
17+
address: `0x${string}`;
18+
codeHash: `0x${string}`;
19+
hasCode: boolean;
20+
}[];
21+
precompiles: {
22+
name: string;
23+
address: `0x${string}`;
24+
implemented: boolean;
25+
}[];
26+
evmStackAddresses: Record<EVMStack, EVMStackResult[]>;
27+
}
28+
829
async function main() {
930
// Initialize chain data.
1031
const { chainId } = init();
@@ -21,7 +42,7 @@ async function main() {
2142
]);
2243

2344
// Format and save the output.
24-
const chain = {
45+
const chain: Chain = {
2546
metadata: sortObjectKeys(metadata, [
2647
'name',
2748
'shortName',

src/components/diff/DiffMetadata.tsx

Lines changed: 47 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { Chain as Metadata } from '@wagmi/chains';
21
import { getAddress } from 'viem';
2+
import { Chain } from '@/../script/index';
33
import { RenderDiff } from '@/components/diff/utils/RenderDiff';
44
import { Copyable } from '@/components/ui/Copyable';
55
import { classNames, toUppercase } from '@/lib/utils';
66
import { ExternalLink } from '../layout/ExternalLink';
77

8+
type Metadata = Chain['metadata'];
89
type MetadataKey = keyof Metadata;
910

1011
type Props = {
@@ -14,79 +15,74 @@ type Props = {
1415
};
1516

1617
const hiddenField = (field: MetadataKey) => {
17-
return ['network'].includes(field);
18+
return ['networkId', 'ens', 'features', 'icon', 'slip44', 'faucets', 'chain', 'parent'].includes(
19+
field
20+
);
1821
};
1922

2023
const formatFieldDisplayName = (field: MetadataKey) => {
21-
if (field === 'id') return 'Chain ID';
24+
if (field === 'chainId') return 'Chain ID';
25+
if (field === 'explorers') return 'Block Explorers';
2226
if (field === 'name') return 'Name';
23-
if (field === 'rpcUrls') return 'RPC URLs';
24-
if (field === 'blockExplorers') return 'Block Explorers';
2527
if (field === 'nativeCurrency') return 'Native Currency';
26-
if (field === 'contracts') return 'Multicall3';
28+
if (field === 'rpc') return 'RPC URLs';
29+
if (field === 'shortName') return 'Short Name';
30+
if (field === 'infoURL') return 'Info URL';
2731
return field;
2832
};
2933

30-
const formatRpcUrls = (data: Metadata['rpcUrls']) => {
31-
const renderRpcUrls = (rpcUrls: Metadata['rpcUrls']['default']) => (
32-
<ul>
33-
{rpcUrls.http.map((url) => (
34-
<li className='text-secondary text-sm' key={url}>
35-
<Copyable content={url} />
36-
</li>
37-
))}
38-
{rpcUrls.webSocket &&
39-
rpcUrls.webSocket.map((url) => (
40-
<li className='text-secondary text-sm' key={url}>
41-
<Copyable content={url} />
42-
</li>
43-
))}
44-
</ul>
45-
);
34+
const formatShortName = (shortName: string) => {
35+
return <Copyable content={shortName.replace(/^['"]|['"]$/g, '')} />; // Remove leading and trailing quotes.
36+
};
4637

38+
const formatInfoURL = (infoURL: string) => {
39+
return <ExternalLink href={infoURL} text={infoURL} />;
40+
};
41+
42+
const formatNativeCurrency = (nativeCurrency: Metadata['nativeCurrency']) => {
4743
return (
48-
<div>
49-
{Object.entries(data).map(([key, rpcUrls], index) => (
50-
<div key={key}>
51-
<h3 className={classNames('font-bold', index > 0 && 'mt-6')}>{toUppercase(key)}</h3>
52-
{renderRpcUrls(rpcUrls)}
53-
</div>
54-
))}
55-
</div>
44+
<>
45+
<div>
46+
{nativeCurrency.name} ({nativeCurrency.symbol})
47+
</div>
48+
<div className='text-secondary text-sm'>{nativeCurrency.decimals} decimals</div>
49+
</>
5650
);
5751
};
5852

59-
const formatBlockExplorerUrls = (data: Metadata['blockExplorers']) => {
53+
const formatRpcUrls = (rpcUrls: Metadata['rpc']) => {
54+
return rpcUrls.map((url) => (
55+
<Copyable className='text-secondary text-sm' key={url} content={url} />
56+
));
57+
};
58+
59+
const formatBlockExplorerUrls = (data: Metadata['explorers']) => {
6060
if (!data) return null;
6161
return (
6262
<div>
63-
{Object.entries(data).map(([key, blockExplorer], index) => (
63+
{Object.entries(data).map(([key, blockExplorer]) => (
6464
<div key={key}>
65-
<h3 className={classNames('font-bold', index > 0 && 'mt-6')}>{toUppercase(key)}</h3>
66-
<p className='text-secondary text-sm'>
67-
<ExternalLink href={blockExplorer.url} text={blockExplorer.url} />
68-
</p>
65+
<Copyable
66+
className='text-secondary text-sm'
67+
content={<ExternalLink href={blockExplorer.url} text={blockExplorer.url} />}
68+
textToCopy={blockExplorer.url}
69+
/>
6970
</div>
7071
))}
7172
</div>
7273
);
7374
};
7475

7576
const formatFieldInfo = (field: MetadataKey, contents: Metadata[MetadataKey]) => {
76-
if (field === 'id') return <Copyable content={contents?.toString() || ''} />;
77+
if (field === 'chainId') return <Copyable content={contents?.toString() || ''} />;
7778
if (field === 'name') return <Copyable content={contents?.toString() || ''} />;
78-
if (field === 'rpcUrls') return formatRpcUrls(contents as Metadata['rpcUrls']);
79-
if (field === 'blockExplorers') {
80-
return formatBlockExplorerUrls(contents as Metadata['blockExplorers']);
81-
}
82-
if (field === 'nativeCurrency') {
83-
const c = contents as Metadata['nativeCurrency'];
84-
return `${c.name} (${c.symbol})`;
85-
}
86-
if (field === 'contracts') {
87-
const c = contents as Metadata['contracts'];
88-
const multicall3 = c?.multicall3?.address;
89-
return multicall3 ? getAddress(multicall3) : 'None';
79+
if (field === 'rpc') return formatRpcUrls(contents as Metadata['rpc']);
80+
if (field === 'shortName') return formatShortName(contents as Metadata['shortName']);
81+
if (field === 'infoURL') return formatInfoURL(contents as Metadata['infoURL']);
82+
if (field === 'nativeCurrency')
83+
return formatNativeCurrency(contents as Metadata['nativeCurrency']);
84+
if (field === 'explorers') {
85+
return formatBlockExplorerUrls(contents as Metadata['explorers']);
9086
}
9187
return JSON.stringify(contents);
9288
};
@@ -97,21 +93,14 @@ export const DiffMetadata = ({ base, target, onlyShowDiff }: Props) => {
9793
<>
9894
{fields.map((field) => {
9995
if (hiddenField(field)) return null;
100-
101-
// When comparing the contracts field, we only consider the Multicall3 field. The other
102-
// fields are around ENS which is just mainnet (and a testnet).
103-
const isEqual =
104-
field === 'contracts'
105-
? JSON.stringify(base[field]?.multicall3?.address) ===
106-
JSON.stringify(target[field]?.multicall3?.address)
107-
: JSON.stringify(base[field]) === JSON.stringify(target[field]);
96+
const isEqual = JSON.stringify(base[field]) === JSON.stringify(target[field]);
10897
const showField = !isEqual || !onlyShowDiff;
10998

11099
return (
111100
showField && (
112101
<div
113102
key={field}
114-
className='grid grid-cols-12 items-center border-b border-zinc-500/10 py-6 dark:border-zinc-500/20'
103+
className='grid grid-cols-12 border-b border-zinc-500/10 py-6 dark:border-zinc-500/20'
115104
>
116105
<div className='col-span-2'>{formatFieldDisplayName(field)}</div>
117106
<div className='col-span-5 pr-4'>{formatFieldInfo(field, base[field])}</div>

src/pages/diff.tsx

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { useState } from 'react';
1+
import { useEffect, useState } from 'react';
22
import { useRouter } from 'next/router';
33
import { LinkIcon } from '@heroicons/react/20/solid';
4+
import { Chain } from '@/../script/index';
45
import { getChainById } from '@/chains';
56
import { ChainDiffSelector } from '@/components/ChainDiffSelector';
67
import { DiffAccountTypes } from '@/components/diff/DiffAccountTypes';
@@ -16,7 +17,6 @@ import { DiffSignatureTypes } from '@/components/diff/DiffSignatureTypes';
1617
import { Copyable } from '@/components/ui/Copyable';
1718
import { Toggle } from '@/components/ui/Toggle';
1819
import { classNames } from '@/lib/utils';
19-
import { Chain } from '@/types';
2020

2121
interface Props<T> {
2222
base: T;
@@ -28,30 +28,56 @@ interface Section {
2828
title: string;
2929
// eslint-disable-next-line @typescript-eslint/no-explicit-any
3030
component: React.ComponentType<Props<any>>;
31-
hide?: boolean;
3231
}
3332

3433
const SECTION_MAP: Record<string, Section> = {
3534
metadata: { title: 'Metadata', component: DiffMetadata },
36-
precompiles: { title: 'Precompiles', component: DiffPrecompiles },
37-
predeploys: { title: 'Predeploys', component: DiffPredeploys },
38-
signatureTypes: { title: 'Transaction and Signature Types', component: DiffSignatureTypes },
39-
accountTypes: { title: 'Account Types', component: DiffAccountTypes },
40-
opcodes: { title: 'Opcodes', component: DiffOpcodes },
41-
mempools: { title: 'Mempools', component: DiffMempools },
42-
deployedContracts: { title: 'Deployed Contracts', component: DiffDeployedContracts },
43-
eips: { title: 'Execution EIPs', component: DiffEIPs },
44-
executionNodes: { title: 'Execution Nodes', component: DiffNodes },
45-
consensusNodes: { title: 'Consensus Nodes', component: DiffNodes, hide: true }, // Hidden to scope UI to execution data
35+
// precompiles: { title: 'Precompiles', component: DiffPrecompiles },
36+
// predeploys: { title: 'Predeploys', component: DiffPredeploys },
37+
// signatureTypes: { title: 'Transaction and Signature Types', component: DiffSignatureTypes },
38+
// accountTypes: { title: 'Account Types', component: DiffAccountTypes },
39+
// opcodes: { title: 'Opcodes', component: DiffOpcodes },
40+
// mempools: { title: 'Mempools', component: DiffMempools },
41+
// deployedContracts: { title: 'Deployed Contracts', component: DiffDeployedContracts },
42+
// eips: { title: 'Execution EIPs', component: DiffEIPs },
43+
// executionNodes: { title: 'Execution Nodes', component: DiffNodes },
44+
// consensusNodes: { title: 'Consensus Nodes', component: DiffNodes, hide: true }, // Hidden to scope UI to execution data
4645
};
4746

4847
const Diff = () => {
4948
// -------- Parse query parameters --------
5049
const router = useRouter();
5150
const { base, target } = router.query;
5251

53-
const baseChain = getChainById(base as string);
54-
const targetChain = getChainById(target as string);
52+
// ================================ START NEW STUFF ================================
53+
const [baseChain, setBaseChain] = useState(null);
54+
const [targetChain, setTargetChain] = useState(null);
55+
56+
useEffect(() => {
57+
const fetchData = async () => {
58+
try {
59+
const urls = [
60+
`https://raw.githubusercontent.com/mds1/evm-diff/refactor/automated/script/data/chain/${base}.json`,
61+
`https://raw.githubusercontent.com/mds1/evm-diff/refactor/automated/script/data/chain/${target}.json`,
62+
];
63+
64+
const chainData = await Promise.all(
65+
urls.map(async (url) => {
66+
const response = await fetch(url);
67+
return response.json();
68+
})
69+
);
70+
71+
setBaseChain(chainData[0]);
72+
setTargetChain(chainData[1]);
73+
} catch (error) {
74+
console.error('Error fetching data:', error);
75+
}
76+
};
77+
78+
fetchData();
79+
}, [base, target]);
80+
// ================================ END NEW STUFF ================================
5581

5682
const ErrorDiv = () => (
5783
<main className='text-center'>
@@ -80,14 +106,15 @@ const Diff = () => {
80106
target: Chain[keyof Chain];
81107
onlyShowDiff: boolean;
82108
}) => {
109+
// if (!SECTION_MAP[section]) return <></>;
83110
const Component = SECTION_MAP[section].component;
84111
return <Component {...{ base, target, onlyShowDiff }} />;
85112
};
86113

87114
// We take `baseChain` and `targetChain` as arguments to ensure that they are not `undefined`
88115
// and remove the need for `?` and `!` operators.
89116
const DiffDiv = ({ baseChain, targetChain }: { baseChain: Chain; targetChain: Chain }) => {
90-
const sections = Object.keys(baseChain);
117+
const sections = Object.keys(SECTION_MAP);
91118
return (
92119
<>
93120
<main>
@@ -106,9 +133,6 @@ const Diff = () => {
106133

107134
{/* Show content */}
108135
{sections.map((section, index) => {
109-
const hideComponent = SECTION_MAP[section].hide;
110-
if (hideComponent) return <></>;
111-
112136
const base = baseChain[section as keyof Chain];
113137
const target = targetChain[section as keyof Chain];
114138
return (

0 commit comments

Comments
 (0)