Add third way to integrate with Sourcify

This commit is contained in:
Willian Mitsuda 2021-09-10 18:27:42 -03:00
parent 05c230ef9d
commit 21ed9ce431
6 changed files with 101 additions and 45 deletions

View File

@ -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>

View File

@ -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 (

View File

@ -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>

View File

@ -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;

View File

@ -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}`;

View File

@ -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;
}; };