Skip to content

Commit

Permalink
Display Scilla contract source (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHinshelwood authored and rrw-zilliqa committed Dec 18, 2023
1 parent 8dd4422 commit 535b949
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 76 deletions.
176 changes: 100 additions & 76 deletions src/execution/address/Contracts.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import React, { useState, useEffect, useContext, useMemo } from "react";
import { commify } from "@ethersproject/units";
import { Menu } from "@headlessui/react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faChevronDown } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Menu } from "@headlessui/react";
Expand All @@ -14,6 +18,8 @@ import ContractFromRepo from "./ContractFromRepo";
import ContractABI from "./contract/ContractABI";
import { useGetCode } from "../../useErigonHooks";
import StandardTextarea from "../../components/StandardTextarea";
import { toUtf8String } from "ethers/lib/utils";
import ScillaContract from "./ScillaContract";

type ContractsProps = {
checksummedAddress: string;
Expand All @@ -23,6 +29,19 @@ type ContractsProps = {
const Contracts: React.FC<ContractsProps> = ({ checksummedAddress, match }) => {
const { provider } = useContext(RuntimeContext);
const code = useGetCode(provider, checksummedAddress);
const scillaCode = useMemo(() => {
try {
if (code) {
let s = toUtf8String(code);
if (s.startsWith("scilla_version")) {
return s;
}
}
} catch (err) {
// Silently ignore on purpose
return undefined;
}
}, [code]);

const [selected, setSelected] = useState<string>();
useEffect(() => {
Expand Down Expand Up @@ -70,85 +89,90 @@ const Contracts: React.FC<ContractsProps> = ({ checksummedAddress, match }) => {
</InfoRow>
</>
)}
<div className="py-5">
{match === undefined && (
<span>Getting data from Sourcify repository...</span>
)}
{match === null && (
<span>
Address is not a contract or couldn't find contract metadata in
Sourcify repository.
</span>
)}
{match !== undefined && match !== null && (
<>
{match.metadata.output.abi && (
<ContractABI abi={match.metadata.output.abi} />
)}
<div>
<Menu>
<div className="flex items-baseline justify-between space-x-2">
<Menu.Button className="flex space-x-2 rounded-t border-l border-r border-t px-2 py-1 text-sm">
<span>{selected}</span>
<span className="self-center">
<FontAwesomeIcon icon={faChevronDown} size="xs" />
</span>
</Menu.Button>
{provider && (
<div className="text-sm">
<ExternalLink
href={openInRemixURL(
checksummedAddress,
provider._network.chainId,
)}
>
Open in Remix
</ExternalLink>
</div>
)}
</div>
<div className="relative">
<Menu.Items className="absolute z-10 flex flex-col rounded-b border bg-white p-1">
{Object.entries(match.metadata.sources).map(([k]) => (
<Menu.Item key={k}>
<button
className={`flex px-2 py-1 text-sm ${
selected === k
? "bg-gray-200 font-bold text-gray-500"
: "text-gray-400 transition-colors duration-75 hover:text-gray-500"
}`}
onClick={() => setSelected(k)}
>
{k}
</button>
</Menu.Item>
))}
</Menu.Items>
</div>
</Menu>
{selected && (
<>
{match.metadata.sources[selected].content ? (
<Contract
content={match.metadata.sources[selected].content}
/>
) : (
<ContractFromRepo
checksummedAddress={checksummedAddress}
networkId={provider!._network.chainId}
filename={selected}
type={match.type}
/>
)}
</>
{!scillaCode && (
<div className="py-5">
{match === undefined && (
<span>Getting data from Sourcify repository...</span>
)}
{match === null && (
<span>
Address is not a contract or couldn't find contract metadata in
Sourcify repository.
</span>
)}
{match !== undefined && match !== null && (
<>
{match.metadata.output.abi && (
<ContractABI abi={match.metadata.output.abi} />
)}
</div>
</>
)}
</div>
<div>
<Menu>
<div className="flex items-baseline justify-between space-x-2">
<Menu.Button className="flex space-x-2 rounded-t border-l border-r border-t px-2 py-1 text-sm">
<span>{selected}</span>
<span className="self-center">
<FontAwesomeIcon icon={faChevronDown} size="xs" />
</span>
</Menu.Button>
{provider && (
<div className="text-sm">
<ExternalLink
href={openInRemixURL(
checksummedAddress,
provider.network.chainId
)}
>
Open in Remix
</ExternalLink>
</div>
)}
</div>
<div className="relative">
<Menu.Items className="absolute flex flex-col rounded-b border bg-white p-1">
{Object.entries(match.metadata.sources).map(([k]) => (
<Menu.Item key={k}>
<button
className={`flex px-2 py-1 text-sm ${
selected === k
? "bg-gray-200 font-bold text-gray-500"
: "text-gray-400 transition-colors duration-75 hover:text-gray-500"
}`}
onClick={() => setSelected(k)}
>
{k}
</button>
</Menu.Item>
))}
</Menu.Items>
</div>
</Menu>
{selected && (
<>
{match.metadata.sources[selected].content ? (
<Contract
content={match.metadata.sources[selected].content}
/>
) : (
<ContractFromRepo
checksummedAddress={checksummedAddress}
networkId={provider!.network.chainId}
filename={selected}
type={match.type}
/>
)}
</>
)}
</div>
</>
)}
</div>
)}
<div className="py-5">
{code === undefined && <span>Getting contract bytecode...</span>}
{code && (
{scillaCode && (
<ScillaContract content={scillaCode} />
)}
{!scillaCode && code && (
<>
<div className="pb-2">Contract Bytecode</div>
<StandardTextarea value={code} />
Expand Down
19 changes: 19 additions & 0 deletions src/execution/address/ScillaContract.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import { SyntaxHighlighter, docco } from "../../highlight-init";

type ContractProps = {
content: any;
};

const ScillaContract: React.FC<ContractProps> = ({ content }) => (
<SyntaxHighlighter
className="h-full w-full border font-code text-base"
language="scilla"
style={docco}
showLineNumbers
>
{content ?? ""}
</SyntaxHighlighter>
);

export default React.memo(ScillaContract);

0 comments on commit 535b949

Please sign in to comment.