From fe50d84996f25c84709359770e8d237b67db8642 Mon Sep 17 00:00:00 2001 From: Stanley Date: Thu, 7 Nov 2024 20:07:42 -0500 Subject: [PATCH 01/37] UI for crosschain modules --- .../_utils/getContractPageSidebarLinks.ts | 6 + .../cross-chain/data-table.tsx | 256 ++++++++++++++++++ .../[contractAddress]/cross-chain/page.tsx | 144 ++++++++++ .../contract-deploy-form/custom-contract.tsx | 17 +- ...ular-contract-default-modules-fieldset.tsx | 4 + apps/dashboard/src/data/explore.ts | 23 ++ 6 files changed, 445 insertions(+), 5 deletions(-) create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx create mode 100644 apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts index 474b31d404b..2281ed405a4 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts @@ -24,6 +24,12 @@ export function getContractPageSidebarLinks(data: { hide: !data.metadata.isModularCore, exactMatch: true, }, + { + label: "Cross Chain", + href: `${layoutPrefix}/cross-chain`, + hide: !data.metadata.isModularCore, + exactMatch: true, + }, { label: "Code Snippets", href: `${layoutPrefix}/code`, diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx new file mode 100644 index 00000000000..6258913067c --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -0,0 +1,256 @@ +"use client"; + +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { getThirdwebClient } from "@/constants/thirdweb.server"; +import { + type ColumnDef, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { verifyContract } from "app/(dashboard)/(chain)/[chain_id]/[contractAddress]/sources/ContractSourcesPage"; +import { + type DeployModalStep, + DeployStatusModal, + useDeployStatusModal, +} from "components/contract-components/contract-deploy-form/deploy-context-modal"; +import { + getModuleInstallParams, + showPrimarySaleFiedset, + showRoyaltyFieldset, + showSuperchainBridgeFieldset, +} from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; +import { useTxNotifications } from "hooks/useTxNotifications"; +import { replaceTemplateValues } from "lib/deployment/template-values"; +import { ZERO_ADDRESS, defineChain } from "thirdweb"; +import type { FetchDeployMetadataResult } from "thirdweb/contract"; +import { deployContractfromDeployMetadata } from "thirdweb/deploys"; +import { useActiveAccount, useSwitchActiveWalletChain } from "thirdweb/react"; + +export type CrossChain = { + id: number; + network: string; + chainId: number; + status: "DEPLOYED" | "NOT_DEPLOYED"; +}; + +export function DataTable({ + data, + coreMetadata, + modulesMetadata, + erc20InitialData: [_name, _symbol, _contractURI, _owner], +}: { + data: CrossChain[]; + coreMetadata: FetchDeployMetadataResult; + modulesMetadata: FetchDeployMetadataResult[]; + erc20InitialData: string[]; +}) { + const activeAccount = useActiveAccount(); + const switchChain = useSwitchActiveWalletChain(); + const deployStatusModal = useDeployStatusModal(); + const { onError } = useTxNotifications( + "Successfully deployed contract", + "Failed to deploy contract", + ); + + const columns: ColumnDef[] = [ + { + accessorKey: "network", + header: "Network", + }, + { + accessorKey: "chainId", + header: "Chain ID", + }, + { + accessorKey: "status", + header: "Status", + cell: ({ row }) => { + if (row.getValue("status") === "DEPLOYED") { + return Deployed; + } + return ( + + ); + }, + }, + ]; + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const deployContract = async (chainId: number) => { + try { + if (!activeAccount) { + throw new Error("No active account"); + } + + const coreInitializeParams = { + ...( + coreMetadata.abi + .filter((a) => a.type === "function") + .find((a) => a.name === "initialize")?.inputs || [] + ).reduce( + (acc, param) => { + if (!param.name) { + param.name = "*"; + } + + acc[param.name] = replaceTemplateValues( + coreMetadata?.constructorParams?.[param.name]?.defaultValue + ? coreMetadata?.constructorParams?.[param.name]?.defaultValue || + "" + : param.name === "_royaltyBps" || + param.name === "_platformFeeBps" + ? "0" + : "", + param.type, + { + connectedWallet: "0x0000000000000000000000000000000000000000", + chainId: 1, + }, + ); + + return acc; + }, + {} as Record, + ), + _name, + _symbol, + _contractURI, + _owner, + }; + + const moduleInitializeParams = modulesMetadata.reduce( + (acc, mod) => { + const params = getModuleInstallParams(mod); + const paramNames = params + .map((param) => param.name) + .filter((p) => p !== undefined); + const returnVal: Record = {}; + + // set connected wallet address as default "royaltyRecipient" + if (showRoyaltyFieldset(paramNames)) { + returnVal.royaltyRecipient = _owner || ""; + returnVal.royaltyBps = "0"; + returnVal.transferValidator = ZERO_ADDRESS; + } + + // set connected wallet address as default "primarySaleRecipient" + else if (showPrimarySaleFiedset(paramNames)) { + returnVal.primarySaleRecipient = _owner || ""; + } + + // set superchain bridge address + else if (showSuperchainBridgeFieldset(paramNames)) { + returnVal.superchainBridge = + "0x4200000000000000000000000000000000000010"; // OP Superchain Bridge + } + + acc[mod.name] = returnVal; + return acc; + }, + {} as Record>, + ); + + const chain = defineChain(chainId); + const client = getThirdwebClient(); + const salt = "thirdweb"; + + await switchChain(chain); + + const steps: DeployModalStep[] = [ + { + type: "deploy", + signatureCount: 1, + }, + ]; + + deployStatusModal.setViewContractLink(""); + deployStatusModal.open(steps); + + const crosschainContractAddress = await deployContractfromDeployMetadata({ + account: activeAccount, + chain, + client, + deployMetadata: coreMetadata, + initializeParams: coreInitializeParams, + salt, + modules: modulesMetadata.map((m) => ({ + deployMetadata: m, + initializeParams: moduleInitializeParams[m.name], + })), + }); + + await verifyContract({ + address: crosschainContractAddress, + chain, + client, + }); + + deployStatusModal.nextStep(); + deployStatusModal.setViewContractLink( + `/${chain.id}/${crosschainContractAddress}`, + ); + } catch (e) { + onError(e); + console.error("failed to deploy contract", e); + deployStatusModal.close(); + } + }; + + return ( + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + +
+ +
+ ); +} diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx new file mode 100644 index 00000000000..f5dcd1c55a6 --- /dev/null +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -0,0 +1,144 @@ +import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy"; +import { notFound, redirect } from "next/navigation"; +import { readContract } from "thirdweb"; +import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; +import { type FetchDeployMetadataResult, getContract } from "thirdweb/contract"; +import { getInstalledModules } from "thirdweb/modules"; +import { eth_getCode, getRpcClient } from "thirdweb/rpc"; +import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; +import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; +import { DataTable } from "./data-table"; + +export function getModuleInstallParams(mod: FetchDeployMetadataResult) { + return ( + mod.abi + .filter((a) => a.type === "function") + .find((f) => f.name === "encodeBytesOnInstall")?.inputs || [] + ); +} + +export default async function Page(props: { + params: Promise<{ + contractAddress: string; + chain_id: string; + }>; +}) { + const params = await props.params; + const info = await getContractPageParamsInfo(params); + + if (!info) { + notFound(); + } + + const { contract } = info; + + if (contract.chain.id === localhost.id) { + return
asd
; + } + + const { isModularCore } = await getContractPageMetadata(contract); + + if (!isModularCore) { + redirect(`/${params.chain_id}/${params.contractAddress}`); + } + + const _originalCode = await eth_getCode( + getRpcClient({ + client: contract.client, + chain: contract.chain, + }), + { + address: contract.address, + }, + ); + + const _topOPStackChainIds = [ + 8453, // Base + 10, // OP Mainnet + 34443, // Mode Network + 7560, // Cyber + 7777777, // Zora + ]; + + const topOPStackTestnetChainIds = [ + 84532, // Base + 11155420, // OP testnet + 919, // Mode Network + 111557560, // Cyber + 999999999, // Zora + ]; + + const chainsDeployedOn = await Promise.all( + topOPStackTestnetChainIds.map(async (chainId) => { + const chain = defineChain(chainId); + const chainMetadata = await getChainMetadata(chain); + + const rpcRequest = getRpcClient({ + client: contract.client, + chain, + }); + const code = await eth_getCode(rpcRequest, { + address: params.contractAddress, + }); + + return { + id: chainId, + network: chainMetadata.name, + chainId: chain.id, + status: code === _originalCode ? "DEPLOYED" : "NOT_DEPLOYED", + }; + }), + ); + + const modules = await getInstalledModules({ contract }); + + const coreMetadata = ( + await fetchPublishedContractsFromDeploy({ + contract, + client: contract.client, + }) + ).at(-1) as FetchDeployMetadataResult; + const modulesMetadata = (await Promise.all( + modules.map(async (m) => + ( + await fetchPublishedContractsFromDeploy({ + contract: getContract({ + chain: contract.chain, + client: contract.client, + address: m.implementation, + }), + client: contract.client, + }) + ).at(-1), + ), + )) as FetchDeployMetadataResult[]; + + const erc20InitialData = await Promise.all([ + readContract({ + contract: contract, + method: "function name() view returns (string)", + }), + readContract({ + contract: contract, + method: "function symbol() view returns (string)", + }), + readContract({ + contract: contract, + method: "function contractURI() view returns (string)", + }), + readContract({ + contract: contract, + method: "function owner() view returns (address)", + }), + ]); + + return ( + + ); +} diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 2b3683b8389..1e835aebeec 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -174,6 +174,10 @@ export const CustomContractForm: React.FC = ({ !isFactoryDeployment && (metadata?.name.includes("AccountFactory") || false); + const isSuperchainInterop = !!modules?.find( + (m) => m.name === "SuperChainInterop", + ); + const parsedDeployParams = useMemo( () => ({ ...deployParams.reduce( @@ -430,11 +434,14 @@ export const CustomContractForm: React.FC = ({ _contractURI, }; - const salt = params.deployDeterministic - ? params.signerAsSalt - ? activeAccount.address.concat(params.saltForCreate2) - : params.saltForCreate2 - : undefined; + // TODO: discuss how to handle the salt properly for crosschain contracts + const salt = isSuperchainInterop + ? "thirdweb" + : params.deployDeterministic + ? params.signerAsSalt + ? activeAccount.address.concat(params.saltForCreate2) + : params.saltForCreate2 + : undefined; return await deployContractfromDeployMetadata({ account: activeAccount, diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx index 46c4c9ff09e..0404cc5f59e 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx @@ -194,3 +194,7 @@ export function showRoyaltyFieldset(paramNames: string[]) { export function showPrimarySaleFiedset(paramNames: string[]) { return paramNames.length === 1 && paramNames.includes("primarySaleRecipient"); } + +export function showSuperchainBridgeFieldset(paramNames: string[]) { + return paramNames.length === 1 && paramNames.includes("superchainBridge"); +} diff --git a/apps/dashboard/src/data/explore.ts b/apps/dashboard/src/data/explore.ts index b0261b4034d..494d259aa12 100644 --- a/apps/dashboard/src/data/explore.ts +++ b/apps/dashboard/src/data/explore.ts @@ -192,6 +192,28 @@ const MODULAR_CONTRACTS = { ], } satisfies ExploreCategory; +const CROSS_CHAIN = { + id: "cross-chain", + name: "cross-chain", + displayName: "Cross Chain", + description: + "Collection of contracts that are popular for building cross-chain applications.", + contracts: [ + // erc20 drop + [ + "thirdweb.eth/ERC20CoreInitializable", + [ + "deployer.thirdweb.eth/ClaimableERC20", + "0xf2d22310905EaD92C19c7ef0003C1AD38e129cb1/SuperChainInterop", // TODO: replace this with the OP published contract + ], + { + title: "Modular Token Drop", + description: "ERC20 Tokens that others can mint.", + }, + ], + ], +} satisfies ExploreCategory; + const AIRDROP = { id: "airdrop", name: "Airdrop", @@ -281,6 +303,7 @@ const SMART_WALLET = { const CATEGORIES: Record = { [POPULAR.id]: POPULAR, [MODULAR_CONTRACTS.id]: MODULAR_CONTRACTS, + [CROSS_CHAIN.id]: CROSS_CHAIN, [NFTS.id]: NFTS, [MARKETS.id]: MARKETS, [DROPS.id]: DROPS, From 9fedac3624dc7003c0e41348acd765959c6e7583 Mon Sep 17 00:00:00 2001 From: Stanley Date: Wed, 27 Nov 2024 12:01:00 -0500 Subject: [PATCH 02/37] WIP: hardcoded modified version of TWCloneFactory to be used --- .../[contractAddress]/cross-chain/data-table.tsx | 3 ++- .../[contractAddress]/cross-chain/page.tsx | 8 -------- .../contract-deploy-form/custom-contract.tsx | 3 ++- .../contract/deployment/deploy-via-autofactory.ts | 4 ++-- .../src/extensions/prebuilts/deploy-published.ts | 14 +++++++++++--- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 6258913067c..67bded7da7b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -36,6 +36,7 @@ import { ZERO_ADDRESS, defineChain } from "thirdweb"; import type { FetchDeployMetadataResult } from "thirdweb/contract"; import { deployContractfromDeployMetadata } from "thirdweb/deploys"; import { useActiveAccount, useSwitchActiveWalletChain } from "thirdweb/react"; +import { concatHex, padHex } from "thirdweb/utils"; export type CrossChain = { id: number; @@ -170,7 +171,7 @@ export function DataTable({ const chain = defineChain(chainId); const client = getThirdwebClient(); - const salt = "thirdweb"; + const salt = concatHex(["0x0101", padHex("0x", { size: 30 })]).toString(); await switchChain(chain); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index f5dcd1c55a6..67df2509ece 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -52,14 +52,6 @@ export default async function Page(props: { }, ); - const _topOPStackChainIds = [ - 8453, // Base - 10, // OP Mainnet - 34443, // Mode Network - 7560, // Cyber - 7777777, // Zora - ]; - const topOPStackTestnetChainIds = [ 84532, // Base 11155420, // OP testnet diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 1e835aebeec..74ad59bec63 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -35,6 +35,7 @@ import { } from "thirdweb/deploys"; import { useActiveAccount, useActiveWalletChain } from "thirdweb/react"; import { upload } from "thirdweb/storage"; +import { concatHex, padHex } from "thirdweb/utils"; import { FormHelperText, FormLabel, Heading, Text } from "tw-components"; import { useCustomFactoryAbi, useFunctionParamsFromABI } from "../hooks"; import { addContractToMultiChainRegistry } from "../utils"; @@ -436,7 +437,7 @@ export const CustomContractForm: React.FC = ({ // TODO: discuss how to handle the salt properly for crosschain contracts const salt = isSuperchainInterop - ? "thirdweb" + ? concatHex(["0x0101", padHex("0x", { size: 30 })]).toString() : params.deployDeterministic ? params.signerAsSalt ? activeAccount.address.concat(params.saltForCreate2) diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index 3c4410fe8a9..97dda7418ed 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -6,7 +6,6 @@ import { getRpcClient } from "../../rpc/rpc.js"; import { encode } from "../../transaction/actions/encode.js"; import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js"; import type { PreparedTransaction } from "../../transaction/prepare-transaction.js"; -import { keccakId } from "../../utils/any-evm/keccak-id.js"; import { isZkSyncChain } from "../../utils/any-evm/zksync/isZkSyncChain.js"; import { toHex } from "../../utils/encoding/hex.js"; import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js"; @@ -35,7 +34,8 @@ export function prepareAutoFactoryDeployTransaction( }); const blockNumber = await eth_blockNumber(rpcRequest); const salt = args.salt - ? keccakId(args.salt) + ? //? keccakId(args.salt) + (args.salt as `0x${string}`) : toHex(blockNumber, { size: 32, }); diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 1d72dab99fd..150547ecaa5 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -163,7 +163,7 @@ export async function deployContractfromDeployMetadata( import("../../contract/deployment/deploy-via-autofactory.js"), import("../../contract/deployment/utils/bootstrap.js"), ]); - const { cloneFactoryContract, implementationContract } = + const { cloneFactoryContract: _, implementationContract } = await getOrDeployInfraForPublishedContract({ chain, client, @@ -181,18 +181,26 @@ export async function deployContractfromDeployMetadata( const initializeTransaction = await getInitializeTransaction({ client, chain, - deployMetadata: deployMetadata, + deployMetadata, implementationContract, initializeParams, account, modules, }); + // TODO: remove this once the modified version of TWCloneFactory + // has been published under the thirdweb wallet + const modifiedCloneFactoryContract = getContract({ + client, + address: "0x7756D8a084e55d9872BD5bBDf6867543D15866A4", // only deployed on OP and zora testnets + chain, + }); + return deployViaAutoFactory({ client, chain, account, - cloneFactoryContract, + cloneFactoryContract: modifiedCloneFactoryContract, initializeTransaction, salt, }); From 4595d58991826c8407b4d61b807f05d68fb01a59 Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 29 Nov 2024 21:16:26 -0500 Subject: [PATCH 03/37] WIP: TODO's created to fetch for ContractInitialized event and updated salt transforming --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 5 +++++ .../[chain_id]/[contractAddress]/cross-chain/page.tsx | 6 ++++-- apps/dashboard/src/data/explore.ts | 2 +- .../src/contract/deployment/deploy-via-autofactory.ts | 6 ++++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 67bded7da7b..bbb252435d3 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -101,6 +101,11 @@ export function DataTable({ throw new Error("No active account"); } + // TODO: fetch the ContractIntialized event + // Guessing it has to fetch the transaction hash based on the contract address + // Then fetch the block number from the transaction hash + // Then fetch the event by limiting the block number + const coreInitializeParams = { ...( coreMetadata.abi diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index 67df2509ece..7f00ebab171 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -77,7 +77,10 @@ export default async function Page(props: { id: chainId, network: chainMetadata.name, chainId: chain.id, - status: code === _originalCode ? "DEPLOYED" : "NOT_DEPLOYED", + status: + code === _originalCode + ? ("DEPLOYED" as const) + : ("NOT_DEPLOYED" as const), }; }), ); @@ -129,7 +132,6 @@ export default async function Page(props: { coreMetadata={coreMetadata} modulesMetadata={modulesMetadata} erc20InitialData={erc20InitialData} - contract={contract} data={chainsDeployedOn} /> ); diff --git a/apps/dashboard/src/data/explore.ts b/apps/dashboard/src/data/explore.ts index 494d259aa12..dd3c3e577f3 100644 --- a/apps/dashboard/src/data/explore.ts +++ b/apps/dashboard/src/data/explore.ts @@ -201,7 +201,7 @@ const CROSS_CHAIN = { contracts: [ // erc20 drop [ - "thirdweb.eth/ERC20CoreInitializable", + "0x5bb2610C42280674d6f70682f76311B44D1c07FB/ERC20CoreInitializable", // TODO: replace this with the thirdweb published contract [ "deployer.thirdweb.eth/ClaimableERC20", "0xf2d22310905EaD92C19c7ef0003C1AD38e129cb1/SuperChainInterop", // TODO: replace this with the OP published contract diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index 97dda7418ed..5d5026eaa8d 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -1,3 +1,4 @@ +import { keccakId } from "src/exports/utils.js"; import { parseEventLogs } from "../../event/actions/parse-logs.js"; import { proxyDeployedEvent } from "../../extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.js"; import { deployProxyByImplementation } from "../../extensions/thirdweb/__generated__/IContractFactory/write/deployProxyByImplementation.js"; @@ -34,8 +35,9 @@ export function prepareAutoFactoryDeployTransaction( }); const blockNumber = await eth_blockNumber(rpcRequest); const salt = args.salt - ? //? keccakId(args.salt) - (args.salt as `0x${string}`) + ? args.salt.startsWith("0x") && args.salt.length === 66 + ? (args.salt as `0x${string}`) + : keccakId(args.salt) : toHex(blockNumber, { size: 32, }); From d08b33c882660c062481c0d4bc0cb2bff802ebf5 Mon Sep 17 00:00:00 2001 From: Stanley Date: Wed, 4 Dec 2024 18:36:13 -0500 Subject: [PATCH 04/37] WIP: Testing out deploying bare core contract with modules installed afterwards --- .../cross-chain/data-table.tsx | 86 +------------------ .../[contractAddress]/cross-chain/page.tsx | 21 ++++- .../modules/components/ModuleForm.tsx | 1 + .../contract-deploy-form/custom-contract.tsx | 85 ++++++++++++++++-- apps/dashboard/src/data/explore.ts | 2 +- .../extensions/prebuilts/deploy-published.ts | 2 +- 6 files changed, 102 insertions(+), 95 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index bbb252435d3..e51f18d3482 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -24,15 +24,8 @@ import { DeployStatusModal, useDeployStatusModal, } from "components/contract-components/contract-deploy-form/deploy-context-modal"; -import { - getModuleInstallParams, - showPrimarySaleFiedset, - showRoyaltyFieldset, - showSuperchainBridgeFieldset, -} from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; import { useTxNotifications } from "hooks/useTxNotifications"; -import { replaceTemplateValues } from "lib/deployment/template-values"; -import { ZERO_ADDRESS, defineChain } from "thirdweb"; +import { defineChain } from "thirdweb"; import type { FetchDeployMetadataResult } from "thirdweb/contract"; import { deployContractfromDeployMetadata } from "thirdweb/deploys"; import { useActiveAccount, useSwitchActiveWalletChain } from "thirdweb/react"; @@ -49,12 +42,12 @@ export function DataTable({ data, coreMetadata, modulesMetadata, - erc20InitialData: [_name, _symbol, _contractURI, _owner], + initializerCalldata, }: { data: CrossChain[]; coreMetadata: FetchDeployMetadataResult; modulesMetadata: FetchDeployMetadataResult[]; - erc20InitialData: string[]; + initializerCalldata: `0x${string}`; }) { const activeAccount = useActiveAccount(); const switchChain = useSwitchActiveWalletChain(); @@ -101,78 +94,7 @@ export function DataTable({ throw new Error("No active account"); } - // TODO: fetch the ContractIntialized event - // Guessing it has to fetch the transaction hash based on the contract address - // Then fetch the block number from the transaction hash - // Then fetch the event by limiting the block number - - const coreInitializeParams = { - ...( - coreMetadata.abi - .filter((a) => a.type === "function") - .find((a) => a.name === "initialize")?.inputs || [] - ).reduce( - (acc, param) => { - if (!param.name) { - param.name = "*"; - } - - acc[param.name] = replaceTemplateValues( - coreMetadata?.constructorParams?.[param.name]?.defaultValue - ? coreMetadata?.constructorParams?.[param.name]?.defaultValue || - "" - : param.name === "_royaltyBps" || - param.name === "_platformFeeBps" - ? "0" - : "", - param.type, - { - connectedWallet: "0x0000000000000000000000000000000000000000", - chainId: 1, - }, - ); - - return acc; - }, - {} as Record, - ), - _name, - _symbol, - _contractURI, - _owner, - }; - - const moduleInitializeParams = modulesMetadata.reduce( - (acc, mod) => { - const params = getModuleInstallParams(mod); - const paramNames = params - .map((param) => param.name) - .filter((p) => p !== undefined); - const returnVal: Record = {}; - - // set connected wallet address as default "royaltyRecipient" - if (showRoyaltyFieldset(paramNames)) { - returnVal.royaltyRecipient = _owner || ""; - returnVal.royaltyBps = "0"; - returnVal.transferValidator = ZERO_ADDRESS; - } - - // set connected wallet address as default "primarySaleRecipient" - else if (showPrimarySaleFiedset(paramNames)) { - returnVal.primarySaleRecipient = _owner || ""; - } - - // set superchain bridge address - else if (showSuperchainBridgeFieldset(paramNames)) { - returnVal.superchainBridge = - "0x4200000000000000000000000000000000000010"; // OP Superchain Bridge - } - - acc[mod.name] = returnVal; - return acc; - }, - {} as Record>, - ); + // TODO: deploy the core contract directly with the initializer calldata const chain = defineChain(chainId); const client = getThirdwebClient(); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index 7f00ebab171..0df58424a7f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -1,6 +1,7 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy"; import { notFound, redirect } from "next/navigation"; import { readContract } from "thirdweb"; +import { getContractEvents, prepareEvent } from "thirdweb"; import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; import { type FetchDeployMetadataResult, getContract } from "thirdweb/contract"; import { getInstalledModules } from "thirdweb/modules"; @@ -42,7 +43,7 @@ export default async function Page(props: { redirect(`/${params.chain_id}/${params.contractAddress}`); } - const _originalCode = await eth_getCode( + const originalCode = await eth_getCode( getRpcClient({ client: contract.client, chain: contract.chain, @@ -78,7 +79,7 @@ export default async function Page(props: { network: chainMetadata.name, chainId: chain.id, status: - code === _originalCode + code === originalCode ? ("DEPLOYED" as const) : ("NOT_DEPLOYED" as const), }; @@ -108,7 +109,7 @@ export default async function Page(props: { ), )) as FetchDeployMetadataResult[]; - const erc20InitialData = await Promise.all([ + const _erc20InitialData = await Promise.all([ readContract({ contract: contract, method: "function name() view returns (string)", @@ -127,11 +128,23 @@ export default async function Page(props: { }), ]); + const ProxyDeployedEvent = prepareEvent({ + signature: + "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", + }); + + // TODO: figure out how to fetch the events properly + const [event] = await getContractEvents({ + contract, + events: [ProxyDeployedEvent], + blockRange: 123456n, + }); + return ( ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx index f0a15b15879..9c520b8569a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx @@ -252,6 +252,7 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => { module: selectedModuleMeta, isQueryEnabled: !!selectedModule && !!isModuleCompatibleQuery.data, }); + console.log("moduleInstallParams", moduleInstallParams.data); return ( diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 74ad59bec63..687366c2369 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -26,16 +26,27 @@ import { CircleAlertIcon, ExternalLinkIcon, InfoIcon } from "lucide-react"; import Link from "next/link"; import { useCallback, useMemo } from "react"; import { FormProvider, type UseFormReturn, useForm } from "react-hook-form"; -import { ZERO_ADDRESS } from "thirdweb"; +import { + ZERO_ADDRESS, + getContract, + sendTransaction, + waitForReceipt, +} from "thirdweb"; import type { FetchDeployMetadataResult } from "thirdweb/contract"; import { deployContractfromDeployMetadata, deployMarketplaceContract, getRequiredTransactions, } from "thirdweb/deploys"; +import { installPublishedModule } from "thirdweb/modules"; import { useActiveAccount, useActiveWalletChain } from "thirdweb/react"; import { upload } from "thirdweb/storage"; -import { concatHex, padHex } from "thirdweb/utils"; +import { + type AbiFunction, + concatHex, + encodeAbiParameters, + padHex, +} from "thirdweb/utils"; import { FormHelperText, FormLabel, Heading, Text } from "tw-components"; import { useCustomFactoryAbi, useFunctionParamsFromABI } from "../hooks"; import { addContractToMultiChainRegistry } from "../utils"; @@ -444,18 +455,78 @@ export const CustomContractForm: React.FC = ({ : params.saltForCreate2 : undefined; - return await deployContractfromDeployMetadata({ + const moduleDeployData = modules?.map((m) => ({ + deployMetadata: m, + initializeParams: params.moduleData[m.name], + })); + console.log("module deploy data", moduleDeployData); + + const deployData = { account: activeAccount, chain: walletChain, client: thirdwebClient, deployMetadata: metadata, initializeParams, salt, - modules: modules?.map((m) => ({ - deployMetadata: m, - initializeParams: params.moduleData[m.name], - })), + modules: isSuperchainInterop + ? // remove modules for superchain interop in order to deploy deterministically deploy just the core contract + [] + : moduleDeployData, + }; + + console.log("deploy data", deployData); + const coreContractAddress = + await deployContractfromDeployMetadata(deployData); + const coreContract = getContract({ + client: thirdwebClient, + address: coreContractAddress, + chain: walletChain, }); + console.log("core contract", coreContract); + + if (isSuperchainInterop && moduleDeployData) { + await Promise.all( + moduleDeployData.map(async (m) => { + let moduleData: `0x${string}` | undefined; + + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; + + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } + console.log("module install params", moduleInstallParams); + + const installTransaction = installPublishedModule({ + contract: coreContract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + }); + console.log("install transaction", installTransaction); + + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); + console.log("tx result", txResult); + + return await waitForReceipt(txResult); + }), + ); + } }, }); diff --git a/apps/dashboard/src/data/explore.ts b/apps/dashboard/src/data/explore.ts index dd3c3e577f3..46eaa168a7a 100644 --- a/apps/dashboard/src/data/explore.ts +++ b/apps/dashboard/src/data/explore.ts @@ -201,7 +201,7 @@ const CROSS_CHAIN = { contracts: [ // erc20 drop [ - "0x5bb2610C42280674d6f70682f76311B44D1c07FB/ERC20CoreInitializable", // TODO: replace this with the thirdweb published contract + "deployer.thirdweb.eth/ERC20CoreInitializable", // TODO: replace this with the thirdweb published contract [ "deployer.thirdweb.eth/ClaimableERC20", "0xf2d22310905EaD92C19c7ef0003C1AD38e129cb1/SuperChainInterop", // TODO: replace this with the OP published contract diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 150547ecaa5..95b5c93d726 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -192,7 +192,7 @@ export async function deployContractfromDeployMetadata( // has been published under the thirdweb wallet const modifiedCloneFactoryContract = getContract({ client, - address: "0x7756D8a084e55d9872BD5bBDf6867543D15866A4", // only deployed on OP and zora testnets + address: "0xB83db4b940e4796aA1f53DBFC824B9B1865835D5", // only deployed on OP and zora testnets chain, }); From 1f74bf686c0cba9913c5444992e40201eb93b639 Mon Sep 17 00:00:00 2001 From: Stanley Date: Thu, 5 Dec 2024 12:05:50 -0500 Subject: [PATCH 05/37] WIP: staging to get help --- .../contract-deploy-form/custom-contract.tsx | 111 ++++++++---------- apps/dashboard/src/data/explore.ts | 2 +- .../deployment/deploy-via-autofactory.ts | 2 +- .../extensions/prebuilts/deploy-published.ts | 2 +- .../IContractFactory/events/ProxyDeployed.ts | 2 +- .../actions/send-and-confirm-transaction.ts | 4 +- packages/thirdweb/src/utils/ens/namehash.ts | 5 +- 7 files changed, 60 insertions(+), 68 deletions(-) diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 687366c2369..e455dbc7796 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -26,27 +26,16 @@ import { CircleAlertIcon, ExternalLinkIcon, InfoIcon } from "lucide-react"; import Link from "next/link"; import { useCallback, useMemo } from "react"; import { FormProvider, type UseFormReturn, useForm } from "react-hook-form"; -import { - ZERO_ADDRESS, - getContract, - sendTransaction, - waitForReceipt, -} from "thirdweb"; +import { ZERO_ADDRESS, getContract } from "thirdweb"; import type { FetchDeployMetadataResult } from "thirdweb/contract"; import { deployContractfromDeployMetadata, deployMarketplaceContract, getRequiredTransactions, } from "thirdweb/deploys"; -import { installPublishedModule } from "thirdweb/modules"; import { useActiveAccount, useActiveWalletChain } from "thirdweb/react"; import { upload } from "thirdweb/storage"; -import { - type AbiFunction, - concatHex, - encodeAbiParameters, - padHex, -} from "thirdweb/utils"; +import { concatHex, padHex } from "thirdweb/utils"; import { FormHelperText, FormLabel, Heading, Text } from "tw-components"; import { useCustomFactoryAbi, useFunctionParamsFromABI } from "../hooks"; import { addContractToMultiChainRegistry } from "../utils"; @@ -461,7 +450,7 @@ export const CustomContractForm: React.FC = ({ })); console.log("module deploy data", moduleDeployData); - const deployData = { + const coreContractAddress = await deployContractfromDeployMetadata({ account: activeAccount, chain: walletChain, client: thirdwebClient, @@ -472,11 +461,7 @@ export const CustomContractForm: React.FC = ({ ? // remove modules for superchain interop in order to deploy deterministically deploy just the core contract [] : moduleDeployData, - }; - - console.log("deploy data", deployData); - const coreContractAddress = - await deployContractfromDeployMetadata(deployData); + }); const coreContract = getContract({ client: thirdwebClient, address: coreContractAddress, @@ -484,49 +469,51 @@ export const CustomContractForm: React.FC = ({ }); console.log("core contract", coreContract); - if (isSuperchainInterop && moduleDeployData) { - await Promise.all( - moduleDeployData.map(async (m) => { - let moduleData: `0x${string}` | undefined; - - const moduleInstallParams = m.deployMetadata.abi.find( - (abiType) => - (abiType as AbiFunction).name === "encodeBytesOnInstall", - ) as AbiFunction | undefined; - - if (m.initializeParams && moduleInstallParams) { - moduleData = encodeAbiParameters( - ( - moduleInstallParams.inputs as { name: string; type: string }[] - ).map((p) => ({ - name: p.name, - type: p.type, - })), - Object.values(m.initializeParams), - ); - } - console.log("module install params", moduleInstallParams); - - const installTransaction = installPublishedModule({ - contract: coreContract, - account: activeAccount, - moduleName: m.deployMetadata.name, - publisher: m.deployMetadata.publisher, - version: m.deployMetadata.version, - moduleData, - }); - console.log("install transaction", installTransaction); - - const txResult = await sendTransaction({ - transaction: installTransaction, - account: activeAccount, - }); - console.log("tx result", txResult); - - return await waitForReceipt(txResult); - }), - ); - } + //if (isSuperchainInterop && moduleDeployData) { + // await Promise.all( + // moduleDeployData.map(async (m) => { + // let moduleData: `0x${string}` | undefined; + // + // const moduleInstallParams = m.deployMetadata.abi.find( + // (abiType) => + // (abiType as AbiFunction).name === "encodeBytesOnInstall", + // ) as AbiFunction | undefined; + // + // if (m.initializeParams && moduleInstallParams) { + // moduleData = encodeAbiParameters( + // ( + // moduleInstallParams.inputs as { name: string; type: string }[] + // ).map((p) => ({ + // name: p.name, + // type: p.type, + // })), + // Object.values(m.initializeParams), + // ); + // } + // console.log("module install params", moduleInstallParams); + // + // const installTransaction = installPublishedModule({ + // contract: coreContract, + // account: activeAccount, + // moduleName: m.deployMetadata.name, + // publisher: m.deployMetadata.publisher, + // version: m.deployMetadata.version, + // moduleData, + // }); + // console.log("install transaction", installTransaction); + // + // const txResult = await sendTransaction({ + // transaction: installTransaction, + // account: activeAccount, + // }); + // console.log("tx result", txResult); + // + // return await waitForReceipt(txResult); + // }), + // ); + //} + + return coreContractAddress; }, }); diff --git a/apps/dashboard/src/data/explore.ts b/apps/dashboard/src/data/explore.ts index 46eaa168a7a..a75809aefd8 100644 --- a/apps/dashboard/src/data/explore.ts +++ b/apps/dashboard/src/data/explore.ts @@ -203,8 +203,8 @@ const CROSS_CHAIN = { [ "deployer.thirdweb.eth/ERC20CoreInitializable", // TODO: replace this with the thirdweb published contract [ - "deployer.thirdweb.eth/ClaimableERC20", "0xf2d22310905EaD92C19c7ef0003C1AD38e129cb1/SuperChainInterop", // TODO: replace this with the OP published contract + "deployer.thirdweb.eth/ClaimableERC20", ], { title: "Modular Token Drop", diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index 5d5026eaa8d..a4a8fc925a0 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -1,4 +1,3 @@ -import { keccakId } from "src/exports/utils.js"; import { parseEventLogs } from "../../event/actions/parse-logs.js"; import { proxyDeployedEvent } from "../../extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.js"; import { deployProxyByImplementation } from "../../extensions/thirdweb/__generated__/IContractFactory/write/deployProxyByImplementation.js"; @@ -7,6 +6,7 @@ import { getRpcClient } from "../../rpc/rpc.js"; import { encode } from "../../transaction/actions/encode.js"; import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js"; import type { PreparedTransaction } from "../../transaction/prepare-transaction.js"; +import { keccakId } from "../../utils/any-evm/keccak-id.js"; import { isZkSyncChain } from "../../utils/any-evm/zksync/isZkSyncChain.js"; import { toHex } from "../../utils/encoding/hex.js"; import { resolvePromisedValue } from "../../utils/promise/resolve-promised-value.js"; diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 95b5c93d726..3e4cb6f8052 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -196,7 +196,7 @@ export async function deployContractfromDeployMetadata( chain, }); - return deployViaAutoFactory({ + return await deployViaAutoFactory({ client, chain, account, diff --git a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts index bc0db42d912..d966b2afc25 100644 --- a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts +++ b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts @@ -41,7 +41,7 @@ export type ProxyDeployedEventFilters = Partial<{ export function proxyDeployedEvent(filters: ProxyDeployedEventFilters = {}) { return prepareEvent({ signature: - "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer)", + "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", filters, }); } diff --git a/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts b/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts index 68d635afd53..69368820d72 100644 --- a/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts +++ b/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts @@ -53,5 +53,7 @@ export async function sendAndConfirmTransaction( options: SendTransactionOptions, ): Promise { const submittedTx = await sendTransaction(options); - return waitForReceipt(submittedTx); + const receipt = await waitForReceipt(submittedTx); + + return receipt; } diff --git a/packages/thirdweb/src/utils/ens/namehash.ts b/packages/thirdweb/src/utils/ens/namehash.ts index 87eb6fae7e4..fa450a83f30 100644 --- a/packages/thirdweb/src/utils/ens/namehash.ts +++ b/packages/thirdweb/src/utils/ens/namehash.ts @@ -20,7 +20,10 @@ export function namehash(name: string) { const hashed = hashFromEncodedLabel ? toBytes(hashFromEncodedLabel) : keccak256(stringToBytes(item), "bytes"); - result = keccak256(concat([result, hashed]), "bytes"); + result = keccak256( + concat([result, hashed]), + "bytes", + ) as Uint8Array; } return bytesToHex(result); From 3248212daf4a3414bfdb5f4afc61935f877c97d8 Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 6 Dec 2024 13:20:47 -0500 Subject: [PATCH 06/37] successfully deploys using the initialize data from the event --- .../cross-chain/data-table.tsx | 16 ++--- .../[contractAddress]/cross-chain/page.tsx | 33 +++------- .../modules/components/ModuleForm.tsx | 1 - .../deployment/deploy-via-autofactory.ts | 62 ++++++++++++++++++- .../extensions/prebuilts/deploy-published.ts | 40 ++++++++++++ .../src/utils/any-evm/deploy-metadata.ts | 2 +- 6 files changed, 120 insertions(+), 34 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index e51f18d3482..1738d6a5cd0 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -42,12 +42,12 @@ export function DataTable({ data, coreMetadata, modulesMetadata, - initializerCalldata, + initializeData, }: { data: CrossChain[]; coreMetadata: FetchDeployMetadataResult; modulesMetadata: FetchDeployMetadataResult[]; - initializerCalldata: `0x${string}`; + initializeData?: `0x${string}`; }) { const activeAccount = useActiveAccount(); const switchChain = useSwitchActiveWalletChain(); @@ -116,14 +116,14 @@ export function DataTable({ account: activeAccount, chain, client, - deployMetadata: coreMetadata, - initializeParams: coreInitializeParams, + deployMetadata: { + ...coreMetadata, + deployType: "crosschain" as const, + }, + initializeData, salt, - modules: modulesMetadata.map((m) => ({ - deployMetadata: m, - initializeParams: moduleInitializeParams[m.name], - })), }); + console.log("crosschain contract address", crosschainContractAddress); await verifyContract({ address: crosschainContractAddress, diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index 0df58424a7f..bef7d5df69c 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -1,6 +1,5 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy"; import { notFound, redirect } from "next/navigation"; -import { readContract } from "thirdweb"; import { getContractEvents, prepareEvent } from "thirdweb"; import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; import { type FetchDeployMetadataResult, getContract } from "thirdweb/contract"; @@ -109,42 +108,30 @@ export default async function Page(props: { ), )) as FetchDeployMetadataResult[]; - const _erc20InitialData = await Promise.all([ - readContract({ - contract: contract, - method: "function name() view returns (string)", - }), - readContract({ - contract: contract, - method: "function symbol() view returns (string)", - }), - readContract({ - contract: contract, - method: "function contractURI() view returns (string)", - }), - readContract({ - contract: contract, - method: "function owner() view returns (address)", - }), - ]); - const ProxyDeployedEvent = prepareEvent({ signature: "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", }); + const twCloneFactoryContract = getContract({ + address: "0xB83db4b940e4796aA1f53DBFC824B9B1865835D5", + chain: contract.chain, + client: contract.client, + }); + // TODO: figure out how to fetch the events properly - const [event] = await getContractEvents({ - contract, + const events = await getContractEvents({ + contract: twCloneFactoryContract, events: [ProxyDeployedEvent], blockRange: 123456n, }); + const event = events.find((e) => e.args.proxy === params.contractAddress); return ( ); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx index b8fda468cc6..8ae9666803f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/modules/components/ModuleForm.tsx @@ -252,7 +252,6 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => { module: selectedModuleMeta, isQueryEnabled: !!selectedModule && !!isModuleCompatibleQuery.data, }); - console.log("moduleInstallParams", moduleInstallParams.data); return ( diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index a4a8fc925a0..a22643b4229 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -47,11 +47,13 @@ export function prepareAutoFactoryDeployTransaction( if (!implementation) { throw new Error("initializeTransaction must have a 'to' field set"); } - return { + const asd = { data: await encode(args.initializeTransaction), implementation, salt, } as const; + console.error("core contract deploy params: ", asd); + return asd; }, }); } @@ -108,3 +110,61 @@ export async function deployViaAutoFactory( } return decodedEvent[0]?.args.proxy; } + +/** + * @internal + */ +export async function deployViaAutoFactoryWithImplementationParams( + options: ClientAndChainAndAccount & { + cloneFactoryContract: ThirdwebContract; + initializeData?: `0x${string}`; + implementationAddress: string; + salt?: string; + }, +): Promise { + const { + client, + chain, + account, + cloneFactoryContract, + initializeData, + implementationAddress, + salt, + } = options; + + const rpcRequest = getRpcClient({ + client, + chain, + }); + const blockNumber = await eth_blockNumber(rpcRequest); + const parsedSalt = salt + ? salt.startsWith("0x") && salt.length === 66 + ? (salt as `0x${string}`) + : keccakId(salt) + : toHex(blockNumber, { + size: 32, + }); + + const asd = { + contract: cloneFactoryContract, + data: initializeData || "0x", + implementation: implementationAddress, + salt: parsedSalt, + }; + console.error("cross chain contract deploy params: ", asd); + const tx = deployProxyByImplementation(asd); + const receipt = await sendAndConfirmTransaction({ + transaction: tx, + account, + }); + const decodedEvent = parseEventLogs({ + events: [proxyDeployedEvent()], + logs: receipt.logs, + }); + if (decodedEvent.length === 0 || !decodedEvent[0]) { + throw new Error( + `No ProxyDeployed event found in transaction: ${receipt.transactionHash}`, + ); + } + return decodedEvent[0]?.args.proxy; +} diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 3e4cb6f8052..8410b6ceec5 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -2,6 +2,7 @@ import type { AbiFunction } from "abitype"; import type { Chain } from "../../chains/types.js"; import type { ThirdwebClient } from "../../client/client.js"; import { type ThirdwebContract, getContract } from "../../contract/contract.js"; +import { deployViaAutoFactoryWithImplementationParams } from "../../contract/deployment/deploy-via-autofactory.js"; import { fetchPublishedContractMetadata } from "../../contract/deployment/publisher.js"; import { getOrDeployInfraContractFromMetadata } from "../../contract/deployment/utils/bootstrap.js"; import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js"; @@ -120,6 +121,7 @@ export type DeployContractfromDeployMetadataOptions = { account: Account; deployMetadata: FetchDeployMetadataResult; initializeParams?: Record; + initializeData?: `0x${string}`; implementationConstructorParams?: Record; modules?: { deployMetadata: FetchDeployMetadataResult; @@ -139,6 +141,7 @@ export async function deployContractfromDeployMetadata( account, chain, initializeParams, + initializeData, deployMetadata, implementationConstructorParams, modules, @@ -205,6 +208,43 @@ export async function deployContractfromDeployMetadata( salt, }); } + case "crosschain": { + const { getOrDeployInfraForPublishedContract } = await import( + "../../contract/deployment/utils/bootstrap.js" + ); + const { cloneFactoryContract: _, implementationContract } = + await getOrDeployInfraForPublishedContract({ + chain, + client, + account, + contractId: deployMetadata.name, + constructorParams: + implementationConstructorParams || + (await getAllDefaultConstructorParamsForImplementation({ + chain, + client, + })), + publisher: deployMetadata.publisher, + }); + + // TODO: remove this once the modified version of TWCloneFactory + // has been published under the thirdweb wallet + const modifiedCloneFactoryContract = getContract({ + client, + address: "0xB83db4b940e4796aA1f53DBFC824B9B1865835D5", // only deployed on OP and zora testnets + chain, + }); + + return await deployViaAutoFactoryWithImplementationParams({ + client, + chain, + account, + cloneFactoryContract: modifiedCloneFactoryContract, + implementationAddress: implementationContract.address, + initializeData, + salt, + }); + } case "customFactory": { if (!deployMetadata?.factoryDeploymentData?.customFactoryInput) { throw new Error("No custom factory info found"); diff --git a/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts b/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts index 365d9b882e0..af30d8c9c1a 100644 --- a/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts +++ b/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts @@ -207,7 +207,7 @@ export type ExtendedMetadata = { factoryAddresses?: Record; } | undefined; - deployType?: "standard" | "autoFactory" | "customFactory"; + deployType?: "standard" | "autoFactory" | "customFactory" | "crosschain"; routerType?: "none" | "plugin" | "dynamic" | "modular"; networksForDeployment?: { allNetworks?: boolean; From 6b9dd3374efb017b99ff332b0140e5943705e8fe Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 6 Dec 2024 16:41:12 -0500 Subject: [PATCH 07/37] able to install modules successfully when crosschain deploying --- .../cross-chain/data-table.tsx | 124 +++++++++++++++++- .../[contractAddress]/cross-chain/page.tsx | 2 + 2 files changed, 123 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 1738d6a5cd0..8fd4cd2cd8e 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -24,12 +24,34 @@ import { DeployStatusModal, useDeployStatusModal, } from "components/contract-components/contract-deploy-form/deploy-context-modal"; +import { + getModuleInstallParams, + showPrimarySaleFiedset, + showRoyaltyFieldset, + showSuperchainBridgeFieldset, +} from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; import { useTxNotifications } from "hooks/useTxNotifications"; -import { defineChain } from "thirdweb"; -import type { FetchDeployMetadataResult } from "thirdweb/contract"; +import { + ZERO_ADDRESS, + defineChain, + getContract, + readContract, + sendTransaction, + waitForReceipt, +} from "thirdweb"; +import type { + FetchDeployMetadataResult, + ThirdwebContract, +} from "thirdweb/contract"; import { deployContractfromDeployMetadata } from "thirdweb/deploys"; +import { installPublishedModule } from "thirdweb/modules"; import { useActiveAccount, useSwitchActiveWalletChain } from "thirdweb/react"; -import { concatHex, padHex } from "thirdweb/utils"; +import { + type AbiFunction, + concatHex, + encodeAbiParameters, + padHex, +} from "thirdweb/utils"; export type CrossChain = { id: number; @@ -41,11 +63,13 @@ export type CrossChain = { export function DataTable({ data, coreMetadata, + coreContract, modulesMetadata, initializeData, }: { data: CrossChain[]; coreMetadata: FetchDeployMetadataResult; + coreContract: ThirdwebContract; modulesMetadata: FetchDeployMetadataResult[]; initializeData?: `0x${string}`; }) { @@ -131,6 +155,100 @@ export function DataTable({ client, }); + const owner = await readContract({ + contract: coreContract, + method: "function owner() view returns (address)", + params: [], + }); + console.log("owner", owner); + + const moduleInitializeParams = modulesMetadata.reduce( + (acc, mod) => { + const params = getModuleInstallParams(mod); + const paramNames = params + .map((param) => param.name) + .filter((p) => p !== undefined); + const returnVal: Record = {}; + + // set connected wallet address as default "royaltyRecipient" + if (showRoyaltyFieldset(paramNames)) { + returnVal.royaltyRecipient = owner || ""; + returnVal.royaltyBps = "0"; + returnVal.transferValidator = ZERO_ADDRESS; + } + + // set connected wallet address as default "primarySaleRecipient" + else if (showPrimarySaleFiedset(paramNames)) { + returnVal.primarySaleRecipient = owner || ""; + } + + // set superchain bridge address + else if (showSuperchainBridgeFieldset(paramNames)) { + returnVal.superchainBridge = + "0x4200000000000000000000000000000000000010"; // OP Superchain Bridge + } + + acc[mod.name] = returnVal; + return acc; + }, + {} as Record>, + ); + console.log("moduleInitializeParams", moduleInitializeParams); + + const moduleDeployData = modulesMetadata.map((m) => ({ + deployMetadata: m, + initializeParams: moduleInitializeParams[m.name], + })); + console.log("module deploy data", moduleDeployData); + + const contract = getContract({ + address: crosschainContractAddress, + chain, + client, + }); + + await Promise.all( + moduleDeployData.map(async (m) => { + let moduleData: `0x${string}` | undefined; + + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; + + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } + console.log("module install params", moduleInstallParams); + + const installTransaction = installPublishedModule({ + contract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + }); + console.log("install transaction", installTransaction); + + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); + console.log("tx result", txResult); + + return await waitForReceipt(txResult); + }), + ); + deployStatusModal.nextStep(); deployStatusModal.setViewContractLink( `/${chain.id}/${crosschainContractAddress}`, diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index bef7d5df69c..a95120a9b12 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -107,6 +107,7 @@ export default async function Page(props: { ).at(-1), ), )) as FetchDeployMetadataResult[]; + console.log("modulesMetadata", modulesMetadata); const ProxyDeployedEvent = prepareEvent({ signature: @@ -133,6 +134,7 @@ export default async function Page(props: { modulesMetadata={modulesMetadata} initializeData={event?.args.data} data={chainsDeployedOn} + coreContract={contract} /> ); } From 64ca5797d7238220ece643a38ceead1c2bd343d1 Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 6 Dec 2024 16:51:20 -0500 Subject: [PATCH 08/37] updated to be promise.allSettled when installing modules to not block deployment of the contract --- .../contract-deploy-form/custom-contract.tsx | 101 ++++++++++-------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index e455dbc7796..5940cac52e6 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -26,16 +26,27 @@ import { CircleAlertIcon, ExternalLinkIcon, InfoIcon } from "lucide-react"; import Link from "next/link"; import { useCallback, useMemo } from "react"; import { FormProvider, type UseFormReturn, useForm } from "react-hook-form"; -import { ZERO_ADDRESS, getContract } from "thirdweb"; +import { + ZERO_ADDRESS, + getContract, + sendTransaction, + waitForReceipt, +} from "thirdweb"; import type { FetchDeployMetadataResult } from "thirdweb/contract"; import { deployContractfromDeployMetadata, deployMarketplaceContract, getRequiredTransactions, } from "thirdweb/deploys"; +import { installPublishedModule } from "thirdweb/modules"; import { useActiveAccount, useActiveWalletChain } from "thirdweb/react"; import { upload } from "thirdweb/storage"; -import { concatHex, padHex } from "thirdweb/utils"; +import { + type AbiFunction, + concatHex, + encodeAbiParameters, + padHex, +} from "thirdweb/utils"; import { FormHelperText, FormLabel, Heading, Text } from "tw-components"; import { useCustomFactoryAbi, useFunctionParamsFromABI } from "../hooks"; import { addContractToMultiChainRegistry } from "../utils"; @@ -469,49 +480,49 @@ export const CustomContractForm: React.FC = ({ }); console.log("core contract", coreContract); - //if (isSuperchainInterop && moduleDeployData) { - // await Promise.all( - // moduleDeployData.map(async (m) => { - // let moduleData: `0x${string}` | undefined; - // - // const moduleInstallParams = m.deployMetadata.abi.find( - // (abiType) => - // (abiType as AbiFunction).name === "encodeBytesOnInstall", - // ) as AbiFunction | undefined; - // - // if (m.initializeParams && moduleInstallParams) { - // moduleData = encodeAbiParameters( - // ( - // moduleInstallParams.inputs as { name: string; type: string }[] - // ).map((p) => ({ - // name: p.name, - // type: p.type, - // })), - // Object.values(m.initializeParams), - // ); - // } - // console.log("module install params", moduleInstallParams); - // - // const installTransaction = installPublishedModule({ - // contract: coreContract, - // account: activeAccount, - // moduleName: m.deployMetadata.name, - // publisher: m.deployMetadata.publisher, - // version: m.deployMetadata.version, - // moduleData, - // }); - // console.log("install transaction", installTransaction); - // - // const txResult = await sendTransaction({ - // transaction: installTransaction, - // account: activeAccount, - // }); - // console.log("tx result", txResult); - // - // return await waitForReceipt(txResult); - // }), - // ); - //} + if (isSuperchainInterop && moduleDeployData) { + await Promise.allSettled( + moduleDeployData.map(async (m) => { + let moduleData: `0x${string}` | undefined; + + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; + + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } + console.log("module install params", moduleInstallParams); + + const installTransaction = installPublishedModule({ + contract: coreContract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + }); + console.log("install transaction", installTransaction); + + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); + console.log("tx result", txResult); + + return await waitForReceipt(txResult); + }), + ); + } return coreContractAddress; }, From 91438571dfdaa762610ca90aae70b46a64427237 Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 6 Dec 2024 16:58:01 -0500 Subject: [PATCH 09/37] removing console logs --- .../[contractAddress]/cross-chain/data-table.tsx | 7 ------- .../[chain_id]/[contractAddress]/cross-chain/page.tsx | 2 -- .../contract-deploy-form/custom-contract.tsx | 6 ------ .../src/contract/deployment/deploy-via-autofactory.ts | 5 +---- 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 8fd4cd2cd8e..86befcbc4e6 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -147,7 +147,6 @@ export function DataTable({ initializeData, salt, }); - console.log("crosschain contract address", crosschainContractAddress); await verifyContract({ address: crosschainContractAddress, @@ -160,7 +159,6 @@ export function DataTable({ method: "function owner() view returns (address)", params: [], }); - console.log("owner", owner); const moduleInitializeParams = modulesMetadata.reduce( (acc, mod) => { @@ -193,13 +191,11 @@ export function DataTable({ }, {} as Record>, ); - console.log("moduleInitializeParams", moduleInitializeParams); const moduleDeployData = modulesMetadata.map((m) => ({ deployMetadata: m, initializeParams: moduleInitializeParams[m.name], })); - console.log("module deploy data", moduleDeployData); const contract = getContract({ address: crosschainContractAddress, @@ -227,7 +223,6 @@ export function DataTable({ Object.values(m.initializeParams), ); } - console.log("module install params", moduleInstallParams); const installTransaction = installPublishedModule({ contract, @@ -237,13 +232,11 @@ export function DataTable({ version: m.deployMetadata.version, moduleData, }); - console.log("install transaction", installTransaction); const txResult = await sendTransaction({ transaction: installTransaction, account: activeAccount, }); - console.log("tx result", txResult); return await waitForReceipt(txResult); }), diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index a95120a9b12..215be1e839d 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -107,7 +107,6 @@ export default async function Page(props: { ).at(-1), ), )) as FetchDeployMetadataResult[]; - console.log("modulesMetadata", modulesMetadata); const ProxyDeployedEvent = prepareEvent({ signature: @@ -120,7 +119,6 @@ export default async function Page(props: { client: contract.client, }); - // TODO: figure out how to fetch the events properly const events = await getContractEvents({ contract: twCloneFactoryContract, events: [ProxyDeployedEvent], diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 5940cac52e6..47898655363 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -446,7 +446,6 @@ export const CustomContractForm: React.FC = ({ _contractURI, }; - // TODO: discuss how to handle the salt properly for crosschain contracts const salt = isSuperchainInterop ? concatHex(["0x0101", padHex("0x", { size: 30 })]).toString() : params.deployDeterministic @@ -459,7 +458,6 @@ export const CustomContractForm: React.FC = ({ deployMetadata: m, initializeParams: params.moduleData[m.name], })); - console.log("module deploy data", moduleDeployData); const coreContractAddress = await deployContractfromDeployMetadata({ account: activeAccount, @@ -478,7 +476,6 @@ export const CustomContractForm: React.FC = ({ address: coreContractAddress, chain: walletChain, }); - console.log("core contract", coreContract); if (isSuperchainInterop && moduleDeployData) { await Promise.allSettled( @@ -501,7 +498,6 @@ export const CustomContractForm: React.FC = ({ Object.values(m.initializeParams), ); } - console.log("module install params", moduleInstallParams); const installTransaction = installPublishedModule({ contract: coreContract, @@ -511,13 +507,11 @@ export const CustomContractForm: React.FC = ({ version: m.deployMetadata.version, moduleData, }); - console.log("install transaction", installTransaction); const txResult = await sendTransaction({ transaction: installTransaction, account: activeAccount, }); - console.log("tx result", txResult); return await waitForReceipt(txResult); }), diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index a22643b4229..90cb042874b 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -47,13 +47,11 @@ export function prepareAutoFactoryDeployTransaction( if (!implementation) { throw new Error("initializeTransaction must have a 'to' field set"); } - const asd = { + return { data: await encode(args.initializeTransaction), implementation, salt, } as const; - console.error("core contract deploy params: ", asd); - return asd; }, }); } @@ -151,7 +149,6 @@ export async function deployViaAutoFactoryWithImplementationParams( implementation: implementationAddress, salt: parsedSalt, }; - console.error("cross chain contract deploy params: ", asd); const tx = deployProxyByImplementation(asd); const receipt = await sendAndConfirmTransaction({ transaction: tx, From 051028bca25a7ad264a99eb7ef5d81957b8b7f88 Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 6 Dec 2024 17:24:19 -0500 Subject: [PATCH 10/37] fixing liniting issues --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 86befcbc4e6..45306bc64a4 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -53,7 +53,7 @@ import { padHex, } from "thirdweb/utils"; -export type CrossChain = { +type CrossChain = { id: number; network: string; chainId: number; @@ -118,8 +118,6 @@ export function DataTable({ throw new Error("No active account"); } - // TODO: deploy the core contract directly with the initializer calldata - const chain = defineChain(chainId); const client = getThirdwebClient(); const salt = concatHex(["0x0101", padHex("0x", { size: 30 })]).toString(); From 97e6b994db7ba94aebb794efb52710c3438344ea Mon Sep 17 00:00:00 2001 From: Stanley Date: Fri, 6 Dec 2024 17:35:26 -0500 Subject: [PATCH 11/37] fixed linting issues --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 1 + .../(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 45306bc64a4..91df19b6930 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -118,6 +118,7 @@ export function DataTable({ throw new Error("No active account"); } + // eslint-disable-next-line no-restricted-syntax const chain = defineChain(chainId); const client = getThirdwebClient(); const salt = concatHex(["0x0101", padHex("0x", { size: 30 })]).toString(); diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index 215be1e839d..adfbac86b64 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -62,6 +62,7 @@ export default async function Page(props: { const chainsDeployedOn = await Promise.all( topOPStackTestnetChainIds.map(async (chainId) => { + // eslint-disable-next-line no-restricted-syntax const chain = defineChain(chainId); const chainMetadata = await getChainMetadata(chain); From 55158b4330ffb3782f47bb04463b7e2aa8909e00 Mon Sep 17 00:00:00 2001 From: Stanley Date: Mon, 9 Dec 2024 14:42:40 -0500 Subject: [PATCH 12/37] created isSuperchain and isCrosschain flag in favour of case switch statement --- .../cross-chain/data-table.tsx | 160 +++++++++--------- .../contract-deploy-form/custom-contract.tsx | 1 + .../extensions/prebuilts/deploy-published.ts | 86 ++++------ 3 files changed, 118 insertions(+), 129 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 91df19b6930..10aa67890a7 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -135,14 +135,16 @@ export function DataTable({ deployStatusModal.setViewContractLink(""); deployStatusModal.open(steps); + const isCrosschain = !!modulesMetadata?.find( + (m) => m.name === "SuperChainInterop", + ); + const crosschainContractAddress = await deployContractfromDeployMetadata({ account: activeAccount, chain, client, - deployMetadata: { - ...coreMetadata, - deployType: "crosschain" as const, - }, + deployMetadata: coreMetadata, + isCrosschain, initializeData, salt, }); @@ -153,93 +155,95 @@ export function DataTable({ client, }); - const owner = await readContract({ - contract: coreContract, - method: "function owner() view returns (address)", - params: [], - }); + if (isCrosschain) { + const owner = await readContract({ + contract: coreContract, + method: "function owner() view returns (address)", + params: [], + }); - const moduleInitializeParams = modulesMetadata.reduce( - (acc, mod) => { - const params = getModuleInstallParams(mod); - const paramNames = params - .map((param) => param.name) - .filter((p) => p !== undefined); - const returnVal: Record = {}; + const moduleInitializeParams = modulesMetadata.reduce( + (acc, mod) => { + const params = getModuleInstallParams(mod); + const paramNames = params + .map((param) => param.name) + .filter((p) => p !== undefined); + const returnVal: Record = {}; - // set connected wallet address as default "royaltyRecipient" - if (showRoyaltyFieldset(paramNames)) { - returnVal.royaltyRecipient = owner || ""; - returnVal.royaltyBps = "0"; - returnVal.transferValidator = ZERO_ADDRESS; - } + // set connected wallet address as default "royaltyRecipient" + if (showRoyaltyFieldset(paramNames)) { + returnVal.royaltyRecipient = owner || ""; + returnVal.royaltyBps = "0"; + returnVal.transferValidator = ZERO_ADDRESS; + } - // set connected wallet address as default "primarySaleRecipient" - else if (showPrimarySaleFiedset(paramNames)) { - returnVal.primarySaleRecipient = owner || ""; - } + // set connected wallet address as default "primarySaleRecipient" + else if (showPrimarySaleFiedset(paramNames)) { + returnVal.primarySaleRecipient = owner || ""; + } - // set superchain bridge address - else if (showSuperchainBridgeFieldset(paramNames)) { - returnVal.superchainBridge = - "0x4200000000000000000000000000000000000010"; // OP Superchain Bridge - } + // set superchain bridge address + else if (showSuperchainBridgeFieldset(paramNames)) { + returnVal.superchainBridge = + "0x4200000000000000000000000000000000000010"; // OP Superchain Bridge + } - acc[mod.name] = returnVal; - return acc; - }, - {} as Record>, - ); + acc[mod.name] = returnVal; + return acc; + }, + {} as Record>, + ); - const moduleDeployData = modulesMetadata.map((m) => ({ - deployMetadata: m, - initializeParams: moduleInitializeParams[m.name], - })); + const moduleDeployData = modulesMetadata.map((m) => ({ + deployMetadata: m, + initializeParams: moduleInitializeParams[m.name], + })); - const contract = getContract({ - address: crosschainContractAddress, - chain, - client, - }); + const contract = getContract({ + address: crosschainContractAddress, + chain, + client, + }); - await Promise.all( - moduleDeployData.map(async (m) => { - let moduleData: `0x${string}` | undefined; + await Promise.all( + moduleDeployData.map(async (m) => { + let moduleData: `0x${string}` | undefined; - const moduleInstallParams = m.deployMetadata.abi.find( - (abiType) => - (abiType as AbiFunction).name === "encodeBytesOnInstall", - ) as AbiFunction | undefined; + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; - if (m.initializeParams && moduleInstallParams) { - moduleData = encodeAbiParameters( - ( - moduleInstallParams.inputs as { name: string; type: string }[] - ).map((p) => ({ - name: p.name, - type: p.type, - })), - Object.values(m.initializeParams), - ); - } + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } - const installTransaction = installPublishedModule({ - contract, - account: activeAccount, - moduleName: m.deployMetadata.name, - publisher: m.deployMetadata.publisher, - version: m.deployMetadata.version, - moduleData, - }); + const installTransaction = installPublishedModule({ + contract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + }); - const txResult = await sendTransaction({ - transaction: installTransaction, - account: activeAccount, - }); + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); - return await waitForReceipt(txResult); - }), - ); + return await waitForReceipt(txResult); + }), + ); + } deployStatusModal.nextStep(); deployStatusModal.setViewContractLink( diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 47898655363..23a742e6d52 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -466,6 +466,7 @@ export const CustomContractForm: React.FC = ({ deployMetadata: metadata, initializeParams, salt, + isSuperchainInterop, modules: isSuperchainInterop ? // remove modules for superchain interop in order to deploy deterministically deploy just the core contract [] diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 8410b6ceec5..a0b03269ad9 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -123,6 +123,8 @@ export type DeployContractfromDeployMetadataOptions = { initializeParams?: Record; initializeData?: `0x${string}`; implementationConstructorParams?: Record; + isSuperchainInterop?: boolean; + isCrosschain?: boolean; modules?: { deployMetadata: FetchDeployMetadataResult; initializeParams?: Record; @@ -143,6 +145,8 @@ export async function deployContractfromDeployMetadata( initializeParams, initializeData, deployMetadata, + isSuperchainInterop, // TODO: Remove this once the updated Clone Factory has been published + isCrosschain, implementationConstructorParams, modules, salt, @@ -166,7 +170,7 @@ export async function deployContractfromDeployMetadata( import("../../contract/deployment/deploy-via-autofactory.js"), import("../../contract/deployment/utils/bootstrap.js"), ]); - const { cloneFactoryContract: _, implementationContract } = + const { cloneFactoryContract, implementationContract } = await getOrDeployInfraForPublishedContract({ chain, client, @@ -181,15 +185,9 @@ export async function deployContractfromDeployMetadata( publisher: deployMetadata.publisher, }); - const initializeTransaction = await getInitializeTransaction({ - client, - chain, - deployMetadata, - implementationContract, - initializeParams, - account, - modules, - }); + console.error("is superchain interop", isSuperchainInterop); + console.error("is crosschain: ", isCrosschain); + console.error("initialize data: ", initializeData); // TODO: remove this once the modified version of TWCloneFactory // has been published under the thirdweb wallet @@ -199,51 +197,37 @@ export async function deployContractfromDeployMetadata( chain, }); - return await deployViaAutoFactory({ - client, - chain, - account, - cloneFactoryContract: modifiedCloneFactoryContract, - initializeTransaction, - salt, - }); - } - case "crosschain": { - const { getOrDeployInfraForPublishedContract } = await import( - "../../contract/deployment/utils/bootstrap.js" - ); - const { cloneFactoryContract: _, implementationContract } = - await getOrDeployInfraForPublishedContract({ + if (isCrosschain) { + return await deployViaAutoFactoryWithImplementationParams({ + client, chain, + account, + cloneFactoryContract: modifiedCloneFactoryContract, + implementationAddress: implementationContract.address, + initializeData, + salt, + }); + } else { + const initializeTransaction = await getInitializeTransaction({ client, + chain, + deployMetadata, + implementationContract, + initializeParams, account, - contractId: deployMetadata.name, - constructorParams: - implementationConstructorParams || - (await getAllDefaultConstructorParamsForImplementation({ - chain, - client, - })), - publisher: deployMetadata.publisher, + modules, }); - - // TODO: remove this once the modified version of TWCloneFactory - // has been published under the thirdweb wallet - const modifiedCloneFactoryContract = getContract({ - client, - address: "0xB83db4b940e4796aA1f53DBFC824B9B1865835D5", // only deployed on OP and zora testnets - chain, - }); - - return await deployViaAutoFactoryWithImplementationParams({ - client, - chain, - account, - cloneFactoryContract: modifiedCloneFactoryContract, - implementationAddress: implementationContract.address, - initializeData, - salt, - }); + return await deployViaAutoFactory({ + client, + chain, + account, + cloneFactoryContract: isSuperchainInterop // TODO: remove this once the updated clone factory is publsihed + ? modifiedCloneFactoryContract + : cloneFactoryContract, + initializeTransaction, + salt, + }); + } } case "customFactory": { if (!deployMetadata?.factoryDeploymentData?.customFactoryInput) { From 67db04dd83b5f3dc77c50ff58ceacb41f7fddd8e Mon Sep 17 00:00:00 2001 From: Stanley Date: Mon, 9 Dec 2024 22:30:26 -0500 Subject: [PATCH 13/37] god bless greg it works --- .../cross-chain/data-table.tsx | 77 ++++++++++------- .../contract-deploy-form/custom-contract.tsx | 85 +++++++++++-------- .../modules/common/installPublishedModule.ts | 5 ++ .../extensions/prebuilts/deploy-published.ts | 2 +- 4 files changed, 98 insertions(+), 71 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 10aa67890a7..2366c6850b6 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -34,7 +34,9 @@ import { useTxNotifications } from "hooks/useTxNotifications"; import { ZERO_ADDRESS, defineChain, + eth_getTransactionCount, getContract, + getRpcClient, readContract, sendTransaction, waitForReceipt, @@ -205,44 +207,53 @@ export function DataTable({ client, }); - await Promise.all( - moduleDeployData.map(async (m) => { - let moduleData: `0x${string}` | undefined; + const rpcRequest = getRpcClient({ + client, + chain, + }); + const currentNonce = await eth_getTransactionCount(rpcRequest, { + address: activeAccount.address, + }); - const moduleInstallParams = m.deployMetadata.abi.find( - (abiType) => - (abiType as AbiFunction).name === "encodeBytesOnInstall", - ) as AbiFunction | undefined; + for (const [i, m] of moduleDeployData.entries()) { + let moduleData: `0x${string}` | undefined; - if (m.initializeParams && moduleInstallParams) { - moduleData = encodeAbiParameters( - ( - moduleInstallParams.inputs as { name: string; type: string }[] - ).map((p) => ({ - name: p.name, - type: p.type, - })), - Object.values(m.initializeParams), - ); - } + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; - const installTransaction = installPublishedModule({ - contract, - account: activeAccount, - moduleName: m.deployMetadata.name, - publisher: m.deployMetadata.publisher, - version: m.deployMetadata.version, - moduleData, - }); + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } - const txResult = await sendTransaction({ - transaction: installTransaction, - account: activeAccount, - }); + const installTransaction = installPublishedModule({ + contract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + nonce: currentNonce + i, + }); - return await waitForReceipt(txResult); - }), - ); + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); + + await waitForReceipt(txResult); + // can't handle parallel transactions, so wait a bit + await new Promise((resolve) => setTimeout(resolve, 1000)); + } } deployStatusModal.nextStep(); diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index 23a742e6d52..e574f2b6f08 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -28,7 +28,9 @@ import { useCallback, useMemo } from "react"; import { FormProvider, type UseFormReturn, useForm } from "react-hook-form"; import { ZERO_ADDRESS, + eth_getTransactionCount, getContract, + getRpcClient, sendTransaction, waitForReceipt, } from "thirdweb"; @@ -478,45 +480,54 @@ export const CustomContractForm: React.FC = ({ chain: walletChain, }); - if (isSuperchainInterop && moduleDeployData) { - await Promise.allSettled( - moduleDeployData.map(async (m) => { - let moduleData: `0x${string}` | undefined; - - const moduleInstallParams = m.deployMetadata.abi.find( - (abiType) => - (abiType as AbiFunction).name === "encodeBytesOnInstall", - ) as AbiFunction | undefined; - - if (m.initializeParams && moduleInstallParams) { - moduleData = encodeAbiParameters( - ( - moduleInstallParams.inputs as { name: string; type: string }[] - ).map((p) => ({ - name: p.name, - type: p.type, - })), - Object.values(m.initializeParams), - ); - } - - const installTransaction = installPublishedModule({ - contract: coreContract, - account: activeAccount, - moduleName: m.deployMetadata.name, - publisher: m.deployMetadata.publisher, - version: m.deployMetadata.version, - moduleData, - }); + const rpcRequest = getRpcClient({ + client: thirdwebClient, + chain: walletChain, + }); + const currentNonce = await eth_getTransactionCount(rpcRequest, { + address: activeAccount.address, + }); - const txResult = await sendTransaction({ - transaction: installTransaction, - account: activeAccount, - }); + if (isSuperchainInterop && moduleDeployData) { + for (const [i, m] of moduleDeployData.entries()) { + let moduleData: `0x${string}` | undefined; + + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; + + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } - return await waitForReceipt(txResult); - }), - ); + const installTransaction = installPublishedModule({ + contract: coreContract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + nonce: currentNonce + i, + }); + + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); + + await waitForReceipt(txResult); + // can't handle parallel transactions, so wait a bit + await new Promise((resolve) => setTimeout(resolve, 1000)); + } } return coreContractAddress; diff --git a/packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts b/packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts index 0d43f338e81..ff022357c1d 100644 --- a/packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts +++ b/packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts @@ -14,6 +14,7 @@ export type InstallPublishedModuleOptions = { version?: string; constructorParams?: Record; moduleData?: `0x${string}`; + nonce?: number; }; /** @@ -43,10 +44,14 @@ export function installPublishedModule(options: InstallPublishedModuleOptions) { constructorParams, publisher, moduleData, + nonce, } = options; return installModule({ contract, + overrides: { + nonce, + }, asyncParams: async () => { const deployedModule = await getOrDeployInfraForPublishedContract({ chain: contract.chain, diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index a0b03269ad9..ddcc0a8ee13 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -217,7 +217,7 @@ export async function deployContractfromDeployMetadata( account, modules, }); - return await deployViaAutoFactory({ + return deployViaAutoFactory({ client, chain, account, From 828c198443d75eaa60f40a396379aa9e497c9f09 Mon Sep 17 00:00:00 2001 From: Stanley Date: Tue, 10 Dec 2024 21:47:23 -0500 Subject: [PATCH 14/37] addressed comments in PR --- .../contract-deploy-form/custom-contract.tsx | 26 ++++++++++++------- .../deployment/deploy-via-autofactory.ts | 19 +++++++++----- .../extensions/prebuilts/deploy-published.ts | 4 --- .../IContractFactory/events/ProxyDeployed.ts | 10 +++++++ .../actions/send-and-confirm-transaction.ts | 4 +-- .../src/utils/any-evm/deploy-metadata.ts | 2 +- 6 files changed, 41 insertions(+), 24 deletions(-) diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index e574f2b6f08..a892c03862c 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -458,7 +458,10 @@ export const CustomContractForm: React.FC = ({ const moduleDeployData = modules?.map((m) => ({ deployMetadata: m, - initializeParams: params.moduleData[m.name], + initializeParams: + m.name === "SuperChainInterop" + ? { superchainBridge: "0x4200000000000000000000000000000000000010" } + : params.moduleData[m.name], })); const coreContractAddress = await deployContractfromDeployMetadata({ @@ -480,15 +483,15 @@ export const CustomContractForm: React.FC = ({ chain: walletChain, }); - const rpcRequest = getRpcClient({ - client: thirdwebClient, - chain: walletChain, - }); - const currentNonce = await eth_getTransactionCount(rpcRequest, { - address: activeAccount.address, - }); - if (isSuperchainInterop && moduleDeployData) { + const rpcRequest = getRpcClient({ + client: thirdwebClient, + chain: walletChain, + }); + const currentNonce = await eth_getTransactionCount(rpcRequest, { + address: activeAccount.address, + }); + for (const [i, m] of moduleDeployData.entries()) { let moduleData: `0x${string}` | undefined; @@ -849,7 +852,10 @@ export const CustomContractForm: React.FC = ({ {isModular && modules && modules.length > 0 && ( mod.name !== "SuperChainInterop", + )} isTWPublisher={isTWPublisher} /> )} diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index 90cb042874b..ce2a35405e2 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -1,5 +1,8 @@ import { parseEventLogs } from "../../event/actions/parse-logs.js"; -import { proxyDeployedEvent } from "../../extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.js"; +import { + modifiedProxyDeployedEvent, + proxyDeployedEvent, +} from "../../extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.js"; import { deployProxyByImplementation } from "../../extensions/thirdweb/__generated__/IContractFactory/write/deployProxyByImplementation.js"; import { eth_blockNumber } from "../../rpc/actions/eth_blockNumber.js"; import { getRpcClient } from "../../rpc/rpc.js"; @@ -97,8 +100,13 @@ export async function deployViaAutoFactory( transaction: tx, account, }); + + // TODO: remove this once the modified version of TWCloneFactory has been published + const proxyEvent = salt?.startsWith("0x0101") + ? modifiedProxyDeployedEvent() + : proxyDeployedEvent(); const decodedEvent = parseEventLogs({ - events: [proxyDeployedEvent()], + events: [proxyEvent], logs: receipt.logs, }); if (decodedEvent.length === 0 || !decodedEvent[0]) { @@ -143,19 +151,18 @@ export async function deployViaAutoFactoryWithImplementationParams( size: 32, }); - const asd = { + const tx = deployProxyByImplementation({ contract: cloneFactoryContract, data: initializeData || "0x", implementation: implementationAddress, salt: parsedSalt, - }; - const tx = deployProxyByImplementation(asd); + }); const receipt = await sendAndConfirmTransaction({ transaction: tx, account, }); const decodedEvent = parseEventLogs({ - events: [proxyDeployedEvent()], + events: [modifiedProxyDeployedEvent()], logs: receipt.logs, }); if (decodedEvent.length === 0 || !decodedEvent[0]) { diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index ddcc0a8ee13..51aa12fd861 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -185,10 +185,6 @@ export async function deployContractfromDeployMetadata( publisher: deployMetadata.publisher, }); - console.error("is superchain interop", isSuperchainInterop); - console.error("is crosschain: ", isCrosschain); - console.error("initialize data: ", initializeData); - // TODO: remove this once the modified version of TWCloneFactory // has been published under the thirdweb wallet const modifiedCloneFactoryContract = getContract({ diff --git a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts index d966b2afc25..e9c7d7546fa 100644 --- a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts +++ b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts @@ -39,6 +39,16 @@ export type ProxyDeployedEventFilters = Partial<{ * ``` */ export function proxyDeployedEvent(filters: ProxyDeployedEventFilters = {}) { + return prepareEvent({ + signature: + "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer)", + filters, + }); +} + + +// TODO: remove this once the modified version of TWCloneFactory has been published +export function modifiedProxyDeployedEvent(filters: ProxyDeployedEventFilters = {}) { return prepareEvent({ signature: "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", diff --git a/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts b/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts index 69368820d72..68d635afd53 100644 --- a/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts +++ b/packages/thirdweb/src/transaction/actions/send-and-confirm-transaction.ts @@ -53,7 +53,5 @@ export async function sendAndConfirmTransaction( options: SendTransactionOptions, ): Promise { const submittedTx = await sendTransaction(options); - const receipt = await waitForReceipt(submittedTx); - - return receipt; + return waitForReceipt(submittedTx); } diff --git a/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts b/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts index af30d8c9c1a..365d9b882e0 100644 --- a/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts +++ b/packages/thirdweb/src/utils/any-evm/deploy-metadata.ts @@ -207,7 +207,7 @@ export type ExtendedMetadata = { factoryAddresses?: Record; } | undefined; - deployType?: "standard" | "autoFactory" | "customFactory" | "crosschain"; + deployType?: "standard" | "autoFactory" | "customFactory"; routerType?: "none" | "plugin" | "dynamic" | "modular"; networksForDeployment?: { allNetworks?: boolean; From 429d10c95a1911b2ad7914b8fd17ae82ee922e52 Mon Sep 17 00:00:00 2001 From: Stanley Date: Wed, 11 Dec 2024 12:20:18 -0500 Subject: [PATCH 15/37] updated wording --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 2 ++ .../contract-deploy-form/custom-contract.tsx | 2 ++ apps/dashboard/src/data/explore.ts | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 2366c6850b6..74f594d8f6b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -235,6 +235,8 @@ export function DataTable({ ); } + console.log("nonce used: ", currentNonce + i); + const installTransaction = installPublishedModule({ contract, account: activeAccount, diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index a892c03862c..e07056abfb3 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -512,6 +512,8 @@ export const CustomContractForm: React.FC = ({ ); } + console.log("nonce used: ", currentNonce + i); + const installTransaction = installPublishedModule({ contract: coreContract, account: activeAccount, diff --git a/apps/dashboard/src/data/explore.ts b/apps/dashboard/src/data/explore.ts index a75809aefd8..f2dcabbfa5d 100644 --- a/apps/dashboard/src/data/explore.ts +++ b/apps/dashboard/src/data/explore.ts @@ -207,8 +207,9 @@ const CROSS_CHAIN = { "deployer.thirdweb.eth/ClaimableERC20", ], { - title: "Modular Token Drop", - description: "ERC20 Tokens that others can mint.", + title: "OP Superchain Modular Token Drop", + description: + "ERC-20 Tokens crosschain compatible across OP Superchains", }, ], ], From 71b8a975b7faa3e3d7fd38dc2d5c96e3f1eb6391 Mon Sep 17 00:00:00 2001 From: Stanley Date: Wed, 11 Dec 2024 17:36:29 -0500 Subject: [PATCH 16/37] updated superchain bridge address --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 2 +- .../contract-deploy-form/custom-contract.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 74f594d8f6b..cf814e944aa 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -187,7 +187,7 @@ export function DataTable({ // set superchain bridge address else if (showSuperchainBridgeFieldset(paramNames)) { returnVal.superchainBridge = - "0x4200000000000000000000000000000000000010"; // OP Superchain Bridge + "0x4200000000000000000000000000000000000028"; // OP Superchain Bridge } acc[mod.name] = returnVal; diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx index e07056abfb3..4565da21471 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/custom-contract.tsx @@ -460,7 +460,7 @@ export const CustomContractForm: React.FC = ({ deployMetadata: m, initializeParams: m.name === "SuperChainInterop" - ? { superchainBridge: "0x4200000000000000000000000000000000000010" } + ? { superchainBridge: "0x4200000000000000000000000000000000000028" } : params.moduleData[m.name], })); From 1a7f7cb0e4ca5ce962eb54a97113a1c52209ed9e Mon Sep 17 00:00:00 2001 From: Stanley Date: Sat, 14 Dec 2024 13:42:29 -0500 Subject: [PATCH 17/37] updated to show links --- apps/dashboard/next-env.d.ts | 2 +- .../[contractAddress]/cross-chain/data-table.tsx | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/dashboard/next-env.d.ts b/apps/dashboard/next-env.d.ts index 3cd7048ed94..725dd6f2451 100644 --- a/apps/dashboard/next-env.d.ts +++ b/apps/dashboard/next-env.d.ts @@ -3,4 +3,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index cf814e944aa..fa30e208c24 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -31,6 +31,7 @@ import { showSuperchainBridgeFieldset, } from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; import { useTxNotifications } from "hooks/useTxNotifications"; +import Link from "next/link"; import { ZERO_ADDRESS, defineChain, @@ -87,6 +88,20 @@ export function DataTable({ { accessorKey: "network", header: "Network", + cell: ({ row }) => { + if (row.getValue("status") === "DEPLOYED") { + return ( + + {row.getValue("network")} + + ); + } + return row.getValue("network"); + }, }, { accessorKey: "chainId", From ec1a0a1bfa6e025baca350bcf8a38e59d2d1dc9d Mon Sep 17 00:00:00 2001 From: Stanley Date: Mon, 16 Dec 2024 20:08:12 -0700 Subject: [PATCH 18/37] implemented cross chain transfers --- .../cross-chain/data-table.tsx | 204 ++++++++++++++---- 1 file changed, 163 insertions(+), 41 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index fa30e208c24..c07e9ca6e7a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -1,7 +1,14 @@ "use client"; -import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { Table, TableBody, @@ -12,6 +19,8 @@ import { TableRow, } from "@/components/ui/table"; import { getThirdwebClient } from "@/constants/thirdweb.server"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useMutation } from "@tanstack/react-query"; import { type ColumnDef, flexRender, @@ -32,13 +41,16 @@ import { } from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; import { useTxNotifications } from "hooks/useTxNotifications"; import Link from "next/link"; +import { useForm } from "react-hook-form"; import { ZERO_ADDRESS, defineChain, eth_getTransactionCount, getContract, getRpcClient, + prepareContractCall, readContract, + sendAndConfirmTransaction, sendTransaction, waitForReceipt, } from "thirdweb"; @@ -55,6 +67,7 @@ import { encodeAbiParameters, padHex, } from "thirdweb/utils"; +import { z } from "zod"; type CrossChain = { id: number; @@ -63,6 +76,22 @@ type CrossChain = { status: "DEPLOYED" | "NOT_DEPLOYED"; }; +type ChainId = "84532" | "11155420" | "919" | "111557560" | "999999999"; + +const formSchema = z.object({ + amounts: z.object({ + "84532": z.string(), + "11155420": z.string(), + "919": z.string(), + "111557560": z.string(), + "999999999": z.string(), + }), +}); +type FormSchema = z.output; + +const positiveIntegerRegex = /^[0-9]\d*$/; +const superchainBridgeAddress = "0x4200000000000000000000000000000000000028"; + export function DataTable({ data, coreMetadata, @@ -84,6 +113,64 @@ export function DataTable({ "Failed to deploy contract", ); + const form = useForm({ + resolver: zodResolver(formSchema), + values: { + amounts: { + "84532": "", // Base + "11155420": "", // OP testnet + "919": "", // Mode Network + "111557560": "", // Cyber + "999999999": "", // Zora + }, + }, + }); + + const crossChainTransfer = async (chainId: ChainId) => { + if (!activeAccount) { + throw new Error("Account not connected"); + } + const amount = form.getValues().amounts[chainId]; + if (!positiveIntegerRegex.test(amount)) { + form.setError(`amounts.${chainId}`, { message: "Invalid Amount" }); + return; + } + + const superChainBridge = getContract({ + address: superchainBridgeAddress, + chain: coreContract.chain, + client: coreContract.client, + }); + + const sendErc20Tx = prepareContractCall({ + contract: superChainBridge, + method: + "function sendERC20(address _token, address _to, uint256 _amount, uint256 _chainId)", + params: [ + coreContract.address, + activeAccount.address, + BigInt(amount), + BigInt(chainId), + ], + }); + + await sendAndConfirmTransaction({ + account: activeAccount, + transaction: sendErc20Tx, + }); + }; + + const crossChainTransferNotifications = useTxNotifications( + "Successfully submitted cross chain transfer", + "Failed to submit cross chain transfer", + ); + + const crossChainTransferMutation = useMutation({ + mutationFn: crossChainTransfer, + onSuccess: crossChainTransferNotifications.onSuccess, + onError: crossChainTransferNotifications.onError, + }); + const columns: ColumnDef[] = [ { accessorKey: "network", @@ -112,10 +199,40 @@ export function DataTable({ header: "Status", cell: ({ row }) => { if (row.getValue("status") === "DEPLOYED") { - return Deployed; + return ( + ( + + +
+ + +
+
+ +
+ )} + /> + ); } return ( - ); @@ -250,8 +367,6 @@ export function DataTable({ ); } - console.log("nonce used: ", currentNonce + i); - const installTransaction = installPublishedModule({ contract, account: activeAccount, @@ -285,42 +400,49 @@ export function DataTable({ }; return ( - - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} - - ))} - - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - +
+ + +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} + + ))} + ))} - - ))} - -
- -
+ + + + + + ); } From 52c3f4453bae4642f0f48a659278e45aa7173797 Mon Sep 17 00:00:00 2001 From: Stanley Date: Wed, 18 Dec 2024 07:01:22 +0900 Subject: [PATCH 19/37] tweaked input + button on crosschain transfer for OP Interop --- .../cross-chain/data-table.tsx | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index c07e9ca6e7a..ceddff9897a 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -18,6 +18,7 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; +import { ToolTipLabel } from "@/components/ui/tooltip"; import { getThirdwebClient } from "@/constants/thirdweb.server"; import { zodResolver } from "@hookform/resolvers/zod"; import { useMutation } from "@tanstack/react-query"; @@ -207,20 +208,27 @@ export function DataTable({ render={({ field }) => ( -
- - -
+ +
+ + +
+
From 624df2169bf42782c3f70fdcfa55b78a4d075680 Mon Sep 17 00:00:00 2001 From: Yash Date: Tue, 14 Jan 2025 21:34:04 +0530 Subject: [PATCH 20/37] remove deployViaAutoFactoryWithImplementationParams, modify deployViaAutoFactory --- .../deployment/deploy-via-autofactory.ts | 94 +++++++------------ .../extensions/prebuilts/deploy-published.ts | 43 +++++---- 2 files changed, 57 insertions(+), 80 deletions(-) diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index ce2a35405e2..3d18c5873cc 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -26,7 +26,10 @@ import { zkDeployProxy } from "./zksync/zkDeployProxy.js"; export function prepareAutoFactoryDeployTransaction( args: ClientAndChain & { cloneFactoryContract: ThirdwebContract; - initializeTransaction: PreparedTransaction; + initializeTransaction?: PreparedTransaction; + initializeData?: `0x${string}`; + implementationAddress?: string; + isCrosschain?: boolean; salt?: string; }, ) { @@ -44,6 +47,25 @@ export function prepareAutoFactoryDeployTransaction( : toHex(blockNumber, { size: 32, }); + + if (args.isCrosschain) { + if (!args.initializeData || !args.implementationAddress) { + throw new Error( + "initializeData or implementationAddress can't be undefined", + ); + } + + return { + data: args.initializeData, + implementation: args.implementationAddress, + salt, + } as const; + } + + if (!args.initializeTransaction) { + throw new Error("initializeTransaction can't be undefined"); + } + const implementation = await resolvePromisedValue( args.initializeTransaction.to, ); @@ -65,7 +87,10 @@ export function prepareAutoFactoryDeployTransaction( export async function deployViaAutoFactory( options: ClientAndChainAndAccount & { cloneFactoryContract: ThirdwebContract; - initializeTransaction: PreparedTransaction; + initializeTransaction?: PreparedTransaction; + initializeData?: `0x${string}`; + implementationAddress?: string; + isCrosschain?: boolean; salt?: string; }, ): Promise { @@ -75,10 +100,16 @@ export async function deployViaAutoFactory( account, cloneFactoryContract, initializeTransaction, + initializeData, + implementationAddress, + isCrosschain, salt, } = options; if (await isZkSyncChain(chain)) { + if (!initializeTransaction) { + throw new Error("initializeTransaction can't be undefined"); + } return zkDeployProxy({ chain, client, @@ -94,6 +125,9 @@ export async function deployViaAutoFactory( client, cloneFactoryContract, initializeTransaction, + initializeData, + implementationAddress, + isCrosschain, salt, }); const receipt = await sendAndConfirmTransaction({ @@ -116,59 +150,3 @@ export async function deployViaAutoFactory( } return decodedEvent[0]?.args.proxy; } - -/** - * @internal - */ -export async function deployViaAutoFactoryWithImplementationParams( - options: ClientAndChainAndAccount & { - cloneFactoryContract: ThirdwebContract; - initializeData?: `0x${string}`; - implementationAddress: string; - salt?: string; - }, -): Promise { - const { - client, - chain, - account, - cloneFactoryContract, - initializeData, - implementationAddress, - salt, - } = options; - - const rpcRequest = getRpcClient({ - client, - chain, - }); - const blockNumber = await eth_blockNumber(rpcRequest); - const parsedSalt = salt - ? salt.startsWith("0x") && salt.length === 66 - ? (salt as `0x${string}`) - : keccakId(salt) - : toHex(blockNumber, { - size: 32, - }); - - const tx = deployProxyByImplementation({ - contract: cloneFactoryContract, - data: initializeData || "0x", - implementation: implementationAddress, - salt: parsedSalt, - }); - const receipt = await sendAndConfirmTransaction({ - transaction: tx, - account, - }); - const decodedEvent = parseEventLogs({ - events: [modifiedProxyDeployedEvent()], - logs: receipt.logs, - }); - if (decodedEvent.length === 0 || !decodedEvent[0]) { - throw new Error( - `No ProxyDeployed event found in transaction: ${receipt.transactionHash}`, - ); - } - return decodedEvent[0]?.args.proxy; -} diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 3086d5a25e4..15500d25293 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -2,7 +2,6 @@ import type { AbiFunction } from "abitype"; import type { Chain } from "../../chains/types.js"; import type { ThirdwebClient } from "../../client/client.js"; import { type ThirdwebContract, getContract } from "../../contract/contract.js"; -import { deployViaAutoFactoryWithImplementationParams } from "../../contract/deployment/deploy-via-autofactory.js"; import { fetchPublishedContractMetadata } from "../../contract/deployment/publisher.js"; import { getOrDeployInfraContractFromMetadata } from "../../contract/deployment/utils/bootstrap.js"; import { sendAndConfirmTransaction } from "../../transaction/actions/send-and-confirm-transaction.js"; @@ -233,7 +232,7 @@ export async function deployContractfromDeployMetadata( }); if (isCrosschain) { - return await deployViaAutoFactoryWithImplementationParams({ + return deployViaAutoFactory({ client, chain, account, @@ -242,27 +241,27 @@ export async function deployContractfromDeployMetadata( initializeData, salt, }); - } else { - const initializeTransaction = await getInitializeTransaction({ - client, - chain, - deployMetadata, - implementationContract, - initializeParams, - account, - modules, - }); - return deployViaAutoFactory({ - client, - chain, - account, - cloneFactoryContract: isSuperchainInterop // TODO: remove this once the updated clone factory is publsihed - ? modifiedCloneFactoryContract - : cloneFactoryContract, - initializeTransaction, - salt, - }); } + + const initializeTransaction = await getInitializeTransaction({ + client, + chain, + deployMetadata, + implementationContract, + initializeParams: processedInitializeParams, + account, + modules, + }); + return deployViaAutoFactory({ + client, + chain, + account, + cloneFactoryContract: isSuperchainInterop // TODO: remove this once the updated clone factory is publsihed + ? modifiedCloneFactoryContract + : cloneFactoryContract, + initializeTransaction, + salt, + }); } case "customFactory": { if (!deployMetadata?.factoryDeploymentData?.customFactoryInput) { From fba275c5fc3e51738ec1757ff773a0f23462e744 Mon Sep 17 00:00:00 2001 From: Yash Date: Tue, 14 Jan 2025 21:42:13 +0530 Subject: [PATCH 21/37] fix import --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index ceddff9897a..0487958a0e2 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -36,7 +36,7 @@ import { } from "components/contract-components/contract-deploy-form/deploy-context-modal"; import { getModuleInstallParams, - showPrimarySaleFiedset, + showPrimarySaleFieldset, showRoyaltyFieldset, showSuperchainBridgeFieldset, } from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; @@ -320,7 +320,7 @@ export function DataTable({ } // set connected wallet address as default "primarySaleRecipient" - else if (showPrimarySaleFiedset(paramNames)) { + else if (showPrimarySaleFieldset(paramNames)) { returnVal.primarySaleRecipient = owner || ""; } From 2b98cc3c230a265650683ac466aa7bc0da6322eb Mon Sep 17 00:00:00 2001 From: Yash Date: Tue, 14 Jan 2025 22:31:08 +0530 Subject: [PATCH 22/37] auto deploy new clone factory --- .../src/contract/deployment/utils/bootstrap.ts | 6 +++++- .../contract/deployment/utils/clone-factory.ts | 1 + .../src/contract/deployment/utils/infra.ts | 5 ++++- .../src/extensions/prebuilts/deploy-published.ts | 15 ++------------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/packages/thirdweb/src/contract/deployment/utils/bootstrap.ts b/packages/thirdweb/src/contract/deployment/utils/bootstrap.ts index a0199c5fe37..f796427d624 100644 --- a/packages/thirdweb/src/contract/deployment/utils/bootstrap.ts +++ b/packages/thirdweb/src/contract/deployment/utils/bootstrap.ts @@ -167,6 +167,7 @@ export async function deployCloneFactory(options: ClientAndChainAndAccount) { ...options, contractId: "TWCloneFactory", constructorParams: { _trustedForwarder: forwarder.address }, + publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", // TODO: use default publisher }); } @@ -218,7 +219,10 @@ export async function getOrDeployInfraContract( const contractMetadata = await fetchPublishedContractMetadata({ client: options.client, contractId: options.contractId, - publisher: options.publisher, + publisher: + options.contractId === "TWCloneFactory" + ? "0x6453a486d52e0EB6E79Ec4491038E2522a926936" // TODO: use default publisher + : options.publisher, version: options.version, }); return getOrDeployInfraContractFromMetadata({ diff --git a/packages/thirdweb/src/contract/deployment/utils/clone-factory.ts b/packages/thirdweb/src/contract/deployment/utils/clone-factory.ts index 48410da5505..3ecd0557426 100644 --- a/packages/thirdweb/src/contract/deployment/utils/clone-factory.ts +++ b/packages/thirdweb/src/contract/deployment/utils/clone-factory.ts @@ -23,6 +23,7 @@ export async function getDeployedCloneFactoryContract(args: ClientAndChain) { ...args, contractId: "TWCloneFactory", constructorParams: { _trustedForwarder: forwarder.address }, + publisher: "0x6453a486d52e0EB6E79Ec4491038E2522a926936", // TODO: use default publisher }); if (!cloneFactory) { return null; diff --git a/packages/thirdweb/src/contract/deployment/utils/infra.ts b/packages/thirdweb/src/contract/deployment/utils/infra.ts index b2f2a5c59e9..495bd844670 100644 --- a/packages/thirdweb/src/contract/deployment/utils/infra.ts +++ b/packages/thirdweb/src/contract/deployment/utils/infra.ts @@ -46,7 +46,10 @@ export async function getDeployedInfraContract( const contractMetadata = await fetchPublishedContractMetadata({ client: options.client, contractId: options.contractId, - publisher: options.publisher, + publisher: + options.contractId === "TWCloneFactory" + ? "0x6453a486d52e0EB6E79Ec4491038E2522a926936" // TODO: use default publisher + : options.publisher, version: options.version, }); return getDeployedInfraContractFromMetadata({ diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 15500d25293..45dadca3cc3 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -149,7 +149,6 @@ export async function deployContractfromDeployMetadata( initializeParams, initializeData, deployMetadata, - isSuperchainInterop, // TODO: Remove this once the updated Clone Factory has been published isCrosschain, implementationConstructorParams, modules, @@ -223,20 +222,12 @@ export async function deployContractfromDeployMetadata( version: deployMetadata.version, }); - // TODO: remove this once the modified version of TWCloneFactory - // has been published under the thirdweb wallet - const modifiedCloneFactoryContract = getContract({ - client, - address: "0xB83db4b940e4796aA1f53DBFC824B9B1865835D5", // only deployed on OP and zora testnets - chain, - }); - if (isCrosschain) { return deployViaAutoFactory({ client, chain, account, - cloneFactoryContract: modifiedCloneFactoryContract, + cloneFactoryContract, implementationAddress: implementationContract.address, initializeData, salt, @@ -256,9 +247,7 @@ export async function deployContractfromDeployMetadata( client, chain, account, - cloneFactoryContract: isSuperchainInterop // TODO: remove this once the updated clone factory is publsihed - ? modifiedCloneFactoryContract - : cloneFactoryContract, + cloneFactoryContract, initializeTransaction, salt, }); From fc59e79a73f92433b5c56cc7944494ff18fd2f45 Mon Sep 17 00:00:00 2001 From: Yash Date: Wed, 15 Jan 2025 05:07:34 +0530 Subject: [PATCH 23/37] remove hardcoded clone factory address --- .../[contractAddress]/cross-chain/page.tsx | 13 ++++++++++--- packages/thirdweb/src/exports/contract.ts | 1 + .../src/extensions/prebuilts/deploy-published.ts | 1 + 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index adfbac86b64..f70c82ae8be 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -2,7 +2,11 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-component import { notFound, redirect } from "next/navigation"; import { getContractEvents, prepareEvent } from "thirdweb"; import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; -import { type FetchDeployMetadataResult, getContract } from "thirdweb/contract"; +import { + type FetchDeployMetadataResult, + getContract, + getDeployedCloneFactoryContract, +} from "thirdweb/contract"; import { getInstalledModules } from "thirdweb/modules"; import { eth_getCode, getRpcClient } from "thirdweb/rpc"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; @@ -114,12 +118,15 @@ export default async function Page(props: { "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", }); - const twCloneFactoryContract = getContract({ - address: "0xB83db4b940e4796aA1f53DBFC824B9B1865835D5", + const twCloneFactoryContract = await getDeployedCloneFactoryContract({ chain: contract.chain, client: contract.client, }); + if (!twCloneFactoryContract) { + throw new Error("Factory not found"); + } + const events = await getContractEvents({ contract: twCloneFactoryContract, events: [ProxyDeployedEvent], diff --git a/packages/thirdweb/src/exports/contract.ts b/packages/thirdweb/src/exports/contract.ts index 74e31db6c7f..cc0d614bb3d 100644 --- a/packages/thirdweb/src/exports/contract.ts +++ b/packages/thirdweb/src/exports/contract.ts @@ -43,3 +43,4 @@ export { prepareAutoFactoryDeployTransaction } from "../contract/deployment/depl export { prepareMethod } from "../utils/abi/prepare-method.js"; export { getCompilerMetadata } from "../contract/actions/get-compiler-metadata.js"; +export { getDeployedCloneFactoryContract } from "../contract/deployment/utils/clone-factory.js"; diff --git a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts index 45dadca3cc3..ad206f57d15 100644 --- a/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts +++ b/packages/thirdweb/src/extensions/prebuilts/deploy-published.ts @@ -231,6 +231,7 @@ export async function deployContractfromDeployMetadata( implementationAddress: implementationContract.address, initializeData, salt, + isCrosschain, }); } From 9e2c32d17b2034c43fafed25525ffcdbfd2f887d Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 16 Jan 2025 02:17:23 +0530 Subject: [PATCH 24/37] enable crosschain deployments for non-modular prebuilts --- .../_utils/getContractPageSidebarLinks.ts | 1 - .../cross-chain/data-table.tsx | 14 +++--- .../[contractAddress]/cross-chain/page.tsx | 44 ++++++++++--------- .../deployment/deploy-via-autofactory.ts | 6 +-- .../IContractFactory/events/ProxyDeployed.ts | 9 ++-- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts index 2281ed405a4..f3a382656c2 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/_utils/getContractPageSidebarLinks.ts @@ -27,7 +27,6 @@ export function getContractPageSidebarLinks(data: { { label: "Cross Chain", href: `${layoutPrefix}/cross-chain`, - hide: !data.metadata.isModularCore, exactMatch: true, }, { diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 0487958a0e2..9d0f46003c1 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -99,12 +99,14 @@ export function DataTable({ coreContract, modulesMetadata, initializeData, + inputSalt, }: { data: CrossChain[]; coreMetadata: FetchDeployMetadataResult; coreContract: ThirdwebContract; - modulesMetadata: FetchDeployMetadataResult[]; + modulesMetadata?: FetchDeployMetadataResult[]; initializeData?: `0x${string}`; + inputSalt?: `0x${string}`; }) { const activeAccount = useActiveAccount(); const switchChain = useSwitchActiveWalletChain(); @@ -263,7 +265,9 @@ export function DataTable({ // eslint-disable-next-line no-restricted-syntax const chain = defineChain(chainId); const client = getThirdwebClient(); - const salt = concatHex(["0x0101", padHex("0x", { size: 30 })]).toString(); + const salt = + inputSalt || + concatHex(["0x0101", padHex("0x", { size: 30 })]).toString(); await switchChain(chain); @@ -286,18 +290,18 @@ export function DataTable({ chain, client, deployMetadata: coreMetadata, - isCrosschain, + isCrosschain: true, initializeData, salt, }); - await verifyContract({ + verifyContract({ address: crosschainContractAddress, chain, client, }); - if (isCrosschain) { + if (isCrosschain && modulesMetadata) { const owner = await readContract({ contract: coreContract, method: "function owner() view returns (address)", diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index f70c82ae8be..13288433a94 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -1,5 +1,5 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy"; -import { notFound, redirect } from "next/navigation"; +import { notFound } from "next/navigation"; import { getContractEvents, prepareEvent } from "thirdweb"; import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; import { @@ -42,10 +42,6 @@ export default async function Page(props: { const { isModularCore } = await getContractPageMetadata(contract); - if (!isModularCore) { - redirect(`/${params.chain_id}/${params.contractAddress}`); - } - const originalCode = await eth_getCode( getRpcClient({ client: contract.client, @@ -90,32 +86,37 @@ export default async function Page(props: { }), ); - const modules = await getInstalledModules({ contract }); - const coreMetadata = ( await fetchPublishedContractsFromDeploy({ contract, client: contract.client, }) ).at(-1) as FetchDeployMetadataResult; - const modulesMetadata = (await Promise.all( - modules.map(async (m) => - ( - await fetchPublishedContractsFromDeploy({ - contract: getContract({ - chain: contract.chain, + + let modulesMetadata: FetchDeployMetadataResult[] | undefined; + + if (isModularCore) { + const modules = await getInstalledModules({ contract }); + + modulesMetadata = (await Promise.all( + modules.map(async (m) => + ( + await fetchPublishedContractsFromDeploy({ + contract: getContract({ + chain: contract.chain, + client: contract.client, + address: m.implementation, + }), client: contract.client, - address: m.implementation, - }), - client: contract.client, - }) - ).at(-1), - ), - )) as FetchDeployMetadataResult[]; + }) + ).at(-1), + ), + )) as FetchDeployMetadataResult[]; + } const ProxyDeployedEvent = prepareEvent({ signature: - "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", + "event ProxyDeployed(address indexed implementation, address indexed proxy, address indexed deployer, bytes32 inputSalt, bytes data)", }); const twCloneFactoryContract = await getDeployedCloneFactoryContract({ @@ -139,6 +140,7 @@ export default async function Page(props: { coreMetadata={coreMetadata} modulesMetadata={modulesMetadata} initializeData={event?.args.data} + inputSalt={event?.args.inputSalt} data={chainsDeployedOn} coreContract={contract} /> diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index 3d18c5873cc..58eb9d57e1d 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -44,9 +44,9 @@ export function prepareAutoFactoryDeployTransaction( ? args.salt.startsWith("0x") && args.salt.length === 66 ? (args.salt as `0x${string}`) : keccakId(args.salt) - : toHex(blockNumber, { - size: 32, - }); + : (`0x0101${toHex(blockNumber, { + size: 30, + }).replace(/^0x/, "")}` as `0x${string}`); if (args.isCrosschain) { if (!args.initializeData || !args.implementationAddress) { diff --git a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts index e9c7d7546fa..612c55550aa 100644 --- a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts +++ b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts @@ -1,5 +1,5 @@ -import { prepareEvent } from "../../../../../event/prepare-event.js"; import type { AbiParameterToPrimitiveType } from "abitype"; +import { prepareEvent } from "../../../../../event/prepare-event.js"; /** * Represents the filters for the "ProxyDeployed" event. @@ -46,12 +46,13 @@ export function proxyDeployedEvent(filters: ProxyDeployedEventFilters = {}) { }); } - // TODO: remove this once the modified version of TWCloneFactory has been published -export function modifiedProxyDeployedEvent(filters: ProxyDeployedEventFilters = {}) { +export function modifiedProxyDeployedEvent( + filters: ProxyDeployedEventFilters = {}, +) { return prepareEvent({ signature: - "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer, bytes data)", + "event ProxyDeployed(address indexed implementation, address indexed proxy, address indexed deployer, bytes32 inputSalt, bytes data)", filters, }); } From 2936839ddc484e24aeb571a6bc184461bec3bc86 Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 16 Jan 2025 05:56:27 +0530 Subject: [PATCH 25/37] enable for direct deploys --- .../cross-chain/data-table.tsx | 230 ++++++++++-------- .../[contractAddress]/cross-chain/page.tsx | 33 ++- 2 files changed, 166 insertions(+), 97 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 9d0f46003c1..5f3bbdb4980 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -46,10 +46,12 @@ import { useForm } from "react-hook-form"; import { ZERO_ADDRESS, defineChain, + eth_getCode, eth_getTransactionCount, getContract, getRpcClient, prepareContractCall, + prepareTransaction, readContract, sendAndConfirmTransaction, sendTransaction, @@ -77,7 +79,14 @@ type CrossChain = { status: "DEPLOYED" | "NOT_DEPLOYED"; }; -type ChainId = "84532" | "11155420" | "919" | "111557560" | "999999999"; +type ChainId = + | "84532" + | "11155420" + | "919" + | "111557560" + | "999999999" + | "11155111" + | "421614"; const formSchema = z.object({ amounts: z.object({ @@ -100,6 +109,7 @@ export function DataTable({ modulesMetadata, initializeData, inputSalt, + initCode, }: { data: CrossChain[]; coreMetadata: FetchDeployMetadataResult; @@ -107,6 +117,7 @@ export function DataTable({ modulesMetadata?: FetchDeployMetadataResult[]; initializeData?: `0x${string}`; inputSalt?: `0x${string}`; + initCode?: `0x${string}`; }) { const activeAccount = useActiveAccount(); const switchChain = useSwitchActiveWalletChain(); @@ -285,121 +296,148 @@ export function DataTable({ (m) => m.name === "SuperChainInterop", ); - const crosschainContractAddress = await deployContractfromDeployMetadata({ - account: activeAccount, - chain, - client, - deployMetadata: coreMetadata, - isCrosschain: true, - initializeData, - salt, - }); - - verifyContract({ - address: crosschainContractAddress, - chain, - client, - }); - - if (isCrosschain && modulesMetadata) { - const owner = await readContract({ - contract: coreContract, - method: "function owner() view returns (address)", - params: [], + let crosschainContractAddress: string | undefined; + if (initCode) { + const tx = prepareTransaction({ + client, + chain, + to: "0x4e59b44847b379578588920cA78FbF26c0B4956C", + data: initCode, }); - const moduleInitializeParams = modulesMetadata.reduce( - (acc, mod) => { - const params = getModuleInstallParams(mod); - const paramNames = params - .map((param) => param.name) - .filter((p) => p !== undefined); - const returnVal: Record = {}; - - // set connected wallet address as default "royaltyRecipient" - if (showRoyaltyFieldset(paramNames)) { - returnVal.royaltyRecipient = owner || ""; - returnVal.royaltyBps = "0"; - returnVal.transferValidator = ZERO_ADDRESS; - } - - // set connected wallet address as default "primarySaleRecipient" - else if (showPrimarySaleFieldset(paramNames)) { - returnVal.primarySaleRecipient = owner || ""; - } - - // set superchain bridge address - else if (showSuperchainBridgeFieldset(paramNames)) { - returnVal.superchainBridge = - "0x4200000000000000000000000000000000000028"; // OP Superchain Bridge - } + await sendAndConfirmTransaction({ + transaction: tx, + account: activeAccount, + }); - acc[mod.name] = returnVal; - return acc; + const code = await eth_getCode( + getRpcClient({ + client, + chain, + }), + { + address: coreContract.address, }, - {} as Record>, ); - const moduleDeployData = modulesMetadata.map((m) => ({ - deployMetadata: m, - initializeParams: moduleInitializeParams[m.name], - })); - - const contract = getContract({ - address: crosschainContractAddress, + if (code && code.length > 2) { + crosschainContractAddress = coreContract.address; + } + } else { + crosschainContractAddress = await deployContractfromDeployMetadata({ + account: activeAccount, chain, client, + deployMetadata: coreMetadata, + isCrosschain: true, + initializeData, + salt, }); - const rpcRequest = getRpcClient({ - client, + verifyContract({ + address: crosschainContractAddress, chain, + client, }); - const currentNonce = await eth_getTransactionCount(rpcRequest, { - address: activeAccount.address, - }); + if (isCrosschain && modulesMetadata) { + const owner = await readContract({ + contract: coreContract, + method: "function owner() view returns (address)", + params: [], + }); - for (const [i, m] of moduleDeployData.entries()) { - let moduleData: `0x${string}` | undefined; - - const moduleInstallParams = m.deployMetadata.abi.find( - (abiType) => - (abiType as AbiFunction).name === "encodeBytesOnInstall", - ) as AbiFunction | undefined; - - if (m.initializeParams && moduleInstallParams) { - moduleData = encodeAbiParameters( - ( - moduleInstallParams.inputs as { name: string; type: string }[] - ).map((p) => ({ - name: p.name, - type: p.type, - })), - Object.values(m.initializeParams), - ); - } + const moduleInitializeParams = modulesMetadata.reduce( + (acc, mod) => { + const params = getModuleInstallParams(mod); + const paramNames = params + .map((param) => param.name) + .filter((p) => p !== undefined); + const returnVal: Record = {}; + + // set connected wallet address as default "royaltyRecipient" + if (showRoyaltyFieldset(paramNames)) { + returnVal.royaltyRecipient = owner || ""; + returnVal.royaltyBps = "0"; + returnVal.transferValidator = ZERO_ADDRESS; + } + + // set connected wallet address as default "primarySaleRecipient" + else if (showPrimarySaleFieldset(paramNames)) { + returnVal.primarySaleRecipient = owner || ""; + } + + // set superchain bridge address + else if (showSuperchainBridgeFieldset(paramNames)) { + returnVal.superchainBridge = + "0x4200000000000000000000000000000000000028"; // OP Superchain Bridge + } + + acc[mod.name] = returnVal; + return acc; + }, + {} as Record>, + ); - const installTransaction = installPublishedModule({ - contract, - account: activeAccount, - moduleName: m.deployMetadata.name, - publisher: m.deployMetadata.publisher, - version: m.deployMetadata.version, - moduleData, - nonce: currentNonce + i, + const moduleDeployData = modulesMetadata.map((m) => ({ + deployMetadata: m, + initializeParams: moduleInitializeParams[m.name], + })); + + const contract = getContract({ + address: crosschainContractAddress, + chain, + client, }); - const txResult = await sendTransaction({ - transaction: installTransaction, - account: activeAccount, + const rpcRequest = getRpcClient({ + client, + chain, + }); + const currentNonce = await eth_getTransactionCount(rpcRequest, { + address: activeAccount.address, }); - await waitForReceipt(txResult); - // can't handle parallel transactions, so wait a bit - await new Promise((resolve) => setTimeout(resolve, 1000)); + for (const [i, m] of moduleDeployData.entries()) { + let moduleData: `0x${string}` | undefined; + + const moduleInstallParams = m.deployMetadata.abi.find( + (abiType) => + (abiType as AbiFunction).name === "encodeBytesOnInstall", + ) as AbiFunction | undefined; + + if (m.initializeParams && moduleInstallParams) { + moduleData = encodeAbiParameters( + ( + moduleInstallParams.inputs as { name: string; type: string }[] + ).map((p) => ({ + name: p.name, + type: p.type, + })), + Object.values(m.initializeParams), + ); + } + + const installTransaction = installPublishedModule({ + contract, + account: activeAccount, + moduleName: m.deployMetadata.name, + publisher: m.deployMetadata.publisher, + version: m.deployMetadata.version, + moduleData, + nonce: currentNonce + i, + }); + + const txResult = await sendTransaction({ + transaction: installTransaction, + account: activeAccount, + }); + + await waitForReceipt(txResult); + // can't handle parallel transactions, so wait a bit + await new Promise((resolve) => setTimeout(resolve, 1000)); + } } } - deployStatusModal.nextStep(); deployStatusModal.setViewContractLink( `/${chain.id}/${crosschainContractAddress}`, diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index 13288433a94..ad1ea5eaf24 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -1,6 +1,10 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-components/fetchPublishedContractsFromDeploy"; import { notFound } from "next/navigation"; -import { getContractEvents, prepareEvent } from "thirdweb"; +import { + eth_getTransactionByHash, + getContractEvents, + prepareEvent, +} from "thirdweb"; import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; import { type FetchDeployMetadataResult, @@ -52,12 +56,38 @@ export default async function Page(props: { }, ); + let initCode: `0x${string}` = "0x"; + try { + const res = await fetch( + `https://contract.thirdweb-dev.com/creation/${contract.chain.id}/${contract.address}`, + ); + const creationData = await res.json(); + + if (creationData.status === "1" && creationData.result[0]?.txHash) { + const creationTx = await eth_getTransactionByHash( + getRpcClient({ + client: contract.client, + chain: defineChain(84532), + }), + { hash: creationData.result[0]?.txHash }, + ); + + if (creationTx.to === "0x4e59b44847b379578588920cA78FbF26c0B4956C") { + initCode = creationTx.input; + } + } + } catch (e) { + console.debug(e); + } + const topOPStackTestnetChainIds = [ 84532, // Base 11155420, // OP testnet 919, // Mode Network 111557560, // Cyber 999999999, // Zora + 11155111, // sepolia + 421614, ]; const chainsDeployedOn = await Promise.all( @@ -143,6 +173,7 @@ export default async function Page(props: { inputSalt={event?.args.inputSalt} data={chainsDeployedOn} coreContract={contract} + initCode={initCode} /> ); } From 92aa7237edc38331044456d4970cfd4dd473db2b Mon Sep 17 00:00:00 2001 From: Yash Date: Thu, 16 Jan 2025 06:16:21 +0530 Subject: [PATCH 26/37] fix chains --- .../[chain_id]/[contractAddress]/cross-chain/data-table.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 5f3bbdb4980..27e2b7ef863 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -95,6 +95,8 @@ const formSchema = z.object({ "919": z.string(), "111557560": z.string(), "999999999": z.string(), + "11155111": z.string(), + "421614": z.string(), }), }); type FormSchema = z.output; @@ -136,6 +138,8 @@ export function DataTable({ "919": "", // Mode Network "111557560": "", // Cyber "999999999": "", // Zora + "11155111": "", // Sepolia + "421614": "", }, }, }); From 3637b3848ce3e97744b0093d62c7276ed53f1b1e Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 17 Jan 2025 05:00:00 +0530 Subject: [PATCH 27/37] fix module fetch, disable superchain --- .../cross-chain/data-table.tsx | 128 ++----------- .../[contractAddress]/cross-chain/page.tsx | 176 +++++++++++++----- ...ular-contract-default-modules-fieldset.tsx | 4 - packages/thirdweb/src/exports/modules.ts | 1 + 4 files changed, 141 insertions(+), 168 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 27e2b7ef863..2a32d716ede 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -34,42 +34,29 @@ import { DeployStatusModal, useDeployStatusModal, } from "components/contract-components/contract-deploy-form/deploy-context-modal"; -import { - getModuleInstallParams, - showPrimarySaleFieldset, - showRoyaltyFieldset, - showSuperchainBridgeFieldset, -} from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; +import {} from "components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset"; import { useTxNotifications } from "hooks/useTxNotifications"; import Link from "next/link"; import { useForm } from "react-hook-form"; import { - ZERO_ADDRESS, defineChain, eth_getCode, - eth_getTransactionCount, getContract, getRpcClient, prepareContractCall, prepareTransaction, - readContract, sendAndConfirmTransaction, - sendTransaction, - waitForReceipt, } from "thirdweb"; import type { FetchDeployMetadataResult, ThirdwebContract, } from "thirdweb/contract"; -import { deployContractfromDeployMetadata } from "thirdweb/deploys"; -import { installPublishedModule } from "thirdweb/modules"; -import { useActiveAccount, useSwitchActiveWalletChain } from "thirdweb/react"; import { - type AbiFunction, - concatHex, - encodeAbiParameters, - padHex, -} from "thirdweb/utils"; + deployContractfromDeployMetadata, + getOrDeployInfraForPublishedContract, +} from "thirdweb/deploys"; +import { useActiveAccount, useSwitchActiveWalletChain } from "thirdweb/react"; +import { concatHex, padHex } from "thirdweb/utils"; import { z } from "zod"; type CrossChain = { @@ -112,6 +99,7 @@ export function DataTable({ initializeData, inputSalt, initCode, + isDirectDeploy, }: { data: CrossChain[]; coreMetadata: FetchDeployMetadataResult; @@ -120,6 +108,7 @@ export function DataTable({ initializeData?: `0x${string}`; inputSalt?: `0x${string}`; initCode?: `0x${string}`; + isDirectDeploy: boolean; }) { const activeAccount = useActiveAccount(); const switchChain = useSwitchActiveWalletChain(); @@ -301,7 +290,7 @@ export function DataTable({ ); let crosschainContractAddress: string | undefined; - if (initCode) { + if (initCode && isDirectDeploy) { const tx = prepareTransaction({ client, chain, @@ -344,101 +333,14 @@ export function DataTable({ client, }); if (isCrosschain && modulesMetadata) { - const owner = await readContract({ - contract: coreContract, - method: "function owner() view returns (address)", - params: [], - }); - - const moduleInitializeParams = modulesMetadata.reduce( - (acc, mod) => { - const params = getModuleInstallParams(mod); - const paramNames = params - .map((param) => param.name) - .filter((p) => p !== undefined); - const returnVal: Record = {}; - - // set connected wallet address as default "royaltyRecipient" - if (showRoyaltyFieldset(paramNames)) { - returnVal.royaltyRecipient = owner || ""; - returnVal.royaltyBps = "0"; - returnVal.transferValidator = ZERO_ADDRESS; - } - - // set connected wallet address as default "primarySaleRecipient" - else if (showPrimarySaleFieldset(paramNames)) { - returnVal.primarySaleRecipient = owner || ""; - } - - // set superchain bridge address - else if (showSuperchainBridgeFieldset(paramNames)) { - returnVal.superchainBridge = - "0x4200000000000000000000000000000000000028"; // OP Superchain Bridge - } - - acc[mod.name] = returnVal; - return acc; - }, - {} as Record>, - ); - - const moduleDeployData = modulesMetadata.map((m) => ({ - deployMetadata: m, - initializeParams: moduleInitializeParams[m.name], - })); - - const contract = getContract({ - address: crosschainContractAddress, - chain, - client, - }); - - const rpcRequest = getRpcClient({ - client, - chain, - }); - const currentNonce = await eth_getTransactionCount(rpcRequest, { - address: activeAccount.address, - }); - - for (const [i, m] of moduleDeployData.entries()) { - let moduleData: `0x${string}` | undefined; - - const moduleInstallParams = m.deployMetadata.abi.find( - (abiType) => - (abiType as AbiFunction).name === "encodeBytesOnInstall", - ) as AbiFunction | undefined; - - if (m.initializeParams && moduleInstallParams) { - moduleData = encodeAbiParameters( - ( - moduleInstallParams.inputs as { name: string; type: string }[] - ).map((p) => ({ - name: p.name, - type: p.type, - })), - Object.values(m.initializeParams), - ); - } - - const installTransaction = installPublishedModule({ - contract, - account: activeAccount, - moduleName: m.deployMetadata.name, - publisher: m.deployMetadata.publisher, - version: m.deployMetadata.version, - moduleData, - nonce: currentNonce + i, - }); - - const txResult = await sendTransaction({ - transaction: installTransaction, + for (const m of modulesMetadata) { + await getOrDeployInfraForPublishedContract({ + chain, + client, account: activeAccount, + contractId: m.name, + publisher: m.publisher, }); - - await waitForReceipt(txResult); - // can't handle parallel transactions, so wait a bit - await new Promise((resolve) => setTimeout(resolve, 1000)); } } } diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx index ad1ea5eaf24..316a9d36c0f 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/page.tsx @@ -2,7 +2,9 @@ import { fetchPublishedContractsFromDeploy } from "components/contract-component import { notFound } from "next/navigation"; import { eth_getTransactionByHash, + eth_getTransactionReceipt, getContractEvents, + parseEventLogs, prepareEvent, } from "thirdweb"; import { defineChain, getChainMetadata, localhost } from "thirdweb/chains"; @@ -10,9 +12,12 @@ import { type FetchDeployMetadataResult, getContract, getDeployedCloneFactoryContract, + resolveContractAbi, } from "thirdweb/contract"; -import { getInstalledModules } from "thirdweb/modules"; +import { moduleInstalledEvent } from "thirdweb/modules"; import { eth_getCode, getRpcClient } from "thirdweb/rpc"; +import type { TransactionReceipt } from "thirdweb/transaction"; +import { type AbiFunction, decodeFunctionData } from "thirdweb/utils"; import { getContractPageParamsInfo } from "../_utils/getContractFromParams"; import { getContractPageMetadata } from "../_utils/getContractPageMetadata"; import { DataTable } from "./data-table"; @@ -40,12 +45,18 @@ export default async function Page(props: { const { contract } = info; - if (contract.chain.id === localhost.id) { - return
asd
; - } - const { isModularCore } = await getContractPageMetadata(contract); + const ProxyDeployedEvent = prepareEvent({ + signature: + "event ProxyDeployed(address indexed implementation, address indexed proxy, address indexed deployer, bytes32 inputSalt, bytes data)", + }); + + const twCloneFactoryContract = await getDeployedCloneFactoryContract({ + chain: contract.chain, + client: contract.client, + }); + const originalCode = await eth_getCode( getRpcClient({ client: contract.client, @@ -57,6 +68,9 @@ export default async function Page(props: { ); let initCode: `0x${string}` = "0x"; + let creationTxReceipt: TransactionReceipt | undefined; + let isDirectDeploy = false; + let isProxyDeploy = false; try { const res = await fetch( `https://contract.thirdweb-dev.com/creation/${contract.chain.id}/${contract.address}`, @@ -64,22 +78,52 @@ export default async function Page(props: { const creationData = await res.json(); if (creationData.status === "1" && creationData.result[0]?.txHash) { - const creationTx = await eth_getTransactionByHash( - getRpcClient({ - client: contract.client, - chain: defineChain(84532), - }), - { hash: creationData.result[0]?.txHash }, - ); + const rpcClient = getRpcClient({ + client: contract.client, + chain: contract.chain, + }); + creationTxReceipt = await eth_getTransactionReceipt(rpcClient, { + hash: creationData.result[0]?.txHash, + }); - if (creationTx.to === "0x4e59b44847b379578588920cA78FbF26c0B4956C") { - initCode = creationTx.input; - } + const creationTx = await eth_getTransactionByHash(rpcClient, { + hash: creationData.result[0]?.txHash, + }); + + initCode = creationTx.input; + isDirectDeploy = + creationTx.to === "0x4e59b44847b379578588920cA78FbF26c0B4956C"; + isProxyDeploy = + !!twCloneFactoryContract && + creationTx.to === twCloneFactoryContract?.address; } } catch (e) { console.debug(e); } + if (!isDirectDeploy && !isProxyDeploy) { + if (contract.chain.id === localhost.id) { + return
Not supported
; + } + } + + let initializeData: `0x${string}` | undefined; + let inputSalt: `0x${string}` | undefined; + let creationBlockNumber: bigint | undefined; + + if (isProxyDeploy && twCloneFactoryContract) { + const events = await getContractEvents({ + contract: twCloneFactoryContract, + events: [ProxyDeployedEvent], + blockRange: 123456n, + }); + const event = events.find((e) => e.args.proxy === params.contractAddress); + + initializeData = event?.args.data; + inputSalt = event?.args.inputSalt; + creationBlockNumber = event?.blockNumber; + } + const topOPStackTestnetChainIds = [ 84532, // Base 11155420, // OP testnet @@ -126,54 +170,84 @@ export default async function Page(props: { let modulesMetadata: FetchDeployMetadataResult[] | undefined; if (isModularCore) { - const modules = await getInstalledModules({ contract }); - - modulesMetadata = (await Promise.all( - modules.map(async (m) => - ( - await fetchPublishedContractsFromDeploy({ - contract: getContract({ - chain: contract.chain, - client: contract.client, - address: m.implementation, - }), - client: contract.client, - }) - ).at(-1), - ), - )) as FetchDeployMetadataResult[]; - } + let modules: string[] | undefined; + const moduleEvent = moduleInstalledEvent(); + // extract module address in ModuleInstalled events from transaction receipt + if (creationTxReceipt) { + const decodedEvent = parseEventLogs({ + events: [moduleEvent], + logs: creationTxReceipt.logs, + }); - const ProxyDeployedEvent = prepareEvent({ - signature: - "event ProxyDeployed(address indexed implementation, address indexed proxy, address indexed deployer, bytes32 inputSalt, bytes data)", - }); + modules = decodedEvent.map((e) => e.args.installedModule); + } - const twCloneFactoryContract = await getDeployedCloneFactoryContract({ - chain: contract.chain, - client: contract.client, - }); + // fetch events from contract + if (!modules && creationBlockNumber) { + const events = await getContractEvents({ + contract: contract, + events: [moduleEvent], + blockRange: 123456n, + }); - if (!twCloneFactoryContract) { - throw new Error("Factory not found"); - } + const filteredEvents = events.filter( + (e) => e.blockNumber === creationBlockNumber, + ); - const events = await getContractEvents({ - contract: twCloneFactoryContract, - events: [ProxyDeployedEvent], - blockRange: 123456n, - }); - const event = events.find((e) => e.args.proxy === params.contractAddress); + modules = filteredEvents.map((e) => e.args.installedModule); + } + + // if receipt not available, try extracting module address from initialize data + if (!modules && initializeData) { + // biome-ignore lint/suspicious/noExplicitAny: FIXME + const decodedData: any = await decodeFunctionData({ + contract, + data: initializeData, + }); + + const abi = await resolveContractAbi(contract).catch(() => undefined); + + if (abi) { + const initializeFunction = abi.find( + (i: AbiFunction) => i.type === "function" && i.name === "initialize", + ) as unknown as AbiFunction; + + const moduleIndex = initializeFunction.inputs.findIndex( + (i) => i.name === "_modules" || i.name === "modules", + ); + + modules = moduleIndex ? decodedData[moduleIndex] : undefined; + } + } + + modulesMetadata = modules + ? ((await Promise.all( + modules.map(async (m) => + ( + await fetchPublishedContractsFromDeploy({ + contract: getContract({ + chain: contract.chain, + client: contract.client, + address: m, + }), + client: contract.client, + }) + ).at(-1), + ), + )) as FetchDeployMetadataResult[]) + : undefined; + } return ( ); } diff --git a/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx b/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx index 16efe898ca6..8cbdee21d17 100644 --- a/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx +++ b/apps/dashboard/src/components/contract-components/contract-deploy-form/modular-contract-default-modules-fieldset.tsx @@ -226,10 +226,6 @@ export function showPrimarySaleFieldset(paramNames: string[]) { return paramNames.length === 1 && paramNames.includes("primarySaleRecipient"); } -export function showSuperchainBridgeFieldset(paramNames: string[]) { - return paramNames.length === 1 && paramNames.includes("superchainBridge"); -} - function showSequentialTokenIdFieldset(paramNames: string[]) { return paramNames.length === 1 && paramNames.includes("startTokenId"); } diff --git a/packages/thirdweb/src/exports/modules.ts b/packages/thirdweb/src/exports/modules.ts index 5e77f55f966..f101b748a63 100644 --- a/packages/thirdweb/src/exports/modules.ts +++ b/packages/thirdweb/src/exports/modules.ts @@ -178,3 +178,4 @@ export { uninstallModuleByProxy, type UninstallModuleByProxyOptions, } from "../extensions/modules/common/uninstallModuleByProxy.js"; +export { moduleInstalledEvent } from "../extensions/modules/__generated__/IModularCore/events/ModuleInstalled.js"; From 1799711617473029c61a21eaec1c888cd9278d37 Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 17 Jan 2025 05:05:12 +0530 Subject: [PATCH 28/37] fix event --- .../abis/thirdweb/IContractFactory.json | 2 +- .../deployment/deploy-via-autofactory.ts | 9 ++------- .../IContractFactory/events/ProxyDeployed.ts | 19 +++++++------------ 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/packages/thirdweb/scripts/generate/abis/thirdweb/IContractFactory.json b/packages/thirdweb/scripts/generate/abis/thirdweb/IContractFactory.json index df338d68439..332a596365e 100644 --- a/packages/thirdweb/scripts/generate/abis/thirdweb/IContractFactory.json +++ b/packages/thirdweb/scripts/generate/abis/thirdweb/IContractFactory.json @@ -1,4 +1,4 @@ [ - "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer)", + "event ProxyDeployed(address indexed implementation, address indexed proxy, address indexed deployer, bytes32 inputSalt, bytes data)", "function deployProxyByImplementation(address implementation, bytes data, bytes32 salt) returns (address)" ] \ No newline at end of file diff --git a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts index 58eb9d57e1d..6f40d6e0513 100644 --- a/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts +++ b/packages/thirdweb/src/contract/deployment/deploy-via-autofactory.ts @@ -1,8 +1,5 @@ import { parseEventLogs } from "../../event/actions/parse-logs.js"; -import { - modifiedProxyDeployedEvent, - proxyDeployedEvent, -} from "../../extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.js"; +import { proxyDeployedEvent } from "../../extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.js"; import { deployProxyByImplementation } from "../../extensions/thirdweb/__generated__/IContractFactory/write/deployProxyByImplementation.js"; import { eth_blockNumber } from "../../rpc/actions/eth_blockNumber.js"; import { getRpcClient } from "../../rpc/rpc.js"; @@ -136,9 +133,7 @@ export async function deployViaAutoFactory( }); // TODO: remove this once the modified version of TWCloneFactory has been published - const proxyEvent = salt?.startsWith("0x0101") - ? modifiedProxyDeployedEvent() - : proxyDeployedEvent(); + const proxyEvent = proxyDeployedEvent(); const decodedEvent = parseEventLogs({ events: [proxyEvent], logs: receipt.logs, diff --git a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts index 612c55550aa..6716bec6032 100644 --- a/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts +++ b/packages/thirdweb/src/extensions/thirdweb/__generated__/IContractFactory/events/ProxyDeployed.ts @@ -1,5 +1,5 @@ -import type { AbiParameterToPrimitiveType } from "abitype"; import { prepareEvent } from "../../../../../event/prepare-event.js"; +import type { AbiParameterToPrimitiveType } from "abitype"; /** * Represents the filters for the "ProxyDeployed" event. @@ -10,6 +10,11 @@ export type ProxyDeployedEventFilters = Partial<{ name: "implementation"; indexed: true; }>; + proxy: AbiParameterToPrimitiveType<{ + type: "address"; + name: "proxy"; + indexed: true; + }>; deployer: AbiParameterToPrimitiveType<{ type: "address"; name: "deployer"; @@ -32,6 +37,7 @@ export type ProxyDeployedEventFilters = Partial<{ * events: [ * proxyDeployedEvent({ * implementation: ..., + * proxy: ..., * deployer: ..., * }) * ], @@ -39,17 +45,6 @@ export type ProxyDeployedEventFilters = Partial<{ * ``` */ export function proxyDeployedEvent(filters: ProxyDeployedEventFilters = {}) { - return prepareEvent({ - signature: - "event ProxyDeployed(address indexed implementation, address proxy, address indexed deployer)", - filters, - }); -} - -// TODO: remove this once the modified version of TWCloneFactory has been published -export function modifiedProxyDeployedEvent( - filters: ProxyDeployedEventFilters = {}, -) { return prepareEvent({ signature: "event ProxyDeployed(address indexed implementation, address indexed proxy, address indexed deployer, bytes32 inputSalt, bytes data)", From f7dcb41e08e5be33258597d89d18320c6762769d Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 17 Jan 2025 05:17:20 +0530 Subject: [PATCH 29/37] clean up --- .../cross-chain/data-table.tsx | 97 +------------------ 1 file changed, 2 insertions(+), 95 deletions(-) diff --git a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx index 2a32d716ede..f50c2585d8b 100644 --- a/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx +++ b/apps/dashboard/src/app/(dashboard)/(chain)/[chain_id]/[contractAddress]/cross-chain/data-table.tsx @@ -1,14 +1,7 @@ "use client"; import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormField, - FormItem, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; +import { Form } from "@/components/ui/form"; import { Table, TableBody, @@ -18,10 +11,8 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { ToolTipLabel } from "@/components/ui/tooltip"; import { getThirdwebClient } from "@/constants/thirdweb.server"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation } from "@tanstack/react-query"; import { type ColumnDef, flexRender, @@ -41,9 +32,7 @@ import { useForm } from "react-hook-form"; import { defineChain, eth_getCode, - getContract, getRpcClient, - prepareContractCall, prepareTransaction, sendAndConfirmTransaction, } from "thirdweb"; @@ -88,9 +77,6 @@ const formSchema = z.object({ }); type FormSchema = z.output; -const positiveIntegerRegex = /^[0-9]\d*$/; -const superchainBridgeAddress = "0x4200000000000000000000000000000000000028"; - export function DataTable({ data, coreMetadata, @@ -133,51 +119,6 @@ export function DataTable({ }, }); - const crossChainTransfer = async (chainId: ChainId) => { - if (!activeAccount) { - throw new Error("Account not connected"); - } - const amount = form.getValues().amounts[chainId]; - if (!positiveIntegerRegex.test(amount)) { - form.setError(`amounts.${chainId}`, { message: "Invalid Amount" }); - return; - } - - const superChainBridge = getContract({ - address: superchainBridgeAddress, - chain: coreContract.chain, - client: coreContract.client, - }); - - const sendErc20Tx = prepareContractCall({ - contract: superChainBridge, - method: - "function sendERC20(address _token, address _to, uint256 _amount, uint256 _chainId)", - params: [ - coreContract.address, - activeAccount.address, - BigInt(amount), - BigInt(chainId), - ], - }); - - await sendAndConfirmTransaction({ - account: activeAccount, - transaction: sendErc20Tx, - }); - }; - - const crossChainTransferNotifications = useTxNotifications( - "Successfully submitted cross chain transfer", - "Failed to submit cross chain transfer", - ); - - const crossChainTransferMutation = useMutation({ - mutationFn: crossChainTransfer, - onSuccess: crossChainTransferNotifications.onSuccess, - onError: crossChainTransferNotifications.onError, - }); - const columns: ColumnDef[] = [ { accessorKey: "network", @@ -206,41 +147,7 @@ export function DataTable({ header: "Status", cell: ({ row }) => { if (row.getValue("status") === "DEPLOYED") { - return ( - ( - - - -
- - -
-
-
- -
- )} - /> - ); + return

Deployed

; } return (