Add support for external files

This commit is contained in:
Willian Mitsuda 2021-07-24 20:33:45 -03:00
parent 0b328123d3
commit f15c75bb8e
4 changed files with 137 additions and 79 deletions

View File

@ -16,7 +16,7 @@ import Copy from "./components/Copy";
import ContentFrame from "./ContentFrame"; import ContentFrame from "./ContentFrame";
import TabGroup from "./components/TabGroup"; import TabGroup from "./components/TabGroup";
import Tab from "./components/Tab"; import Tab from "./components/Tab";
import Contract from "./address/Contract"; import Contracts from "./address/Contracts";
import UndefinedPageControl from "./search/UndefinedPageControl"; import UndefinedPageControl from "./search/UndefinedPageControl";
import ResultHeader from "./search/ResultHeader"; import ResultHeader from "./search/ResultHeader";
import PendingResults from "./search/PendingResults"; import PendingResults from "./search/PendingResults";
@ -273,7 +273,7 @@ const AddressTransactions: React.FC = () => {
</ContentFrame> </ContentFrame>
</Route> </Route>
<Route path="/address/:addressOrName/contract" exact> <Route path="/address/:addressOrName/contract" exact>
<Contract checksummedAddress={checksummedAddress} /> <Contracts checksummedAddress={checksummedAddress} />
</Route> </Route>
</Switch> </Switch>
</> </>

View File

@ -1,100 +1,53 @@
import React, { useState, useEffect, useContext } from "react"; import React, { useState } from "react";
import { ethers } from "ethers";
import ContentFrame from "../ContentFrame";
import Highlight from "react-highlight"; import Highlight from "react-highlight";
import InfoRow from "../components/InfoRow";
import { sourcifyMetadata } from "../url";
import { RuntimeContext } from "../useRuntime";
import "highlight.js/styles/stackoverflow-light.css"; import "highlight.js/styles/stackoverflow-light.css";
import hljs from "highlight.js"; import hljs from "highlight.js";
import hljsDefineSolidity from "highlightjs-solidity"; import hljsDefineSolidity from "highlightjs-solidity";
import { useEffect } from "react";
import { sourcifySourceFile } from "../url";
hljsDefineSolidity(hljs); hljsDefineSolidity(hljs);
hljs.initHighlightingOnLoad(); hljs.initHighlightingOnLoad();
type ContractProps = { type ContractProps = {
checksummedAddress: string; checksummedAddress: string;
networkId: number;
filename: string;
source: any;
}; };
const Contract: React.FC<ContractProps> = ({ checksummedAddress }) => { const Contract: React.FC<ContractProps> = ({
const { provider } = useContext(RuntimeContext); checksummedAddress,
const [rawMetadata, setRawMetadata] = useState<any>(); networkId,
filename,
source,
}) => {
const [content, setContent] = useState<string>(source.content);
useEffect(() => { useEffect(() => {
if (!checksummedAddress) { if (source.content) {
return; return;
} }
const fetchMetadata = async () => { const readContent = async () => {
try { const normalizedFilename = filename.replaceAll("@", "_");
const result = await fetch( const url = sourcifySourceFile(
sourcifyMetadata(checksummedAddress, provider!.network.chainId) checksummedAddress,
networkId,
normalizedFilename
); );
if (result.ok) { const res = await fetch(url);
const json = await result.json(); if (res.ok) {
console.log(json); const _content = await res.text();
setRawMetadata(json); setContent(_content);
setSelected(Object.keys(json.sources)[0]);
} else {
setRawMetadata(null);
}
} catch (err) {
console.error(err);
setRawMetadata(null);
} }
}; };
fetchMetadata(); readContent();
}, [provider, checksummedAddress]); }, [checksummedAddress, networkId, filename, source.content]);
const [selected, setSelected] = useState<string>();
const optimizer = rawMetadata?.settings?.optimizer;
return ( return (
<ContentFrame tabs>
{rawMetadata && (
<>
<InfoRow title="Compiler">
<span>{rawMetadata.compiler?.version}</span>
</InfoRow>
<InfoRow title="Optimizer Enabled">
{optimizer?.enabled ? (
<span>
<span className="font-bold text-green-600">Yes</span> with{" "}
<span className="font-bold text-green-600">
{ethers.utils.commify(optimizer?.runs)}
</span>{" "}
runs
</span>
) : (
<span className="font-bold text-red-600">No</span>
)}
</InfoRow>
</>
)}
<div className="py-5">
{rawMetadata === null && (
<span>Couldn't find contract metadata in Sourcify repository.</span>
)}
{rawMetadata !== undefined && rawMetadata !== null && (
<div>
{Object.entries(rawMetadata.sources).map(([k]) => (
<button
className={`border-b-2 border-transparent rounded-t text-sm px-2 py-1 bg-gray-200 text-gray-500 ${
selected === k ? "border-orange-300 font-bold" : ""
}`}
>
{k}
</button>
))}
{selected && (
<Highlight className="w-full h-full border focus:outline-none font-code text-base"> <Highlight className="w-full h-full border focus:outline-none font-code text-base">
{rawMetadata.sources[selected].content} {content}
</Highlight> </Highlight>
)}
</div>
)}
</div>
</ContentFrame>
); );
}; };

98
src/address/Contracts.tsx Normal file
View File

@ -0,0 +1,98 @@
import React, { useState, useEffect, useContext } from "react";
import { ethers } from "ethers";
import ContentFrame from "../ContentFrame";
import InfoRow from "../components/InfoRow";
import Contract from "./Contract";
import { sourcifyMetadata } from "../url";
import { RuntimeContext } from "../useRuntime";
type ContractsProps = {
checksummedAddress: string;
};
const Contracts: React.FC<ContractsProps> = ({ checksummedAddress }) => {
const { provider } = useContext(RuntimeContext);
const [rawMetadata, setRawMetadata] = useState<any>();
useEffect(() => {
if (!checksummedAddress) {
return;
}
const fetchMetadata = async () => {
try {
const result = await fetch(
sourcifyMetadata(checksummedAddress, provider!.network.chainId)
);
if (result.ok) {
const json = await result.json();
console.log(json);
setRawMetadata(json);
setSelected(Object.keys(json.sources)[0]);
} else {
setRawMetadata(null);
}
} catch (err) {
console.error(err);
setRawMetadata(null);
}
};
fetchMetadata();
}, [provider, checksummedAddress]);
const [selected, setSelected] = useState<string>();
const optimizer = rawMetadata?.settings?.optimizer;
return (
<ContentFrame tabs>
{rawMetadata && (
<>
<InfoRow title="Compiler">
<span>{rawMetadata.compiler?.version}</span>
</InfoRow>
<InfoRow title="Optimizer Enabled">
{optimizer?.enabled ? (
<span>
<span className="font-bold text-green-600">Yes</span> with{" "}
<span className="font-bold text-green-600">
{ethers.utils.commify(optimizer?.runs)}
</span>{" "}
runs
</span>
) : (
<span className="font-bold text-red-600">No</span>
)}
</InfoRow>
</>
)}
<div className="py-5">
{rawMetadata === null && (
<span>Couldn't find contract metadata in Sourcify repository.</span>
)}
{rawMetadata !== undefined && rawMetadata !== null && (
<div>
{Object.entries(rawMetadata.sources).map(([k]) => (
<button
className={`border-b-2 border-transparent rounded-t text-sm px-2 py-1 bg-gray-200 text-gray-500 ${
selected === k ? "border-orange-300 font-bold" : ""
}`}
>
{k}
</button>
))}
{selected && (
<Contract
checksummedAddress={checksummedAddress}
networkId={provider!.network.chainId}
filename={selected}
source={rawMetadata.sources[selected]}
/>
)}
</div>
)}
</div>
</ContentFrame>
);
};
export default React.memo(Contracts);

View File

@ -19,3 +19,10 @@ export const sourcifyMetadata = (
networkId: number networkId: number
) => ) =>
`http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`; `http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/metadata.json`;
export const sourcifySourceFile = (
checksummedAddress: string,
networkId: number,
filepath: string
) =>
`http://localhost:7000/sourcify/contracts/full_match/${networkId}/${checksummedAddress}/sources/${filepath}`;