Skip to content

Commit 3578df6

Browse files
committed
feat: add opcodes back to UI
1 parent 4dd3804 commit 3578df6

File tree

2 files changed

+15
-249
lines changed

2 files changed

+15
-249
lines changed

src/components/diff/DiffOpcodes.tsx

Lines changed: 13 additions & 248 deletions
Original file line numberDiff line numberDiff line change
@@ -1,260 +1,44 @@
1+
import { Chain } from '@/../script/index';
12
import { Collapsible } from '@/components/diff/utils/Collapsible';
23
import { Markdown } from '@/components/diff/utils/Markdown';
34
import { RenderDiff } from '@/components/diff/utils/RenderDiff';
45
import { ExternalLink } from '@/components/layout/ExternalLink';
56
import { Copyable } from '@/components/ui/Copyable';
67
import { classNames, formatPrefixByte } from '@/lib/utils';
78
import { toUppercase } from '@/lib/utils';
8-
import { Example, Opcode, Variable } from '@/types';
99
import { GasComputation } from '@/types/opcode';
1010
import { formatHardfork, formatStringList } from './utils/format';
1111

12+
type Opcodes = Chain['opcodes'];
13+
type Opcode = Opcodes[0];
14+
1215
type Props = {
13-
base: Opcode[];
14-
target: Opcode[];
16+
base: Opcodes;
17+
target: Opcodes;
1518
onlyShowDiff: boolean;
1619
};
1720

18-
const formatVariables = (title: string, array?: Variable[]): JSX.Element => {
19-
return (
20-
<>
21-
<h3 className={classNames('font-bold', 'mt-2')}>{toUppercase(title)}</h3>
22-
{array === undefined || array.length === 0 ? (
23-
<>None</>
24-
) : (
25-
<ul>
26-
{array.map((v, id) => (
27-
<li key={id}>{formatVariable(v)}</li>
28-
))}
29-
</ul>
30-
)}
31-
</>
32-
);
33-
};
34-
35-
const formatVariable = (v: Variable): JSX.Element => {
36-
return (
37-
<div key={v.name} className='text-secondary'>
38-
<p>
39-
<code className='text-sm'>{v.name}</code>: <span>{v.description}</span>
40-
</p>
41-
{v.expression && (
42-
<>
43-
<div className='text-sm'>
44-
<h5 className='text-primary mt-4 font-semibold'>
45-
Sub-variables (<code>{v.name}</code>)
46-
</h5>
47-
<code>
48-
{v.name} = {v.expression}
49-
</code>
50-
{v.variables && (
51-
<>
52-
<ul>{v.variables.map((subvariables) => formatVariable(subvariables))}</ul>
53-
</>
54-
)}
55-
</div>
56-
<br />
57-
</>
58-
)}
59-
</div>
60-
);
61-
};
62-
63-
const formatExamples = (opcode: Opcode): JSX.Element => {
64-
if (!opcode.examples || opcode.examples.length === 0) return <></>;
65-
const contents = (
66-
<>
67-
<div className='text-secondary text-sm'>
68-
Stack inputs are shown on the left of the arrow symbol and stack outputs on the right.{' '}
69-
{opcode.playgroundLink && (
70-
<span>
71-
Or,{' '}
72-
<ExternalLink className='text-sm' href={opcode.playgroundLink}>
73-
try it out
74-
</ExternalLink>{' '}
75-
on the playground.
76-
</span>
77-
)}
78-
</div>
79-
<ul>
80-
{opcode.examples.map((e, id) => (
81-
<li key={id}>{formatExample(e, id)}</li>
82-
))}
83-
</ul>
84-
</>
85-
);
86-
87-
return <Collapsible kind='custom' title='Examples' contents={contents} />;
88-
};
89-
90-
const formatExample = (e: Example, id: number): JSX.Element => {
91-
const input = e.input ? '[' + e.input.toString() + ']' : '[]';
92-
const output = '[' + (e.output ? e.output : '') + ']';
93-
return (
94-
<>
95-
<h4 className='mt-3 font-semibold'>Example #{id}</h4>
96-
<div className='ml-3'>
97-
{e.description && <p>{e.description}</p>}
98-
<code className='text-secondary text-sm'>
99-
{input} {'=>'} {output}
100-
</code>
101-
{e.memory && (
102-
<>
103-
<h5 className='mt-4 font-bold'>Memory</h5>
104-
<h6 className='text-secondary font-semibold'>Before</h6>
105-
<code className='text-secondary text-sm'>
106-
{e.memory.before ? e.memory.before : '[]'}
107-
</code>
108-
<h6 className='text-secondary mt-2 font-semibold'>After</h6>
109-
<code className='text-secondary text-sm'>{e.memory.after ? e.memory.after : '[]'}</code>
110-
</>
111-
)}
112-
{e.storage && (
113-
<>
114-
<h5 className='mt-4 font-bold'>Storage</h5>
115-
<h6 className='text-secondary font-semibold'>Before</h6>
116-
<code className='text-secondary text-sm'>
117-
{e.storage.before && formatStorage(e.storage.before)}
118-
</code>
119-
<h6 className='text-secondary mt-2 font-semibold'>After</h6>
120-
<code className='text-secondary text-sm'>
121-
{e.storage.after && formatStorage(e.storage.after)}
122-
</code>
123-
</>
124-
)}
125-
{e.calldata && (
126-
<>
127-
<h5 className='mt-4 font-bold'>Calldata</h5>
128-
<code className='text-secondary text-sm'>{e.calldata}</code>
129-
</>
130-
)}
131-
{e.code && (
132-
<>
133-
<h5 className='mt-4 font-bold'>Code</h5>
134-
<code className='text-secondary text-sm'>{e.code}</code>
135-
</>
136-
)}
137-
{e.returndata && (
138-
<>
139-
<h5 className='mt-4 font-bold'>Return Data</h5>
140-
<code className='text-secondary text-sm'>{e.returndata}</code>
141-
</>
142-
)}
143-
</div>
144-
</>
145-
);
146-
};
147-
148-
const formatStorage = (record: Record<string, string>): JSX.Element => {
149-
if (!record || record === undefined) return <></>;
150-
const keyValues: JSX.Element[] = [];
151-
for (const key in record) {
152-
keyValues.push(
153-
<li key={key}>
154-
{key}: {record[key]}
155-
</li>
156-
);
157-
}
158-
return <ul>{keyValues}</ul>;
159-
};
160-
161-
const formatGasComputation = (gc: GasComputation | undefined): JSX.Element => {
162-
if (!gc) return <></>;
163-
const contents = (
164-
<>
165-
<code className='text-secondary text-sm'>gas_cost = static_gas_cost + dynamic_gas_cost</code>
166-
167-
<div>
168-
{gc.staticGasCost && (
169-
<>
170-
<code className='text-secondary text-sm'>
171-
static_gas_cost = {gc.staticGasCost.expression}
172-
</code>
173-
{gc.staticGasCost.variables && (
174-
<>
175-
<h5 className='mt-4 font-bold'>Sub-variables (static_gas_cost)</h5>
176-
<ul>
177-
{gc.staticGasCost.variables.map((v) => (
178-
<li key={v.name}>{formatVariable(v)}</li>
179-
))}
180-
</ul>
181-
</>
182-
)}
183-
</>
184-
)}
185-
</div>
186-
187-
<div>
188-
{gc.dynamicGasCost && (
189-
<>
190-
<code className='text-secondary text-sm'>
191-
dynamic_gas_cost = {gc.dynamicGasCost.expression}
192-
</code>
193-
{gc.dynamicGasCost.variables && (
194-
<>
195-
<h5 className='mt-4 font-semibold'>
196-
Sub-variables (<code className='text-sm'>dynamic_gas_cost</code>)
197-
</h5>
198-
<ul>
199-
{gc.dynamicGasCost.variables.map((v) => (
200-
<li key={v.name}>{formatVariable(v)}</li>
201-
))}
202-
</ul>
203-
</>
204-
)}
205-
</>
206-
)}
207-
</div>
208-
209-
<h4 className='mt-3 font-semibold'>Refunds</h4>
210-
<p className='text-secondary'>{gc.refunds || 'No refunds'}</p>
211-
</>
212-
);
213-
return <Collapsible kind='custom' title='Gas Computation' contents={contents} />;
214-
};
215-
21621
const formatOpcode = (opcode: Opcode | undefined): JSX.Element => {
217-
if (!opcode) return <p>Not present</p>;
21822
return (
219-
<>
220-
<Markdown className='mb-4' content={opcode.description} />
221-
{formatHardfork(opcode.supportedHardforks)}
222-
<p className='text-secondary text-sm'>⛽️ Minimum Gas: {opcode.minGas}</p>
223-
{formatVariables('Inputs', opcode.inputs)}
224-
{formatVariables('Outputs', opcode.outputs)}
225-
{formatStringList('Error Cases', opcode.errorCases)}
226-
{formatStringList('Notes', opcode.notes)}
227-
<div className='mt-4'>
228-
{formatExamples(opcode)}
229-
{formatGasComputation(opcode.gasComputation)}
230-
<Collapsible kind='references' contents={opcode.references} />
231-
</div>
232-
</>
23+
<div>{opcode?.supported === 'unknown' ? 'Unknown' : opcode?.supported ? 'Yes' : 'No'}</div>
23324
);
23425
};
23526

23627
export const DiffOpcodes = ({ base, target, onlyShowDiff }: Props): JSX.Element => {
23728
if (!Array.isArray(base) || !Array.isArray(target)) return <></>;
23829

239-
// Generate a sorted list of all opcode numbers from both base and target.
240-
const sortedOpcodeNumbers = [
241-
...base.map((opcode) => opcode.number),
242-
...target.map((opcode) => opcode.number),
243-
].sort((a, b) => a - b);
244-
const opcodeNumbers = [...new Set(sortedOpcodeNumbers)];
30+
const opcodeNumbers = Array.from(Array(0xff + 1).keys());
24531

24632
const diffContent = (
24733
<>
24834
{opcodeNumbers.map((number) => {
249-
const baseOpcode = base.find((opcode) => opcode.number === number);
250-
const targetOpcode = target.find((opcode) => opcode.number === number);
35+
const baseOpcode = base.find((opcode) => Number(opcode.number) === number);
36+
const targetOpcode = target.find((opcode) => Number(opcode.number) === number);
25137
if (!baseOpcode || !targetOpcode) {
252-
return <></>;
38+
return <div key={number}></div>;
25339
}
25440

255-
const isEqual =
256-
JSON.stringify(convertToComparableOpcode(baseOpcode)) ===
257-
JSON.stringify(convertToComparableOpcode(targetOpcode));
41+
const isEqual = JSON.stringify(baseOpcode) === JSON.stringify(targetOpcode);
25842
const showOpcode = !isEqual || !onlyShowDiff;
25943

26044
return (
@@ -265,7 +49,7 @@ export const DiffOpcodes = ({ base, target, onlyShowDiff }: Props): JSX.Element
26549
>
26650
<div className='col-span-2'>
26751
<Copyable content={baseOpcode?.name.toLocaleUpperCase()} />
268-
<Copyable content={formatPrefixByte(baseOpcode?.number)} />
52+
<Copyable content={formatPrefixByte(Number(baseOpcode?.number))} />
26953
</div>
27054
<div className='col-span-5 pr-4'>{formatOpcode(baseOpcode)}</div>
27155
<div className='col-span-5'>{formatOpcode(targetOpcode)}</div>
@@ -278,22 +62,3 @@ export const DiffOpcodes = ({ base, target, onlyShowDiff }: Props): JSX.Element
27862

27963
return <RenderDiff content={diffContent} />;
28064
};
281-
282-
// Convert an `Opcode` object to a simpler struct in order to compare it to other opcodes.
283-
// Note: casting an object from a type with properties X, Y and Z to a subset type with properties
284-
// X and Y using the `as` keyword will still retain the field Z unless you explicitly remove it.
285-
// That's why this function exists.
286-
export const convertToComparableOpcode = (
287-
opcode: Opcode
288-
): Omit<Opcode, 'examples' | 'playgroundLink' | 'notes' | 'references' | 'supportedHardforks'> => {
289-
return {
290-
number: opcode.number,
291-
name: opcode.name,
292-
description: opcode.description,
293-
minGas: opcode.minGas,
294-
gasComputation: opcode.gasComputation,
295-
inputs: opcode.inputs,
296-
outputs: opcode.outputs,
297-
errorCases: opcode.errorCases,
298-
};
299-
};

src/pages/diff.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ interface Section {
3232

3333
const SECTION_MAP: Record<string, Section> = {
3434
metadata: { title: 'Metadata', component: DiffMetadata },
35+
opcodes: { title: 'Opcodes', component: DiffOpcodes },
3536
// precompiles: { title: 'Precompiles', component: DiffPrecompiles },
3637
// predeploys: { title: 'Predeploys', component: DiffPredeploys },
3738
// signatureTypes: { title: 'Transaction and Signature Types', component: DiffSignatureTypes },
3839
// accountTypes: { title: 'Account Types', component: DiffAccountTypes },
39-
// opcodes: { title: 'Opcodes', component: DiffOpcodes },
4040
// mempools: { title: 'Mempools', component: DiffMempools },
4141
// deployedContracts: { title: 'Deployed Contracts', component: DiffDeployedContracts },
4242
// eips: { title: 'Execution EIPs', component: DiffEIPs },
@@ -54,6 +54,7 @@ const Diff = () => {
5454
const [targetChain, setTargetChain] = useState(null);
5555

5656
useEffect(() => {
57+
if (!base || !target) return;
5758
const fetchData = async () => {
5859
try {
5960
const urls = [

0 commit comments

Comments
 (0)