diff --git a/packages/thirdweb/scripts/generate/abis/dynamic-contracts/IExtensionManager.json b/packages/thirdweb/scripts/generate/abis/dynamic-contracts/IExtensionManager.json index 8e6684149bc..eef8b474805 100644 --- a/packages/thirdweb/scripts/generate/abis/dynamic-contracts/IExtensionManager.json +++ b/packages/thirdweb/scripts/generate/abis/dynamic-contracts/IExtensionManager.json @@ -1,4 +1,5 @@ [ "function addExtension(((string name, string metadataURI, address implementation) metadata, (bytes4 functionSelector, string functionSignature)[] functions) extension)", - "function removeExtension(string extensionName)" + "function removeExtension(string extensionName)", + "function getAllExtensions() view returns (((string name, string metadataURI, address implementation) metadata, (bytes4 functionSelector, string functionSignature)[] functions)[])" ] \ No newline at end of file diff --git a/packages/thirdweb/src/extensions/dynamic-contracts/__generated__/IExtensionManager/read/getAllExtensions.ts b/packages/thirdweb/src/extensions/dynamic-contracts/__generated__/IExtensionManager/read/getAllExtensions.ts new file mode 100644 index 00000000000..e3a8808253c --- /dev/null +++ b/packages/thirdweb/src/extensions/dynamic-contracts/__generated__/IExtensionManager/read/getAllExtensions.ts @@ -0,0 +1,104 @@ +import { readContract } from "../../../../../transaction/read-contract.js"; +import type { BaseTransactionOptions } from "../../../../../transaction/types.js"; + +import { decodeAbiParameters } from "viem"; +import type { Hex } from "../../../../../utils/encoding/hex.js"; +import { detectMethod } from "../../../../../utils/bytecode/detectExtension.js"; + +export const FN_SELECTOR = "0x4a00cc48" as const; +const FN_INPUTS = [] as const; +const FN_OUTPUTS = [ + { + type: "tuple[]", + components: [ + { + type: "tuple", + name: "metadata", + components: [ + { + type: "string", + name: "name", + }, + { + type: "string", + name: "metadataURI", + }, + { + type: "address", + name: "implementation", + }, + ], + }, + { + type: "tuple[]", + name: "functions", + components: [ + { + type: "bytes4", + name: "functionSelector", + }, + { + type: "string", + name: "functionSignature", + }, + ], + }, + ], + }, +] as const; + +/** + * Checks if the `getAllExtensions` method is supported by the given contract. + * @param availableSelectors An array of 4byte function selectors of the contract. You can get this in various ways, such as using "whatsabi" or if you have the ABI of the contract available you can use it to generate the selectors. + * @returns A boolean indicating if the `getAllExtensions` method is supported. + * @extension DYNAMIC-CONTRACTS + * @example + * ```ts + * import { isGetAllExtensionsSupported } from "thirdweb/extensions/dynamic-contracts"; + * const supported = isGetAllExtensionsSupported(["0x..."]); + * ``` + */ +export function isGetAllExtensionsSupported(availableSelectors: string[]) { + return detectMethod({ + availableSelectors, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + }); +} + +/** + * Decodes the result of the getAllExtensions function call. + * @param result - The hexadecimal result to decode. + * @returns The decoded result as per the FN_OUTPUTS definition. + * @extension DYNAMIC-CONTRACTS + * @example + * ```ts + * import { decodeGetAllExtensionsResult } from "thirdweb/extensions/dynamic-contracts"; + * const result = decodeGetAllExtensionsResultResult("..."); + * ``` + */ +export function decodeGetAllExtensionsResult(result: Hex) { + return decodeAbiParameters(FN_OUTPUTS, result)[0]; +} + +/** + * Calls the "getAllExtensions" function on the contract. + * @param options - The options for the getAllExtensions function. + * @returns The parsed result of the function call. + * @extension DYNAMIC-CONTRACTS + * @example + * ```ts + * import { getAllExtensions } from "thirdweb/extensions/dynamic-contracts"; + * + * const result = await getAllExtensions({ + * contract, + * }); + * + * ``` + */ +export async function getAllExtensions(options: BaseTransactionOptions) { + return readContract({ + contract: options.contract, + method: [FN_SELECTOR, FN_INPUTS, FN_OUTPUTS] as const, + params: [], + }); +} diff --git a/packages/thirdweb/src/extensions/thirdweb/write/publish.ts b/packages/thirdweb/src/extensions/thirdweb/write/publish.ts index 5ae855d6719..49bf3a13c14 100644 --- a/packages/thirdweb/src/extensions/thirdweb/write/publish.ts +++ b/packages/thirdweb/src/extensions/thirdweb/write/publish.ts @@ -5,6 +5,7 @@ import type { ThirdwebClient } from "../../../client/client.js"; import { ZERO_ADDRESS } from "../../../constants/addresses.js"; import { getContract } from "../../../contract/contract.js"; import { CONTRACT_PUBLISHER_ADDRESS } from "../../../contract/deployment/publisher.js"; +import { isGetAllExtensionsSupported } from "../../../extensions/dynamic-contracts/__generated__/IExtensionManager/read/getAllExtensions.js"; import { download } from "../../../storage/download.js"; import { upload } from "../../../storage/upload.js"; import type { BaseTransactionOptions } from "../../../transaction/types.js"; @@ -79,8 +80,14 @@ export function publishContract( compositeAbi: options.metadata.compositeAbi, constructorParams: options.metadata.constructorParams, implConstructorParams: options.metadata.implConstructorParams, - defaultExtensions: options.metadata.defaultExtensions, - defaultModules: options.metadata.defaultModules, + defaultExtensions: + routerType === "dynamic" + ? options.metadata.defaultExtensions + : undefined, + defaultModules: + routerType === "modular" + ? options.metadata.defaultModules + : undefined, deployType: options.metadata.deployType, description: options.metadata.description, displayName: options.metadata.displayName, @@ -131,6 +138,7 @@ function getRouterType(abi: Abi) { .filter((f) => f.type === "function") .map((f) => toFunctionSelector(f)); const isModule = isGetInstalledModulesSupported(fnSelectors); - // TODO add dynamic detection - return isModule ? "modular" : "none"; + const isDynamic = isGetAllExtensionsSupported(fnSelectors); + + return isModule ? "modular" : isDynamic ? "dynamic" : "none"; }