Skip to content
5 changes: 5 additions & 0 deletions .changeset/sharp-files-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

deploy and install stylus modules
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ export const InstallModuleForm = (props: InstallModuleFormProps) => {
moduleInfo: {
bytecodeUri: selectedModule.metadata.bytecodeUri,
},
isStylus:
(selectedModule.metadata.compilers?.stylus &&
selectedModule.metadata.compilers?.stylus?.length > 0) ||
false,
});
},
queryKey: [
Expand Down Expand Up @@ -432,6 +436,7 @@ async function isModuleCompatible(options: {
moduleInfo: {
bytecodeUri: string;
};
isStylus: boolean;
client: ThirdwebClient;
}) {
// 1. get module's bytecode
Expand All @@ -444,15 +449,17 @@ async function isModuleCompatible(options: {

// 2. check compatibility with core and installed modules
try {
const isCompatible = await checkModulesCompatibility({
chain: options.contractInfo.chain,
client: options.client,
coreBytecode: options.contractInfo.bytecode,
moduleBytecodes: [
moduleBytecode,
...options.contractInfo.installedModuleBytecodes,
],
});
const isCompatible =
options.isStylus ||
(await checkModulesCompatibility({
chain: options.contractInfo.chain,
client: options.client,
coreBytecode: options.contractInfo.bytecode,
moduleBytecodes: [
moduleBytecode,
...options.contractInfo.installedModuleBytecodes,
],
}));

return isCompatible;
} catch (e) {
Expand Down
54 changes: 53 additions & 1 deletion apps/dashboard/src/app/(app)/(dashboard)/explore/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,64 @@ const STYLUS = {
],
description:
"Airdrop your NFTs or tokens to a large number of recipients. Built with Arbitrum Stylus.",
displayName: "Arbitrum Stylus Contracts",
displayName: "Airdrop - Arbitrum Stylus",
id: "stylus",
name: "Stylus",
showInExplore: true,
isBeta: true,
} satisfies ExploreCategory;

const MODULAR_CONTRACTS_STYLUS = {
contracts: [
// erc721 token
[
"thirdweb.eth/ERC721CoreInitializable",
[
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusMintableERC721",
"deployer.thirdweb.eth/BatchMetadataERC721",
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusTransferableERC721",
],
{
description: "ERC721 NFTs that only owners can mint.",
title: "Modular NFT Collection",
},
],
// erc1155 token
[
"thirdweb.eth/ERC1155CoreInitializable",
[
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusMintableERC1155",
"deployer.thirdweb.eth/BatchMetadataERC1155",
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusTransferableERC1155",
"deployer.thirdweb.eth/SequentialTokenIdERC1155",
],
{
description: "ERC1155 NFTs that only owners can mint.",
title: "Modular Edition",
},
],
// erc20 token
[
"thirdweb.eth/ERC20CoreInitializable",
[
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusMintableERC20",
"0x6453a486d52e0eb6e79ec4491038e2522a926936/StylusTransferableERC20",
],
{
description: "ERC20 Tokens that only owners can mint.",
title: "Modular Token",
},
],
],
description:
"Collection of highly customizable and upgradeable smart contracts built with the modular contracts framework.",
displayName: "Modular Contracts - Arbitrum Stylus",
id: "modular-contracts",
name: "modular",
showInExplore: true,
isBeta: true,
} satisfies ExploreCategory;

const CATEGORIES: Record<string, ExploreCategory> = {
[POPULAR.id]: POPULAR,
[NFTS.id]: NFTS,
Expand All @@ -203,6 +254,7 @@ const CATEGORIES: Record<string, ExploreCategory> = {
[COMMERCE.id]: COMMERCE,
[STAKING.id]: STAKING,
[GOVERNANCE.id]: GOVERNANCE,
[MODULAR_CONTRACTS_STYLUS.id]: MODULAR_CONTRACTS_STYLUS,
};

export function getCategory(id: string) {
Expand Down
49 changes: 49 additions & 0 deletions packages/thirdweb/src/cli/commands/stylus/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export async function createStylusProject() {
{ title: "Airdrop ERC1155", value: "airdrop1155" },
{ title: "ZK ERC721", value: "zk-erc721" },
{ title: "ZK ERC20", value: "zk-erc20" },
{ title: "Mint module - ERC20", value: "mintable20" },
{ title: "Transfer module - ERC20", value: "transferable20" },
{ title: "Mint module - ERC721", value: "mintable721" },
{ title: "Transfer module - ERC721", value: "transferable721" },
{ title: "Mint module - ERC1155", value: "mintable1155" },
{ title: "Transfer module - ERC1155", value: "transferable1155" },
],
message: "Select a template:",
name: "projectType",
Expand Down Expand Up @@ -133,6 +139,49 @@ export async function createStylusProject() {
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
} else if (projectType === "mintable20") {
const repoUrl = "[email protected]:thirdweb-example/stylus-mintable-erc20.git";
spinner.start(`Creating new ERC20 Mintable module: ${projectName}...`);
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
} else if (projectType === "transferable20") {
const repoUrl =
"[email protected]:thirdweb-example/stylus-transferable-erc20.git";
spinner.start(`Creating new ERC20 Transferable module: ${projectName}...`);
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
} else if (projectType === "mintable721") {
const repoUrl =
"[email protected]:thirdweb-example/stylus-mintable-erc721.git";
spinner.start(`Creating new ERC721 Mintable module: ${projectName}...`);
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
} else if (projectType === "transferable721") {
const repoUrl =
"[email protected]:thirdweb-example/stylus-transferable-erc721.git";
spinner.start(`Creating new ERC721 Transferable module: ${projectName}...`);
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
} else if (projectType === "mintable1155") {
const repoUrl =
"[email protected]:thirdweb-example/stylus-mintable-erc1155.git";
spinner.start(`Creating new ERC1155 Mintable module: ${projectName}...`);
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
} else if (projectType === "transferable1155") {
const repoUrl =
"[email protected]:thirdweb-example/stylus-transferable-erc1155.git";
spinner.start(
`Creating new ERC1155 Transferable module: ${projectName}...`,
);
newProject = spawnSync("git", ["clone", repoUrl, projectName], {
stdio: "inherit",
});
}

if (!newProject || newProject.status !== 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ThirdwebContract } from "../../../contract/contract.js";
import { getOrDeployInfraForPublishedContract } from "../../../contract/deployment/utils/bootstrap.js";
import type { Account } from "../../../wallets/interfaces/wallet.js";
import { deployPublishedContract } from "../../prebuilts/deploy-published.js";
import { installModule } from "../__generated__/IModularCore/write/installModule.js";

/**
Expand Down Expand Up @@ -49,17 +50,35 @@

return installModule({
asyncParams: async () => {
const deployedModule = await getOrDeployInfraForPublishedContract({
account,
chain: contract.chain,
client: contract.client,
constructorParams,
contractId: moduleName,
publisher,
});
let implementationAddress: string;

Check warning on line 53 in packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts#L53

Added line #L53 was not covered by tests

if (moduleName.toLowerCase().includes("stylus")) {

Check warning on line 55 in packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts#L55

Added line #L55 was not covered by tests
// TODO: switch to deterministic / create2 when available

implementationAddress = await deployPublishedContract({
account,
chain: contract.chain,
client: contract.client,
contractParams: constructorParams,
contractId: moduleName,
publisher,
});
} else {
const deployedModule = await getOrDeployInfraForPublishedContract({
account,
chain: contract.chain,
client: contract.client,
constructorParams,
contractId: moduleName,
publisher,
});

Check warning on line 74 in packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts#L58-L74

Added lines #L58 - L74 were not covered by tests

implementationAddress = deployedModule.implementationContract
.address as string;
}

Check warning on line 78 in packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts#L76-L78

Added lines #L76 - L78 were not covered by tests
return {
data: moduleData || "0x",
moduleContract: deployedModule.implementationContract.address as string,
moduleContract: implementationAddress,

Check warning on line 81 in packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts

View check run for this annotation

Codecov / codecov/patch

packages/thirdweb/src/extensions/modules/common/installPublishedModule.ts#L81

Added line #L81 was not covered by tests
};
},
contract,
Expand Down
Loading