Skip to content

Commit

Permalink
ERC1967 proxies (#105)
Browse files Browse the repository at this point in the history
* (feat) Remainder of ERC1967 support.

* (fix) Remove console.log()

* (fix) Types

* (fix) Formatting
  • Loading branch information
rrw-zilliqa authored Jan 29, 2025
1 parent 55c5a2f commit 57e0cd6
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ const AddressContract = lazy(
const AddressReadContract = lazy(
() => import("./execution/address/AddressReadContract"),
);
const AddressReadContractAsProxy = lazy(
() => import("./execution/address/AddressReadContractAsProxy"),
);
const AddressERC20Results = lazy(
() => import("./execution/address/AddressERC20Results"),
);
Expand Down Expand Up @@ -352,6 +355,10 @@ const router = createBrowserRouter(
loader={addressContractLoader}
/>
<Route path="readContract" element={<AddressReadContract />} />
<Route
path="readContractAs1967Proxy"
element={<AddressReadContractAsProxy />}
/>
<Route
path="proxyLogicContract"
element={<ProxyContract />}
Expand Down
16 changes: 16 additions & 0 deletions src/execution/AddressMainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import SourcifyLogo from "../sourcify/SourcifyLogo";
import { Match, useSourcifyMetadata } from "../sourcify/useSourcify";
import { useWhatsabiMetadata } from "../sourcify/useWhatsabi";
import { ChecksummedAddress } from "../types";
import { useERC1967ProxyAttributes } from "../useERC1967";
import { useHasCode } from "../useErigonHooks";
import { useAddressOrENS } from "../useResolvedAddresses";
import { RuntimeContext } from "../useRuntime";
Expand Down Expand Up @@ -96,6 +97,11 @@ const AddressMainPage: React.FC = () => {
config.assetsURLPrefix,
);

const eip1967ProxyAttrs = useERC1967ProxyAttributes(
provider,
checksummedAddress,
);

return (
<StandardFrame>
{error ? (
Expand Down Expand Up @@ -170,6 +176,16 @@ const AddressMainPage: React.FC = () => {
</span>
</NavTab>
)}
{(match || whatsabiMatch) &&
eip1967ProxyAttrs?.delegate && (
<NavTab
href={`/address/${addressOrName}/readContractAs1967Proxy`}
>
<span className={`flex items-baseline space-x-2`}>
<span>Read As Proxy</span>
</span>
</NavTab>
)}
</>
)}
{config?.experimental && (
Expand Down
57 changes: 57 additions & 0 deletions src/execution/address/AddressReadContractAsProxy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { FC, useContext } from "react";
import { NavLink, useOutletContext } from "react-router-dom";
import ContentFrame from "../../components/ContentFrame";
import { useSourcifyMetadata } from "../../sourcify/useSourcify";
import { useWhatsabiMetadata } from "../../sourcify/useWhatsabi";
import { addressURL } from "../../url.ts";
import { useERC1967ProxyAttributes } from "../../useERC1967";
import { RuntimeContext } from "../../useRuntime";
import { type AddressOutletContext } from "../AddressMainPage";
import ReadContract from "./contract/ReadContract";

const AddressReadContractAsProxy: FC = () => {
const { address } = useOutletContext() as AddressOutletContext;
const { config, provider } = useContext(RuntimeContext);
const proxyAttributes = useERC1967ProxyAttributes(provider, address);
const match = useSourcifyMetadata(
proxyAttributes?.delegate,
provider._network.chainId,
);
const whatsabiMatch = useWhatsabiMetadata(
match === null ? proxyAttributes?.delegate : undefined,
provider._network.chainId,
provider,
config.assetsURLPrefix,
);

return (
<ContentFrame tabs>
<div>
<p className="py-6">
{" "}
Reading ERC-1967 proxy
<NavLink
className="rounded-lg bg-link-blue/10 px-2 py-1 text-xs text-link-blue hover:bg-link-blue/100 hover:text-white"
to={addressURL(address)}
>
{address}
</NavLink>
whose implementation is at
<NavLink
className="rounded-lg bg-link-blue/10 px-2 py-1 text-xs text-link-blue hover:bg-link-blue/100 hover:text-white"
to={addressURL(proxyAttributes!.delegate)}
>
{proxyAttributes?.delegate}
</NavLink>
.
</p>
</div>
<ReadContract
checksummedAddress={address}
match={match ?? whatsabiMatch}
/>
</ContentFrame>
);
};

export default AddressReadContractAsProxy;
3 changes: 3 additions & 0 deletions src/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ export const transactionURL = (txHash: string) => `/tx/${txHash}`;
export const addressByNonceURL = (address: ChecksummedAddress, nonce: bigint) =>
`/address/${address}?nonce=${nonce}`;

export const addressURL = (address: ChecksummedAddress) =>
`/address/${address}`;

export const openInRemixURL = (checksummedAddress: string, networkId: bigint) =>
`https://remix.ethereum.org/#activate=sourcify&call=sourcify//fetchAndSave//${checksummedAddress}//${networkId}`;
65 changes: 65 additions & 0 deletions src/useERC1967.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useQuery } from "@tanstack/react-query";
import { JsonRpcApiProvider, getAddress } from "ethers";

type Address = string;

const DELEGATE_STORAGE_LOCATION =
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
const BEACON_STORAGE_LOCATION =
"0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50";
const ADMIN_STORAGE_LOCATION =
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103";

export type ERC1967ProxyAttributes = {
delegate: Address;
beacon: Address | undefined;
admin: Address | undefined;
};

export const GetStorageQuery = (
provider: JsonRpcApiProvider,
address: Address,
slot: string,
) => {
return {
queryKey: ["getStorageAt", address, slot],
queryFn: () => {
return provider.getStorage(address, slot);
},
};
};

export const useERC1967ProxyAttributes = (
provider: JsonRpcApiProvider,
address: Address | undefined,
): ERC1967ProxyAttributes | undefined => {
function toAddress(s: string | undefined): string | undefined {
if (s === undefined || s === "0x") {
return undefined;
}
const addr = getAddress("0x" + s.toLowerCase().slice(-40));
return addr;
}

if (address === undefined) {
return undefined;
}
const { data: delegateData } = useQuery(
GetStorageQuery(provider, address, DELEGATE_STORAGE_LOCATION),
);
const { data: beaconData } = useQuery(
GetStorageQuery(provider, address, BEACON_STORAGE_LOCATION),
);
const { data: adminData } = useQuery(
GetStorageQuery(provider, address, ADMIN_STORAGE_LOCATION),
);

const delegate = toAddress(delegateData);
const beacon = toAddress(beaconData);
const admin = toAddress(adminData);

if (delegate === undefined) {
return undefined;
}
return { delegate, beacon, admin };
};

0 comments on commit 57e0cd6

Please sign in to comment.