Add third way to integrate with Sourcify
This commit is contained in:
parent
05c230ef9d
commit
21ed9ce431
|
@ -32,6 +32,7 @@ import { useFeeToggler } from "./search/useFeeToggler";
|
||||||
import { SelectionContext, useSelection } from "./useSelection";
|
import { SelectionContext, useSelection } from "./useSelection";
|
||||||
import { useMultipleETHUSDOracle } from "./usePriceOracle";
|
import { useMultipleETHUSDOracle } from "./usePriceOracle";
|
||||||
import { useSourcify } from "./useSourcify";
|
import { useSourcify } from "./useSourcify";
|
||||||
|
import { SourcifySource } from "./url";
|
||||||
|
|
||||||
type BlockParams = {
|
type BlockParams = {
|
||||||
addressOrName: string;
|
addressOrName: string;
|
||||||
|
@ -179,11 +180,13 @@ const AddressTransactions: React.FC = () => {
|
||||||
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
const [feeDisplay, feeDisplayToggler] = useFeeToggler();
|
||||||
|
|
||||||
const selectionCtx = useSelection();
|
const selectionCtx = useSelection();
|
||||||
const [useIPFS, setUseIPFS] = useState<boolean>(true);
|
const [sourcifySource, setSourcifySource] = useState<SourcifySource>(
|
||||||
|
SourcifySource.IPFS_IPNS
|
||||||
|
);
|
||||||
const rawMetadata = useSourcify(
|
const rawMetadata = useSourcify(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
provider?.network.chainId,
|
provider?.network.chainId,
|
||||||
useIPFS
|
sourcifySource
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -313,8 +316,8 @@ const AddressTransactions: React.FC = () => {
|
||||||
<Contracts
|
<Contracts
|
||||||
checksummedAddress={checksummedAddress}
|
checksummedAddress={checksummedAddress}
|
||||||
rawMetadata={rawMetadata}
|
rawMetadata={rawMetadata}
|
||||||
useIPFS={useIPFS}
|
sourcifySource={sourcifySource}
|
||||||
setUseIPFS={setUseIPFS}
|
setSourcifySource={setSourcifySource}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { Light as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import hljs from "highlight.js";
|
import hljs from "highlight.js";
|
||||||
import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco";
|
import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco";
|
||||||
import { useContract } from "../useSourcify";
|
import { useContract } from "../useSourcify";
|
||||||
|
import { SourcifySource } from "../url";
|
||||||
|
|
||||||
import hljsDefineSolidity from "highlightjs-solidity";
|
import hljsDefineSolidity from "highlightjs-solidity";
|
||||||
hljsDefineSolidity(hljs);
|
hljsDefineSolidity(hljs);
|
||||||
|
@ -12,7 +13,7 @@ type ContractProps = {
|
||||||
networkId: number;
|
networkId: number;
|
||||||
filename: string;
|
filename: string;
|
||||||
source: any;
|
source: any;
|
||||||
useIPFS: boolean;
|
sourcifySource: SourcifySource;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Contract: React.FC<ContractProps> = ({
|
const Contract: React.FC<ContractProps> = ({
|
||||||
|
@ -20,14 +21,14 @@ const Contract: React.FC<ContractProps> = ({
|
||||||
networkId,
|
networkId,
|
||||||
filename,
|
filename,
|
||||||
source,
|
source,
|
||||||
useIPFS,
|
sourcifySource,
|
||||||
}) => {
|
}) => {
|
||||||
const content = useContract(
|
const content = useContract(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
networkId,
|
networkId,
|
||||||
filename,
|
filename,
|
||||||
source,
|
source,
|
||||||
useIPFS
|
sourcifySource
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState, useEffect, useContext, Fragment } from "react";
|
import React, { useState, useEffect, useContext, Fragment } from "react";
|
||||||
import { commify } from "@ethersproject/units";
|
import { commify } from "@ethersproject/units";
|
||||||
import { Menu, Switch } from "@headlessui/react";
|
import { Menu, RadioGroup } from "@headlessui/react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||||
import ContentFrame from "../ContentFrame";
|
import ContentFrame from "../ContentFrame";
|
||||||
|
@ -10,20 +10,21 @@ import Contract from "./Contract";
|
||||||
import { RuntimeContext } from "../useRuntime";
|
import { RuntimeContext } from "../useRuntime";
|
||||||
import { Metadata } from "../useSourcify";
|
import { Metadata } from "../useSourcify";
|
||||||
import ExternalLink from "../components/ExternalLink";
|
import ExternalLink from "../components/ExternalLink";
|
||||||
import { openInRemixURL } from "../url";
|
import { openInRemixURL, SourcifySource } from "../url";
|
||||||
|
import RadioButton from "./RadioButton";
|
||||||
|
|
||||||
type ContractsProps = {
|
type ContractsProps = {
|
||||||
checksummedAddress: string;
|
checksummedAddress: string;
|
||||||
rawMetadata: Metadata | null | undefined;
|
rawMetadata: Metadata | null | undefined;
|
||||||
useIPFS: boolean;
|
sourcifySource: SourcifySource;
|
||||||
setUseIPFS: (useIPFS: boolean) => void;
|
setSourcifySource: (sourcifySource: SourcifySource) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Contracts: React.FC<ContractsProps> = ({
|
const Contracts: React.FC<ContractsProps> = ({
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
rawMetadata,
|
rawMetadata,
|
||||||
useIPFS,
|
sourcifySource,
|
||||||
setUseIPFS,
|
setSourcifySource,
|
||||||
}) => {
|
}) => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
|
||||||
|
@ -37,20 +38,20 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentFrame tabs>
|
<ContentFrame tabs>
|
||||||
<InfoRow title="Use IPFS">
|
<InfoRow title="Sourcify integration">
|
||||||
<Switch
|
<RadioGroup value={sourcifySource} onChange={setSourcifySource}>
|
||||||
className={`flex items-center h-7 w-12 px-1 rounded-full transition duration-200 ${
|
<div className="flex space-x-2">
|
||||||
useIPFS ? "bg-blue-500" : "bg-blue-900"
|
<RadioButton value={SourcifySource.IPFS_IPNS}>
|
||||||
}`}
|
Resolve IPNS
|
||||||
checked={useIPFS}
|
</RadioButton>
|
||||||
onChange={setUseIPFS}
|
<RadioButton value={SourcifySource.CENTRAL_SERVER}>
|
||||||
>
|
Sourcify Servers
|
||||||
<span
|
</RadioButton>
|
||||||
className={`inline-block border rounded-full w-5 h-5 bg-white transform duration-200 ${
|
<RadioButton value={SourcifySource.CUSTOM_SNAPSHOT_SERVER}>
|
||||||
useIPFS ? "" : "translate-x-5"
|
Local Snapshot
|
||||||
}`}
|
</RadioButton>
|
||||||
></span>
|
</div>
|
||||||
</Switch>
|
</RadioGroup>
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
{rawMetadata && (
|
{rawMetadata && (
|
||||||
<>
|
<>
|
||||||
|
@ -76,6 +77,9 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="py-5">
|
<div className="py-5">
|
||||||
|
{rawMetadata === undefined && (
|
||||||
|
<span>Getting data from Sourcify repository...</span>
|
||||||
|
)}
|
||||||
{rawMetadata === null && (
|
{rawMetadata === null && (
|
||||||
<span>
|
<span>
|
||||||
Address is not a contract or couldn't find contract metadata in
|
Address is not a contract or couldn't find contract metadata in
|
||||||
|
@ -139,7 +143,7 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
networkId={provider!.network.chainId}
|
networkId={provider!.network.chainId}
|
||||||
filename={selected}
|
filename={selected}
|
||||||
source={rawMetadata.sources[selected]}
|
source={rawMetadata.sources[selected]}
|
||||||
useIPFS={useIPFS}
|
sourcifySource={sourcifySource}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import { RadioGroup } from "@headlessui/react";
|
||||||
|
import { SourcifySource } from "../url";
|
||||||
|
|
||||||
|
type RadioButtonProps = {
|
||||||
|
value: SourcifySource;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RadioButton: React.FC<RadioButtonProps> = ({ value, children }) => (
|
||||||
|
<RadioGroup.Option
|
||||||
|
className={({ checked }) =>
|
||||||
|
`border rounded px-2 py-1 cursor-pointer ${
|
||||||
|
checked
|
||||||
|
? "bg-blue-400 hover:bg-blue-500 text-white"
|
||||||
|
: "hover:bg-gray-200"
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
value={value}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</RadioGroup.Option>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default RadioButton;
|
43
src/url.ts
43
src/url.ts
|
@ -14,29 +14,52 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`;
|
||||||
|
|
||||||
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
export const blockTxsURL = (blockNum: BlockTag) => `/block/${blockNum}/txs`;
|
||||||
|
|
||||||
const sourcifyRootHash =
|
export enum SourcifySource {
|
||||||
|
// Resolve trusted IPNS for root IPFS
|
||||||
|
IPFS_IPNS,
|
||||||
|
|
||||||
|
// Centralized Sourcify servers
|
||||||
|
CENTRAL_SERVER,
|
||||||
|
|
||||||
|
// Snapshot server
|
||||||
|
CUSTOM_SNAPSHOT_SERVER,
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourcifyIPNS =
|
||||||
"k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p";
|
"k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p";
|
||||||
const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyRootHash}`;
|
const ipfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyIPNS}`;
|
||||||
|
// const ipfsGatewayPrefix = "http://localhost:8080/ipfs/QmWQoGfrLcizHueg3YkgDCh1S7SkfSP9A7H8YeZmUDfbnn"
|
||||||
const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`;
|
const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`;
|
||||||
|
const snapshotPrefix = "http://localhost:3006";
|
||||||
|
|
||||||
|
const resolveSourcifySource = (source: SourcifySource) => {
|
||||||
|
if (source === SourcifySource.IPFS_IPNS) {
|
||||||
|
return ipfsGatewayPrefix;
|
||||||
|
}
|
||||||
|
if (source === SourcifySource.CENTRAL_SERVER) {
|
||||||
|
return sourcifyHttpRepoPrefix;
|
||||||
|
}
|
||||||
|
return snapshotPrefix;
|
||||||
|
};
|
||||||
|
|
||||||
export const sourcifyMetadata = (
|
export const sourcifyMetadata = (
|
||||||
checksummedAddress: string,
|
checksummedAddress: string,
|
||||||
networkId: number,
|
networkId: number,
|
||||||
useIPFS: boolean
|
source: SourcifySource
|
||||||
) =>
|
) =>
|
||||||
`${
|
`${resolveSourcifySource(
|
||||||
useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix
|
source
|
||||||
}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`;
|
)}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`;
|
||||||
|
|
||||||
export const sourcifySourceFile = (
|
export const sourcifySourceFile = (
|
||||||
checksummedAddress: string,
|
checksummedAddress: string,
|
||||||
networkId: number,
|
networkId: number,
|
||||||
filepath: string,
|
filepath: string,
|
||||||
useIPFS: boolean
|
source: SourcifySource
|
||||||
) =>
|
) =>
|
||||||
`${
|
`${resolveSourcifySource(
|
||||||
useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix
|
source
|
||||||
}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`;
|
)}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`;
|
||||||
|
|
||||||
export const openInRemixURL = (checksummedAddress: string, networkId: number) =>
|
export const openInRemixURL = (checksummedAddress: string, networkId: number) =>
|
||||||
`https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`;
|
`https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { sourcifyMetadata, sourcifySourceFile } from "./url";
|
import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "./url";
|
||||||
|
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
version: string;
|
version: string;
|
||||||
|
@ -39,7 +39,7 @@ export type Metadata = {
|
||||||
export const useSourcify = (
|
export const useSourcify = (
|
||||||
checksummedAddress: string | undefined,
|
checksummedAddress: string | undefined,
|
||||||
chainId: number | undefined,
|
chainId: number | undefined,
|
||||||
useIPFS: boolean
|
source: SourcifySource
|
||||||
) => {
|
) => {
|
||||||
const [rawMetadata, setRawMetadata] = useState<Metadata | null | undefined>();
|
const [rawMetadata, setRawMetadata] = useState<Metadata | null | undefined>();
|
||||||
|
|
||||||
|
@ -48,12 +48,13 @@ export const useSourcify = (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setRawMetadata(undefined);
|
||||||
const fetchMetadata = async () => {
|
const fetchMetadata = async () => {
|
||||||
try {
|
try {
|
||||||
const contractMetadataURL = sourcifyMetadata(
|
const contractMetadataURL = sourcifyMetadata(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
chainId,
|
chainId,
|
||||||
useIPFS
|
source
|
||||||
);
|
);
|
||||||
const result = await fetch(contractMetadataURL);
|
const result = await fetch(contractMetadataURL);
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
|
@ -68,7 +69,7 @@ export const useSourcify = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchMetadata();
|
fetchMetadata();
|
||||||
}, [checksummedAddress, chainId, useIPFS]);
|
}, [checksummedAddress, chainId, source]);
|
||||||
|
|
||||||
return rawMetadata;
|
return rawMetadata;
|
||||||
};
|
};
|
||||||
|
@ -78,7 +79,7 @@ export const useContract = (
|
||||||
networkId: number,
|
networkId: number,
|
||||||
filename: string,
|
filename: string,
|
||||||
source: any,
|
source: any,
|
||||||
useIPFS: boolean = true
|
sourcifySource: SourcifySource
|
||||||
) => {
|
) => {
|
||||||
const [content, setContent] = useState<string>(source.content);
|
const [content, setContent] = useState<string>(source.content);
|
||||||
|
|
||||||
|
@ -93,7 +94,7 @@ export const useContract = (
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
networkId,
|
networkId,
|
||||||
normalizedFilename,
|
normalizedFilename,
|
||||||
useIPFS
|
sourcifySource
|
||||||
);
|
);
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
@ -102,7 +103,7 @@ export const useContract = (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
readContent();
|
readContent();
|
||||||
}, [checksummedAddress, networkId, filename, source.content, useIPFS]);
|
}, [checksummedAddress, networkId, filename, source.content, sourcifySource]);
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue