diff --git a/src/AddressTransactions.tsx b/src/AddressTransactions.tsx index 47c9653..45a07ca 100644 --- a/src/AddressTransactions.tsx +++ b/src/AddressTransactions.tsx @@ -32,6 +32,7 @@ import { useFeeToggler } from "./search/useFeeToggler"; import { SelectionContext, useSelection } from "./useSelection"; import { useMultipleETHUSDOracle } from "./usePriceOracle"; import { useSourcify } from "./useSourcify"; +import { SourcifySource } from "./url"; type BlockParams = { addressOrName: string; @@ -179,11 +180,13 @@ const AddressTransactions: React.FC = () => { const [feeDisplay, feeDisplayToggler] = useFeeToggler(); const selectionCtx = useSelection(); - const [useIPFS, setUseIPFS] = useState(true); + const [sourcifySource, setSourcifySource] = useState( + SourcifySource.IPFS_IPNS + ); const rawMetadata = useSourcify( checksummedAddress, provider?.network.chainId, - useIPFS + sourcifySource ); return ( @@ -313,8 +316,8 @@ const AddressTransactions: React.FC = () => { diff --git a/src/address/Contract.tsx b/src/address/Contract.tsx index 8a18d86..eece5fd 100644 --- a/src/address/Contract.tsx +++ b/src/address/Contract.tsx @@ -3,6 +3,7 @@ import { Light as SyntaxHighlighter } from "react-syntax-highlighter"; import hljs from "highlight.js"; import docco from "react-syntax-highlighter/dist/esm/styles/hljs/docco"; import { useContract } from "../useSourcify"; +import { SourcifySource } from "../url"; import hljsDefineSolidity from "highlightjs-solidity"; hljsDefineSolidity(hljs); @@ -12,7 +13,7 @@ type ContractProps = { networkId: number; filename: string; source: any; - useIPFS: boolean; + sourcifySource: SourcifySource; }; const Contract: React.FC = ({ @@ -20,14 +21,14 @@ const Contract: React.FC = ({ networkId, filename, source, - useIPFS, + sourcifySource, }) => { const content = useContract( checksummedAddress, networkId, filename, source, - useIPFS + sourcifySource ); return ( diff --git a/src/address/Contracts.tsx b/src/address/Contracts.tsx index 94f25c4..fb2f3a7 100644 --- a/src/address/Contracts.tsx +++ b/src/address/Contracts.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect, useContext, Fragment } from "react"; import { commify } from "@ethersproject/units"; -import { Menu, Switch } from "@headlessui/react"; +import { Menu, RadioGroup } from "@headlessui/react"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; import ContentFrame from "../ContentFrame"; @@ -10,20 +10,21 @@ import Contract from "./Contract"; import { RuntimeContext } from "../useRuntime"; import { Metadata } from "../useSourcify"; import ExternalLink from "../components/ExternalLink"; -import { openInRemixURL } from "../url"; +import { openInRemixURL, SourcifySource } from "../url"; +import RadioButton from "./RadioButton"; type ContractsProps = { checksummedAddress: string; rawMetadata: Metadata | null | undefined; - useIPFS: boolean; - setUseIPFS: (useIPFS: boolean) => void; + sourcifySource: SourcifySource; + setSourcifySource: (sourcifySource: SourcifySource) => void; }; const Contracts: React.FC = ({ checksummedAddress, rawMetadata, - useIPFS, - setUseIPFS, + sourcifySource, + setSourcifySource, }) => { const { provider } = useContext(RuntimeContext); @@ -37,20 +38,20 @@ const Contracts: React.FC = ({ return ( - - - - + + +
+ + Resolve IPNS + + + Sourcify Servers + + + Local Snapshot + +
+
{rawMetadata && ( <> @@ -76,6 +77,9 @@ const Contracts: React.FC = ({ )}
+ {rawMetadata === undefined && ( + Getting data from Sourcify repository... + )} {rawMetadata === null && ( Address is not a contract or couldn't find contract metadata in @@ -139,7 +143,7 @@ const Contracts: React.FC = ({ networkId={provider!.network.chainId} filename={selected} source={rawMetadata.sources[selected]} - useIPFS={useIPFS} + sourcifySource={sourcifySource} /> )}
diff --git a/src/address/RadioButton.tsx b/src/address/RadioButton.tsx new file mode 100644 index 0000000..e93fad3 --- /dev/null +++ b/src/address/RadioButton.tsx @@ -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 = ({ value, children }) => ( + + `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} + +); + +export default RadioButton; diff --git a/src/url.ts b/src/url.ts index e2a191d..ffbca9b 100644 --- a/src/url.ts +++ b/src/url.ts @@ -14,29 +14,52 @@ export const blockURL = (blockNum: BlockTag) => `/block/${blockNum}`; 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"; -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 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 = ( checksummedAddress: string, networkId: number, - useIPFS: boolean + source: SourcifySource ) => - `${ - useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix - }/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; + `${resolveSourcifySource( + source + )}/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; export const sourcifySourceFile = ( checksummedAddress: string, networkId: number, filepath: string, - useIPFS: boolean + source: SourcifySource ) => - `${ - useIPFS ? ipfsGatewayPrefix : sourcifyHttpRepoPrefix - }/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; + `${resolveSourcifySource( + source + )}/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`; export const openInRemixURL = (checksummedAddress: string, networkId: number) => `https://remix.ethereum.org/#call=source-verification//fetchAndSave//${checksummedAddress}//${networkId}`; diff --git a/src/useSourcify.ts b/src/useSourcify.ts index ac0cbde..e8806b9 100644 --- a/src/useSourcify.ts +++ b/src/useSourcify.ts @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { sourcifyMetadata, sourcifySourceFile } from "./url"; +import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "./url"; export type Metadata = { version: string; @@ -39,7 +39,7 @@ export type Metadata = { export const useSourcify = ( checksummedAddress: string | undefined, chainId: number | undefined, - useIPFS: boolean + source: SourcifySource ) => { const [rawMetadata, setRawMetadata] = useState(); @@ -48,12 +48,13 @@ export const useSourcify = ( return; } + setRawMetadata(undefined); const fetchMetadata = async () => { try { const contractMetadataURL = sourcifyMetadata( checksummedAddress, chainId, - useIPFS + source ); const result = await fetch(contractMetadataURL); if (result.ok) { @@ -68,7 +69,7 @@ export const useSourcify = ( } }; fetchMetadata(); - }, [checksummedAddress, chainId, useIPFS]); + }, [checksummedAddress, chainId, source]); return rawMetadata; }; @@ -78,7 +79,7 @@ export const useContract = ( networkId: number, filename: string, source: any, - useIPFS: boolean = true + sourcifySource: SourcifySource ) => { const [content, setContent] = useState(source.content); @@ -93,7 +94,7 @@ export const useContract = ( checksummedAddress, networkId, normalizedFilename, - useIPFS + sourcifySource ); const res = await fetch(url); if (res.ok) { @@ -102,7 +103,7 @@ export const useContract = ( } }; readContent(); - }, [checksummedAddress, networkId, filename, source.content, useIPFS]); + }, [checksummedAddress, networkId, filename, source.content, sourcifySource]); return content; };