Merge branch 'feature/sourcify-partial-matches' into develop; fix #344
This commit is contained in:
commit
88c0b9f516
|
@ -55,7 +55,7 @@ const AddressMainPage: React.FC<AddressMainPageProps> = ({}) => {
|
||||||
|
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
const hasCode = useHasCode(provider, checksummedAddress, "latest");
|
const hasCode = useHasCode(provider, checksummedAddress, "latest");
|
||||||
const addressMetadata = useSourcifyMetadata(
|
const match = useSourcifyMetadata(
|
||||||
hasCode ? checksummedAddress : undefined,
|
hasCode ? checksummedAddress : undefined,
|
||||||
provider?.network.chainId
|
provider?.network.chainId
|
||||||
);
|
);
|
||||||
|
@ -110,18 +110,18 @@ const AddressMainPage: React.FC<AddressMainPageProps> = ({}) => {
|
||||||
<NavTab href={`/address/${addressOrName}/contract`}>
|
<NavTab href={`/address/${addressOrName}/contract`}>
|
||||||
<span
|
<span
|
||||||
className={`flex items-baseline space-x-2 ${
|
className={`flex items-baseline space-x-2 ${
|
||||||
addressMetadata === undefined ? "italic opacity-50" : ""
|
match === undefined ? "italic opacity-50" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span>Contract</span>
|
<span>Contract</span>
|
||||||
{addressMetadata === undefined ? (
|
{match === undefined ? (
|
||||||
<span className="self-center">
|
<span className="self-center">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
className="animate-spin"
|
className="animate-spin"
|
||||||
icon={faCircleNotch}
|
icon={faCircleNotch}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
) : addressMetadata === null ? (
|
) : match === null ? (
|
||||||
<span className="self-center text-red-500">
|
<span className="self-center text-red-500">
|
||||||
<FontAwesomeIcon icon={faQuestionCircle} />
|
<FontAwesomeIcon icon={faQuestionCircle} />
|
||||||
</span>
|
</span>
|
||||||
|
@ -153,7 +153,7 @@ const AddressMainPage: React.FC<AddressMainPageProps> = ({}) => {
|
||||||
element={
|
element={
|
||||||
<Contracts
|
<Contracts
|
||||||
checksummedAddress={checksummedAddress}
|
checksummedAddress={checksummedAddress}
|
||||||
rawMetadata={addressMetadata}
|
match={match}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useMemo, useState } from "react";
|
||||||
import { Outlet } from "react-router-dom";
|
import { Outlet } from "react-router-dom";
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
import { AppConfig, AppConfigContext } from "./useAppConfig";
|
import { AppConfig, AppConfigContext } from "./useAppConfig";
|
||||||
import { SourcifySource } from "./url";
|
import { SourcifySource } from "./sourcify/useSourcify";
|
||||||
|
|
||||||
const Main: React.FC = () => {
|
const Main: React.FC = () => {
|
||||||
const [sourcifySource, setSourcifySource] = useState<SourcifySource>(
|
const [sourcifySource, setSourcifySource] = useState<SourcifySource>(
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React, { PropsWithChildren } from "react";
|
||||||
import { Menu } from "@headlessui/react";
|
import { Menu } from "@headlessui/react";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { faBars } from "@fortawesome/free-solid-svg-icons/faBars";
|
import { faBars } from "@fortawesome/free-solid-svg-icons/faBars";
|
||||||
import { SourcifySource } from "./url";
|
|
||||||
import { useAppConfigContext } from "./useAppConfig";
|
import { useAppConfigContext } from "./useAppConfig";
|
||||||
|
import { SourcifySource } from "./sourcify/useSourcify";
|
||||||
|
|
||||||
const SourcifyMenu: React.FC = () => {
|
const SourcifyMenu: React.FC = () => {
|
||||||
const { sourcifySource, setSourcifySource } = useAppConfigContext();
|
const { sourcifySource, setSourcifySource } = useAppConfigContext();
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { SyntaxHighlighter, docco } from "../highlight-init";
|
import { SyntaxHighlighter, docco } from "../highlight-init";
|
||||||
import { useContract } from "../sourcify/useSourcify";
|
import { MatchType, useContract } from "../sourcify/useSourcify";
|
||||||
import { useAppConfigContext } from "../useAppConfig";
|
import { useAppConfigContext } from "../useAppConfig";
|
||||||
|
|
||||||
type ContractFromRepoProps = {
|
type ContractFromRepoProps = {
|
||||||
checksummedAddress: string;
|
checksummedAddress: string;
|
||||||
networkId: number;
|
networkId: number;
|
||||||
filename: string;
|
filename: string;
|
||||||
|
type: MatchType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ContractFromRepo: React.FC<ContractFromRepoProps> = ({
|
const ContractFromRepo: React.FC<ContractFromRepoProps> = ({
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
networkId,
|
networkId,
|
||||||
filename,
|
filename,
|
||||||
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
const { sourcifySource } = useAppConfigContext();
|
const { sourcifySource } = useAppConfigContext();
|
||||||
const content = useContract(
|
const content = useContract(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
networkId,
|
networkId,
|
||||||
filename,
|
filename,
|
||||||
sourcifySource
|
sourcifySource,
|
||||||
|
type
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -8,39 +8,39 @@ import InfoRow from "../components/InfoRow";
|
||||||
import Contract from "./Contract";
|
import Contract from "./Contract";
|
||||||
import ContractFromRepo from "./ContractFromRepo";
|
import ContractFromRepo from "./ContractFromRepo";
|
||||||
import { RuntimeContext } from "../useRuntime";
|
import { RuntimeContext } from "../useRuntime";
|
||||||
import { Metadata } from "../sourcify/useSourcify";
|
import { Match, MatchType } from "../sourcify/useSourcify";
|
||||||
import ExternalLink from "../components/ExternalLink";
|
import ExternalLink from "../components/ExternalLink";
|
||||||
import { openInRemixURL } from "../url";
|
import { openInRemixURL } from "../url";
|
||||||
import ContractABI from "./ContractABI";
|
import ContractABI from "./ContractABI";
|
||||||
|
|
||||||
type ContractsProps = {
|
type ContractsProps = {
|
||||||
checksummedAddress: string;
|
checksummedAddress: string;
|
||||||
rawMetadata: Metadata | null | undefined;
|
match: Match | null | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Contracts: React.FC<ContractsProps> = ({
|
const Contracts: React.FC<ContractsProps> = ({ checksummedAddress, match }) => {
|
||||||
checksummedAddress,
|
|
||||||
rawMetadata,
|
|
||||||
}) => {
|
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
|
|
||||||
const [selected, setSelected] = useState<string>();
|
const [selected, setSelected] = useState<string>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rawMetadata) {
|
if (match) {
|
||||||
setSelected(Object.keys(rawMetadata.sources)[0]);
|
setSelected(Object.keys(match.metadata.sources)[0]);
|
||||||
}
|
}
|
||||||
}, [rawMetadata]);
|
}, [match]);
|
||||||
const optimizer = rawMetadata?.settings?.optimizer;
|
const optimizer = match?.metadata.settings?.optimizer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContentFrame tabs>
|
<ContentFrame tabs>
|
||||||
{rawMetadata && (
|
{match && (
|
||||||
<>
|
<>
|
||||||
|
<InfoRow title="Match">
|
||||||
|
{match.type === MatchType.FULL_MATCH ? "Full" : "Partial"}
|
||||||
|
</InfoRow>
|
||||||
<InfoRow title="Language">
|
<InfoRow title="Language">
|
||||||
<span>{rawMetadata.language}</span>
|
<span>{match.metadata.language}</span>
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
<InfoRow title="Compiler">
|
<InfoRow title="Compiler">
|
||||||
<span>{rawMetadata.compiler.version}</span>
|
<span>{match.metadata.compiler.version}</span>
|
||||||
</InfoRow>
|
</InfoRow>
|
||||||
<InfoRow title="Optimizer Enabled">
|
<InfoRow title="Optimizer Enabled">
|
||||||
{optimizer?.enabled ? (
|
{optimizer?.enabled ? (
|
||||||
|
@ -58,19 +58,19 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<div className="py-5">
|
<div className="py-5">
|
||||||
{rawMetadata === undefined && (
|
{match === undefined && (
|
||||||
<span>Getting data from Sourcify repository...</span>
|
<span>Getting data from Sourcify repository...</span>
|
||||||
)}
|
)}
|
||||||
{rawMetadata === null && (
|
{match === 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
|
||||||
Sourcify repository.
|
Sourcify repository.
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{rawMetadata !== undefined && rawMetadata !== null && (
|
{match !== undefined && match !== null && (
|
||||||
<>
|
<>
|
||||||
{rawMetadata.output.abi && (
|
{match.metadata.output.abi && (
|
||||||
<ContractABI abi={rawMetadata.output.abi} />
|
<ContractABI abi={match.metadata.output.abi} />
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<Menu>
|
<Menu>
|
||||||
|
@ -96,7 +96,7 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
</div>
|
</div>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Menu.Items className="absolute border p-1 rounded-b bg-white flex flex-col">
|
<Menu.Items className="absolute border p-1 rounded-b bg-white flex flex-col">
|
||||||
{Object.entries(rawMetadata.sources).map(([k]) => (
|
{Object.entries(match.metadata.sources).map(([k]) => (
|
||||||
<Menu.Item key={k}>
|
<Menu.Item key={k}>
|
||||||
<button
|
<button
|
||||||
className={`flex text-sm px-2 py-1 ${
|
className={`flex text-sm px-2 py-1 ${
|
||||||
|
@ -115,13 +115,16 @@ const Contracts: React.FC<ContractsProps> = ({
|
||||||
</Menu>
|
</Menu>
|
||||||
{selected && (
|
{selected && (
|
||||||
<>
|
<>
|
||||||
{rawMetadata.sources[selected].content ? (
|
{match.metadata.sources[selected].content ? (
|
||||||
<Contract content={rawMetadata.sources[selected].content} />
|
<Contract
|
||||||
|
content={match.metadata.sources[selected].content}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ContractFromRepo
|
<ContractFromRepo
|
||||||
checksummedAddress={checksummedAddress}
|
checksummedAddress={checksummedAddress}
|
||||||
networkId={provider!.network.chainId}
|
networkId={provider!.network.chainId}
|
||||||
filename={selected}
|
filename={selected}
|
||||||
|
type={match.type}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -38,7 +38,7 @@ const DecoratedAddressLink: React.FC<DecoratedAddressLinkProps> = ({
|
||||||
eoa,
|
eoa,
|
||||||
}) => {
|
}) => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
const metadata = useSourcifyMetadata(address, provider?.network.chainId);
|
const match = useSourcifyMetadata(address, provider?.network.chainId);
|
||||||
|
|
||||||
const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS;
|
const mint = addressCtx === AddressContext.FROM && address === ZERO_ADDRESS;
|
||||||
const burn = addressCtx === AddressContext.TO && address === ZERO_ADDRESS;
|
const burn = addressCtx === AddressContext.TO && address === ZERO_ADDRESS;
|
||||||
|
@ -78,7 +78,7 @@ const DecoratedAddressLink: React.FC<DecoratedAddressLinkProps> = ({
|
||||||
<FontAwesomeIcon icon={faCoins} size="1x" />
|
<FontAwesomeIcon icon={faCoins} size="1x" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{metadata && (
|
{match && (
|
||||||
<NavLink
|
<NavLink
|
||||||
className="self-center shrink-0 flex items-center"
|
className="self-center shrink-0 flex items-center"
|
||||||
to={`/address/${address}/contract`}
|
to={`/address/${address}/contract`}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { Interface } from "@ethersproject/abi";
|
||||||
import { ErrorDescription } from "@ethersproject/abi/lib/interface";
|
import { ErrorDescription } from "@ethersproject/abi/lib/interface";
|
||||||
import useSWRImmutable from "swr/immutable";
|
import useSWRImmutable from "swr/immutable";
|
||||||
import { ChecksummedAddress, TransactionData } from "../types";
|
import { ChecksummedAddress, TransactionData } from "../types";
|
||||||
import { sourcifyMetadata, SourcifySource, sourcifySourceFile } from "../url";
|
|
||||||
import { useAppConfigContext } from "../useAppConfig";
|
import { useAppConfigContext } from "../useAppConfig";
|
||||||
|
|
||||||
export type UserMethod = {
|
export type UserMethod = {
|
||||||
|
@ -82,16 +81,111 @@ export type Metadata = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const sourcifyFetcher = async (url: string) => {
|
export enum SourcifySource {
|
||||||
|
// Resolve trusted IPNS for root IPFS
|
||||||
|
IPFS_IPNS,
|
||||||
|
|
||||||
|
// Centralized Sourcify servers
|
||||||
|
CENTRAL_SERVER,
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourcifyIPNS =
|
||||||
|
"k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p";
|
||||||
|
const defaultIpfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyIPNS}`;
|
||||||
|
const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`;
|
||||||
|
|
||||||
|
const resolveSourcifySource = (source: SourcifySource) => {
|
||||||
|
if (source === SourcifySource.IPFS_IPNS) {
|
||||||
|
return defaultIpfsGatewayPrefix;
|
||||||
|
}
|
||||||
|
if (source === SourcifySource.CENTRAL_SERVER) {
|
||||||
|
return sourcifyHttpRepoPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown Sourcify integration source code: ${source}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a complete Sourcify metadata.json URL given the contract address
|
||||||
|
* and chain.
|
||||||
|
*/
|
||||||
|
export const sourcifyMetadata = (
|
||||||
|
address: ChecksummedAddress,
|
||||||
|
chainId: number,
|
||||||
|
source: SourcifySource,
|
||||||
|
type: MatchType
|
||||||
|
) =>
|
||||||
|
`${resolveSourcifySource(source)}/contracts/${
|
||||||
|
type === MatchType.FULL_MATCH ? "full_match" : "partial_match"
|
||||||
|
}/${chainId}/${address}/metadata.json`;
|
||||||
|
|
||||||
|
export const sourcifySourceFile = (
|
||||||
|
address: ChecksummedAddress,
|
||||||
|
chainId: number,
|
||||||
|
filepath: string,
|
||||||
|
source: SourcifySource,
|
||||||
|
type: MatchType
|
||||||
|
) =>
|
||||||
|
`${resolveSourcifySource(source)}/contracts/${
|
||||||
|
type === MatchType.FULL_MATCH ? "full_match" : "partial_match"
|
||||||
|
}/${chainId}/${address}/sources/${filepath}`;
|
||||||
|
|
||||||
|
export enum MatchType {
|
||||||
|
FULL_MATCH,
|
||||||
|
PARTIAL_MATCH,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Match = {
|
||||||
|
type: MatchType;
|
||||||
|
metadata: Metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sourcifyFetcher = async (
|
||||||
|
_: "sourcify",
|
||||||
|
address: ChecksummedAddress,
|
||||||
|
chainId: number,
|
||||||
|
sourcifySource: SourcifySource
|
||||||
|
): Promise<Match | null | undefined> => {
|
||||||
|
// Try full match
|
||||||
try {
|
try {
|
||||||
|
const url = sourcifyMetadata(
|
||||||
|
address,
|
||||||
|
chainId,
|
||||||
|
sourcifySource,
|
||||||
|
MatchType.FULL_MATCH
|
||||||
|
);
|
||||||
const res = await fetch(url);
|
const res = await fetch(url);
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
return res.json();
|
return {
|
||||||
|
type: MatchType.FULL_MATCH,
|
||||||
|
metadata: await res.json(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.info(
|
||||||
|
`error while getting Sourcify full_match metadata: chainId=${chainId} address=${address} err=${err}; falling back to partial_match`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to try partial match
|
||||||
|
try {
|
||||||
|
const url = sourcifyMetadata(
|
||||||
|
address,
|
||||||
|
chainId,
|
||||||
|
sourcifySource,
|
||||||
|
MatchType.PARTIAL_MATCH
|
||||||
|
);
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (res.ok) {
|
||||||
|
return {
|
||||||
|
type: MatchType.PARTIAL_MATCH,
|
||||||
|
metadata: await res.json(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`error while getting Sourcify metadata: url=${url} err=${err}`
|
`error while getting Sourcify partial_match metadata: chainId=${chainId} address=${address} err=${err}`
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -100,13 +194,13 @@ const sourcifyFetcher = async (url: string) => {
|
||||||
export const useSourcifyMetadata = (
|
export const useSourcifyMetadata = (
|
||||||
address: ChecksummedAddress | undefined,
|
address: ChecksummedAddress | undefined,
|
||||||
chainId: number | undefined
|
chainId: number | undefined
|
||||||
): Metadata | null | undefined => {
|
): Match | null | undefined => {
|
||||||
const { sourcifySource } = useAppConfigContext();
|
const { sourcifySource } = useAppConfigContext();
|
||||||
const metadataURL = () =>
|
const metadataURL = () =>
|
||||||
address === undefined || chainId === undefined
|
address === undefined || chainId === undefined
|
||||||
? null
|
? null
|
||||||
: sourcifyMetadata(address, chainId, sourcifySource);
|
: ["sourcify", address, chainId, sourcifySource];
|
||||||
const { data, error } = useSWRImmutable<Metadata>(
|
const { data, error } = useSWRImmutable<Match | null | undefined>(
|
||||||
metadataURL,
|
metadataURL,
|
||||||
sourcifyFetcher
|
sourcifyFetcher
|
||||||
);
|
);
|
||||||
|
@ -128,14 +222,16 @@ export const useContract = (
|
||||||
checksummedAddress: string,
|
checksummedAddress: string,
|
||||||
networkId: number,
|
networkId: number,
|
||||||
filename: string,
|
filename: string,
|
||||||
sourcifySource: SourcifySource
|
sourcifySource: SourcifySource,
|
||||||
|
type: MatchType
|
||||||
) => {
|
) => {
|
||||||
const normalizedFilename = filename.replaceAll(/[@:]/g, "_");
|
const normalizedFilename = filename.replaceAll(/[@:]/g, "_");
|
||||||
const url = sourcifySourceFile(
|
const url = sourcifySourceFile(
|
||||||
checksummedAddress,
|
checksummedAddress,
|
||||||
networkId,
|
networkId,
|
||||||
normalizedFilename,
|
normalizedFilename,
|
||||||
sourcifySource
|
sourcifySource,
|
||||||
|
type
|
||||||
);
|
);
|
||||||
|
|
||||||
const { data, error } = useSWRImmutable(url, contractFetcher);
|
const { data, error } = useSWRImmutable(url, contractFetcher);
|
||||||
|
|
|
@ -80,7 +80,8 @@ const Details: React.FC<DetailsProps> = ({ txData }) => {
|
||||||
|
|
||||||
const tokenTransfers = useTokenTransfers(txData);
|
const tokenTransfers = useTokenTransfers(txData);
|
||||||
|
|
||||||
const metadata = useSourcifyMetadata(txData?.to, provider?.network.chainId);
|
const match = useSourcifyMetadata(txData?.to, provider?.network.chainId);
|
||||||
|
const metadata = match?.metadata;
|
||||||
|
|
||||||
const txDesc = useSourcifyTransactionDescription(metadata, txData);
|
const txDesc = useSourcifyTransactionDescription(metadata, txData);
|
||||||
const userDoc = metadata?.output.userdoc;
|
const userDoc = metadata?.output.userdoc;
|
||||||
|
|
|
@ -17,14 +17,14 @@ type LogEntryProps = {
|
||||||
|
|
||||||
const LogEntry: React.FC<LogEntryProps> = ({ log }) => {
|
const LogEntry: React.FC<LogEntryProps> = ({ log }) => {
|
||||||
const { provider } = useContext(RuntimeContext);
|
const { provider } = useContext(RuntimeContext);
|
||||||
const metadata = useSourcifyMetadata(log.address, provider?.network.chainId);
|
const match = useSourcifyMetadata(log.address, provider?.network.chainId);
|
||||||
|
|
||||||
const logDesc = useMemo(() => {
|
const logDesc = useMemo(() => {
|
||||||
if (!metadata) {
|
if (!match) {
|
||||||
return metadata;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
const abi = metadata.output.abi;
|
const abi = match.metadata.output.abi;
|
||||||
const intf = new Interface(abi as any);
|
const intf = new Interface(abi as any);
|
||||||
try {
|
try {
|
||||||
return intf.parseLog({
|
return intf.parseLog({
|
||||||
|
@ -35,7 +35,7 @@ const LogEntry: React.FC<LogEntryProps> = ({ log }) => {
|
||||||
console.warn("Couldn't find function signature", err);
|
console.warn("Couldn't find function signature", err);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [log, metadata]);
|
}, [log, match]);
|
||||||
|
|
||||||
const rawTopic0 = log.topics[0];
|
const rawTopic0 = log.topics[0];
|
||||||
const topic0 = useTopic0(rawTopic0);
|
const topic0 = useTopic0(rawTopic0);
|
||||||
|
|
47
src/url.ts
47
src/url.ts
|
@ -29,52 +29,5 @@ export const transactionURL = (txHash: string) => `/tx/${txHash}`;
|
||||||
export const addressByNonceURL = (address: ChecksummedAddress, nonce: number) =>
|
export const addressByNonceURL = (address: ChecksummedAddress, nonce: number) =>
|
||||||
`/address/${address}?nonce=${nonce}`;
|
`/address/${address}?nonce=${nonce}`;
|
||||||
|
|
||||||
export enum SourcifySource {
|
|
||||||
// Resolve trusted IPNS for root IPFS
|
|
||||||
IPFS_IPNS,
|
|
||||||
|
|
||||||
// Centralized Sourcify servers
|
|
||||||
CENTRAL_SERVER,
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourcifyIPNS =
|
|
||||||
"k51qzi5uqu5dll0ocge71eudqnrgnogmbr37gsgl12uubsinphjoknl6bbi41p";
|
|
||||||
const defaultIpfsGatewayPrefix = `https://ipfs.io/ipns/${sourcifyIPNS}`;
|
|
||||||
const sourcifyHttpRepoPrefix = `https://repo.sourcify.dev`;
|
|
||||||
|
|
||||||
const resolveSourcifySource = (source: SourcifySource) => {
|
|
||||||
if (source === SourcifySource.IPFS_IPNS) {
|
|
||||||
return defaultIpfsGatewayPrefix;
|
|
||||||
}
|
|
||||||
if (source === SourcifySource.CENTRAL_SERVER) {
|
|
||||||
return sourcifyHttpRepoPrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`Unknown Sourcify integration source code: ${source}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a complete Sourcify metadata.json URL given the contract address
|
|
||||||
* and chain.
|
|
||||||
*/
|
|
||||||
export const sourcifyMetadata = (
|
|
||||||
address: ChecksummedAddress,
|
|
||||||
chainId: number,
|
|
||||||
source: SourcifySource
|
|
||||||
) =>
|
|
||||||
`${resolveSourcifySource(
|
|
||||||
source
|
|
||||||
)}/contracts/full_match/${chainId}/${address}/metadata.json`;
|
|
||||||
|
|
||||||
export const sourcifySourceFile = (
|
|
||||||
address: ChecksummedAddress,
|
|
||||||
chainId: number,
|
|
||||||
filepath: string,
|
|
||||||
source: SourcifySource
|
|
||||||
) =>
|
|
||||||
`${resolveSourcifySource(
|
|
||||||
source
|
|
||||||
)}/contracts/full_match/${chainId}/${address}/sources/${filepath}`;
|
|
||||||
|
|
||||||
export const openInRemixURL = (checksummedAddress: string, networkId: number) =>
|
export const openInRemixURL = (checksummedAddress: string, networkId: number) =>
|
||||||
`https://remix.ethereum.org/#activate=sourcify&call=sourcify//fetchAndSave//${checksummedAddress}//${networkId}`;
|
`https://remix.ethereum.org/#activate=sourcify&call=sourcify//fetchAndSave//${checksummedAddress}//${networkId}`;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import { SourcifySource } from "./url";
|
import { SourcifySource } from "./sourcify/useSourcify";
|
||||||
|
|
||||||
export type AppConfig = {
|
export type AppConfig = {
|
||||||
sourcifySource: SourcifySource;
|
sourcifySource: SourcifySource;
|
||||||
|
|
Loading…
Reference in New Issue