Skip to content

feat: add JSON-RPC API methods #52

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

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions src/chains/arbitrum/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export const arbitrum: Chain = {
deployedContracts: sortedArrayByFields(deployedContracts, ['kind', 'name']),
executionNodes,
consensusNodes,
jsonRPCMethods: [],
};
2 changes: 2 additions & 0 deletions src/chains/mainnet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { sortedArrayByField, sortedArrayByFields } from '@/lib/utils';
import { Chain } from '@/types';
import { accountTypes } from './accountTypes';
import { deployedContracts } from './deployedContracts';
import { jsonRPCMethods } from './jsonRpcMethods';
import { mempools } from './mempools';
import { consensusNodes } from './nodes/consensus';
import { executionNodes } from './nodes/execution';
Expand All @@ -22,4 +23,5 @@ export const mainnet: Chain = {
deployedContracts: sortedArrayByFields(deployedContracts, ['kind', 'name']),
executionNodes,
consensusNodes,
jsonRPCMethods,
};
46 changes: 46 additions & 0 deletions src/chains/mainnet/jsonRpcMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Method, MethodNamespace, MethodVariableType as Type } from '@/types';

// web3 namespace.
const web3ClientVersion: Method = {
name: 'web3_clientVersion',
namespace: MethodNamespace.Web3,
description: 'Returns the current client version',
return: {
type: Type.String,
description: 'The current client version',
},
example: {
parameters: [],
result: 'Geth/v1.12.1-stable/linux-amd64/go1.19.1',
},
};

const web3Sha3: Method = {
name: 'web3_sha3',
namespace: MethodNamespace.Web3,
description: 'Returns Keccak-256 (not the standardized SHA3-256) of the given data',
parameters: [
{
type: Type.Data,
description: 'The data to convert into a SHA3 hash',
},
],
return: {
type: Type.Data,
description: 'The SHA3 result of the given string',
},
example: {
parameters: ['0x68656c6c6f20776f726c64'],
result: '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad',
},
};

const web3Methods: Method[] = [web3ClientVersion, web3Sha3];

// TODO: net namespace.
const netMethods: Method[] = [];

// TODO: eth namespace.
const ethMethods: Method[] = [];

export const jsonRPCMethods: Method[] = [...web3Methods, ...netMethods, ...ethMethods];
14 changes: 7 additions & 7 deletions src/chains/mainnet/nodes/execution.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Language, Node, NodeType, SyncStrategy } from '@/types';
import { Language, Node, NodeSyncStrategy, NodeType } from '@/types';

const besu: Node = {
name: 'besu',
description: 'An enterprise-grade Java-based, Apache 2.0 licensed Ethereum client.',
type: NodeType.Execution,
language: Language.Java,
syncStrategy: [SyncStrategy.Snap, SyncStrategy.Fast, SyncStrategy.Full],
syncStrategy: [NodeSyncStrategy.Snap, NodeSyncStrategy.Fast, NodeSyncStrategy.Full],
repository: 'https://github.com/hyperledger/besu',
documentation: 'https://besu.hyperledger.org/',
};
Expand All @@ -15,7 +15,7 @@ const coregeth: Node = {
description: 'A highly configurable Go implementation of the Ethereum protocol.',
type: NodeType.Execution,
language: Language.Go,
syncStrategy: [SyncStrategy.Snap, SyncStrategy.Full],
syncStrategy: [NodeSyncStrategy.Snap, NodeSyncStrategy.Full],
forkOf: 'geth',
repository: 'https://github.com/etclabscore/core-geth',
documentation: 'https://etclabscore.github.io/core-geth/',
Expand All @@ -26,7 +26,7 @@ const erigon: Node = {
description: 'Ethereum implementation on the efficiency frontier.',
type: NodeType.Execution,
language: Language.Go,
syncStrategy: [SyncStrategy.Full],
syncStrategy: [NodeSyncStrategy.Full],
forkOf: 'geth',
repository: 'https://github.com/ledgerwatch/erigon',
documentation: 'https://erigon.gitbook.io/erigon/',
Expand All @@ -37,7 +37,7 @@ const geth: Node = {
description: 'Official Go implementation of the Ethereum protocol.',
type: NodeType.Execution,
language: Language.Go,
syncStrategy: [SyncStrategy.Snap, SyncStrategy.Full],
syncStrategy: [NodeSyncStrategy.Snap, NodeSyncStrategy.Full],
repository: 'https://github.com/ethereum/go-ethereum',
documentation: 'https://geth.ethereum.org/',
};
Expand All @@ -47,7 +47,7 @@ export const nethermind: Node = {
description: 'A robust execution client for Ethereum node operators.',
type: NodeType.Execution,
language: Language.CSharp,
syncStrategy: [SyncStrategy.Snap, SyncStrategy.Fast, SyncStrategy.Full],
syncStrategy: [NodeSyncStrategy.Snap, NodeSyncStrategy.Fast, NodeSyncStrategy.Full],
repository: 'https://github.com/NethermindEth/nethermind',
documentation: 'https://docs.nethermind.io/',
};
Expand All @@ -58,7 +58,7 @@ export const reth: Node = {
'Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol, in Rust.',
type: NodeType.Execution,
language: Language.Rust,
syncStrategy: [SyncStrategy.Full],
syncStrategy: [NodeSyncStrategy.Full],
repository: 'https://github.com/paradigmxyz/reth',
documentation: 'https://paradigmxyz.github.io/reth/',
};
Expand Down
2 changes: 1 addition & 1 deletion src/chains/mainnet/vm/opcodes/stack/dup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
evmCodesOpcodesLink,
evmCodesPlaygroundLink,
} from '@/lib/opcodes';
import { Opcode, Variable } from '@/types';
import { Opcode, OpcodeVariable as Variable } from '@/types';

const generateIgnoredValues = (n: number): Variable[] => {
const alphabet = 'abcdefghijklmno';
Expand Down
2 changes: 1 addition & 1 deletion src/chains/mainnet/vm/opcodes/stack/swap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
evmCodesOpcodesLink,
evmCodesPlaygroundLink,
} from '@/lib/opcodes';
import { Opcode, Variable } from '@/types';
import { Opcode, OpcodeVariable as Variable } from '@/types';

const generateIgnoredValues = (n: number): Variable[] => {
const alphabet = 'abcdefghijklmno';
Expand Down
1 change: 1 addition & 0 deletions src/chains/optimism/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export const optimism: Chain = {
deployedContracts: sortedArrayByFields(deployedContracts, ['kind', 'name']),
executionNodes,
consensusNodes,
jsonRPCMethods: [],
};
145 changes: 145 additions & 0 deletions src/components/diff/DiffMethods.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { RenderDiff } from '@/components/diff/utils/RenderDiff';
import { Copyable } from '@/components/ui/Copyable';
import { jsonRPCMethodSrc } from '@/lib/methods';
import {
Method,
MethodExample,
MethodNamespace,
MethodVariableType as Type,
MethodVariable as Variable,
} from '@/types';
import { Collapsible } from './utils/Collapsible';
import { Markdown } from './utils/Markdown';

type Props = {
base: Method[];
target: Method[];
onlyShowDiff: boolean;
};

export const DiffMethods = ({ base, target, onlyShowDiff }: Props) => {
const sortedMethodNames = [...base.map((n) => n.name), ...target.map((n) => n.name)].sort(
(a, b) => a.localeCompare(b)
);
const methodNames = [...new Set(sortedMethodNames)];

const diffContent = (
<>
{methodNames.map((name) => {
const baseMethod = base.find((n) => n.name === name);
const targetMethod = target.find((n) => n.name === name);
if (!baseMethod && !targetMethod) {
return <></>;
}

const isEqual = JSON.stringify(baseMethod) === JSON.stringify(targetMethod);
const showNode = !isEqual || !onlyShowDiff;

return (
showNode && (
<div
key={name}
className='grid grid-cols-12 items-center border-b border-zinc-500/10 py-6 dark:border-zinc-500/20'
>
<div className='col-span-2'>
<Copyable content={name} />
</div>
<div className='col-span-5 pr-4'>{formatMethod(baseMethod)}</div>
<div className='col-span-5'>{formatMethod(targetMethod)}</div>
</div>
)
);
})}
</>
);

return <RenderDiff content={diffContent} />;
};

const formatMethod = (method: Method | undefined) => {
if (!method) return <div>Not present</div>;
return (
<>
<Markdown codeSize='0.9rem' content={method.name} />
<div className='text-secondary text-sm'>
<Markdown content={method.description} />
</div>
<div className='text-secondary mt-3 grid grid-cols-4 space-y-1 text-sm'>
<div className='col-span-2'>Namespace</div>
<div className='col-span-2'>{formatNamespace(method.namespace)}</div>
</div>
{method.parameters && formatParameters(method.parameters)}
<div className='mt-4 text-sm'>
<Collapsible
kind='custom'
title='Return'
contents={`- ${formatType(method.return.type)}: ${method.return.description}`}
/>
</div>
{method.example && formatExample(method.name, method.example)}
<div className='mt-4'>
<Collapsible
kind='references'
contents={`[ethereum.org](${jsonRPCMethodSrc(method.name)})`}
/>
</div>
</>
);
};

const formatNamespace = (n: MethodNamespace) =>
n === MethodNamespace.Web3
? 'Web3'
: n === MethodNamespace.Net
? 'Net'
: n === MethodNamespace.Eth
? 'Web3'
: (() => {
throw new Error(`Unsupported namespace: ${n}`);
})();

const formatType = (t: Type): string =>
t === Type.String
? 'String'
: t === Type.Data
? 'Data'
: (() => {
throw new Error(`Unsupported type: ${t}`);
})();

const formatParameters = (params: Variable[]): JSX.Element => {
if (!Array.isArray(params)) return <></>;
const contents = (
<>
<ul className='text-sm'>
{params.map((p) => (
<li key={p.description}>
- {formatType(p.type)}: {p.description}
</li>
))}
</ul>
</>
);
return <Collapsible kind='custom' title='Parameters' contents={contents} />;
};

const formatExample = (name: string, example: MethodExample): JSX.Element => {
const id = 74;
const version = '2.0';
const request = `curl -H 'Content-Type: application/json' -d '{"jsonrpc": "${version}", "method": "${name}", "params": [${
example.parameters === undefined ? '' : example.parameters.map((p) => `"${p}"`).join(', ')
}], "id": ${id}}' <url>`;
const result = `{"jsonrpc":"${version}","id":${id},"result":"${example.result}"}`;
const contents = (
<code className='text-secondary text-xs'>
# request
<br />
{request}
<br />
# result
<br />
{result}
</code>
);
return <Collapsible kind='custom' title='Example' contents={contents} />;
};
10 changes: 5 additions & 5 deletions src/components/diff/DiffNodes.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RenderDiff } from '@/components/diff/utils/RenderDiff';
import { Copyable } from '@/components/ui/Copyable';
import { Language, Node, NodeType, SyncStrategy } from '@/types';
import { Language, Node, NodeSyncStrategy, NodeType } from '@/types';
import { Collapsible } from './utils/Collapsible';
import { Markdown } from './utils/Markdown';

Expand Down Expand Up @@ -98,8 +98,8 @@ const formatSyncStrategies = (n: Node) => {
);
};

const formatSyncStrategy = (s: SyncStrategy) => {
if (s === SyncStrategy.Snap) return 'Snap';
if (s === SyncStrategy.Full) return 'Full';
if (s === SyncStrategy.Fast) return 'Fast';
const formatSyncStrategy = (s: NodeSyncStrategy) => {
if (s === NodeSyncStrategy.Snap) return 'Snap';
if (s === NodeSyncStrategy.Full) return 'Full';
if (s === NodeSyncStrategy.Fast) return 'Fast';
};
8 changes: 4 additions & 4 deletions src/components/diff/DiffOpcodes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Copyable } from '@/components/ui/Copyable';
import { CURRENT_MAINNET_HARDFORK } from '@/lib/constants';
import { classNames, formatPrefixByte } from '@/lib/utils';
import { toUppercase } from '@/lib/utils';
import { Example, Opcode, Variable } from '@/types';
import { Opcode, OpcodeExample, OpcodeVariable } from '@/types';
import { GasComputation } from '@/types/opcode';

type Props = {
Expand Down Expand Up @@ -40,7 +40,7 @@ const formatHardfork = (array: string[]): JSX.Element => {
);
};

const formatVariables = (title: string, array?: Variable[]): JSX.Element => {
const formatVariables = (title: string, array?: OpcodeVariable[]): JSX.Element => {
return (
<>
<h3 className={classNames('font-bold', 'mt-2')}>{toUppercase(title)}</h3>
Expand All @@ -57,7 +57,7 @@ const formatVariables = (title: string, array?: Variable[]): JSX.Element => {
);
};

const formatVariable = (v: Variable): JSX.Element => {
const formatVariable = (v: OpcodeVariable): JSX.Element => {
return (
<div key={v.name} className='text-secondary'>
<p>
Expand Down Expand Up @@ -112,7 +112,7 @@ const formatExamples = (opcode: Opcode): JSX.Element => {
return <Collapsible kind='custom' title='Examples' contents={contents} />;
};

const formatExample = (e: Example, id: number): JSX.Element => {
const formatExample = (e: OpcodeExample, id: number): JSX.Element => {
const input = e.input ? '[' + e.input.toString() + ']' : '[]';
const output = '[' + (e.output ? e.output : '') + ']';
return (
Expand Down
2 changes: 2 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export { CURRENT_MAINNET_HARDFORK } from '@/chains/mainnet/hardforks';
export const ETHEREUM_EXECUTION_SPECS_URL = 'https://github.com/ethereum/execution-specs';
export const ETHEREUM_EXECUTION_SPECS_COMMIT_ID = '87f5e4f5ec03c6a23b2d2cd909482664f870fd1e';
export const EVM_OPCODES_URL = 'https://www.evm.codes';

export const ETHEREUM_ORG_JSON_RPC_URL = 'https://ethereum.org/en/developers/docs/apis/json-rpc';
3 changes: 3 additions & 0 deletions src/lib/methods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { ETHEREUM_ORG_JSON_RPC_URL } from './constants';

export const jsonRPCMethodSrc = (name: string): string => `${ETHEREUM_ORG_JSON_RPC_URL}#${name}`;
2 changes: 2 additions & 0 deletions src/pages/diff.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DiffAccountTypes } from '@/components/diff/DiffAccountTypes';
import { DiffDeployedContracts } from '@/components/diff/DiffDeployedContracts';
import { DiffMempools } from '@/components/diff/DiffMempools';
import { DiffMetadata } from '@/components/diff/DiffMetadata';
import { DiffMethods } from '@/components/diff/DiffMethods';
import { DiffNodes } from '@/components/diff/DiffNodes';
import { DiffOpcodes } from '@/components/diff/DiffOpcodes';
import { DiffPrecompiles } from '@/components/diff/DiffPrecompiles';
Expand Down Expand Up @@ -41,6 +42,7 @@ const SECTION_MAP: Record<string, Section> = {
deployedContracts: { title: 'Deployed Contracts', component: DiffDeployedContracts },
executionNodes: { title: 'Execution Nodes', component: DiffNodes },
consensusNodes: { title: 'Consensus Nodes', component: DiffNodes },
jsonRPCMethods: { title: 'JSON-RPC Methods', component: DiffMethods },
};

const Diff = () => {
Expand Down
2 changes: 2 additions & 0 deletions src/types/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Chain as Metadata } from '@wagmi/chains';
import { AccountType } from './accountType';
import { DeployedContract } from './deployedContract';
import { Mempool } from './mempool';
import { Method } from './method';
import { Node } from './node';
import { Opcode } from './opcode';
import { Precompile } from './precompile';
Expand All @@ -19,4 +20,5 @@ export type Chain = {
deployedContracts: DeployedContract[];
executionNodes: Node[];
consensusNodes: Node[];
jsonRPCMethods: Method[];
};
Loading