diff --git a/docs/sdk.contractdeployer.deployreleasedcontract.md b/docs/sdk.contractdeployer.deployreleasedcontract.md
new file mode 100644
index 000000000..8c24a52c2
--- /dev/null
+++ b/docs/sdk.contractdeployer.deployreleasedcontract.md
@@ -0,0 +1,26 @@
+
+
+[Home](./index.md) > [@thirdweb-dev/sdk](./sdk.md) > [ContractDeployer](./sdk.contractdeployer.md) > [deployReleasedContract](./sdk.contractdeployer.deployreleasedcontract.md)
+
+## ContractDeployer.deployReleasedContract() method
+
+Deploy any released contract by its name
+
+Signature:
+
+```typescript
+deployReleasedContract(releaserAddress: string, contractName: string, constructorParams: any[]): Promise;
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| releaserAddress | string | the address of the releaser |
+| contractName | string | the name of the contract to deploy |
+| constructorParams | any\[\] | the constructor params to pass to the contract |
+
+Returns:
+
+Promise<string>
+
diff --git a/docs/sdk.contractdeployer.md b/docs/sdk.contractdeployer.md
index 4a802bd56..528c62088 100644
--- a/docs/sdk.contractdeployer.md
+++ b/docs/sdk.contractdeployer.md
@@ -30,6 +30,7 @@ export declare class ContractDeployer extends RPCConnectionHandler
| [deployNFTCollection(metadata)](./sdk.contractdeployer.deploynftcollection.md) | | Deploys an NFT Collection contract |
| [deployNFTDrop(metadata)](./sdk.contractdeployer.deploynftdrop.md) | | Deploys a new NFTDrop contract |
| [deployPack(metadata)](./sdk.contractdeployer.deploypack.md) | | Deploys a new Pack contract |
+| [deployReleasedContract(releaserAddress, contractName, constructorParams)](./sdk.contractdeployer.deployreleasedcontract.md) | | Deploy any released contract by its name |
| [deploySignatureDrop(metadata)](./sdk.contractdeployer.deploysignaturedrop.md) | | Deploys a new SignatureDrop contract |
| [deploySplit(metadata)](./sdk.contractdeployer.deploysplit.md) | | Deploys a new Split contract |
| [deployToken(metadata)](./sdk.contractdeployer.deploytoken.md) | | Deploys a new Token contract |
diff --git a/etc/sdk.api.md b/etc/sdk.api.md
index 7e3e1fdc7..5111c4fd7 100644
--- a/etc/sdk.api.md
+++ b/etc/sdk.api.md
@@ -1151,6 +1151,12 @@ export class ContractDeployer extends RPCConnectionHandler {
constructor(network: NetworkOrSignerOrProvider, options: SDKOptions, storage: IStorage);
// @internal
deployBuiltInContract(contractType: TContract["contractType"], contractMetadata: z.input): Promise;
+ // @internal (undocumented)
+ deployContractFromUri(publishMetadataUri: string, constructorParamValues: any[]): Promise;
+ // @internal (undocumented)
+ deployContractWithAbi(abi: ContractInterface, bytecode: BytesLike | {
+ object: string;
+ }, constructorParams: Array): Promise;
deployEdition(metadata: NFTContractDeployMetadata): Promise;
deployEditionDrop(metadata: NFTContractDeployMetadata): Promise;
deployMarketplace(metadata: MarketplaceContractDeployMetadata): Promise;
@@ -1159,6 +1165,7 @@ export class ContractDeployer extends RPCConnectionHandler {
deployNFTCollection(metadata: NFTContractDeployMetadata): Promise;
deployNFTDrop(metadata: NFTContractDeployMetadata): Promise;
deployPack(metadata: NFTContractDeployMetadata): Promise;
+ deployReleasedContract(releaserAddress: string, contractName: string, constructorParams: any[]): Promise;
deploySignatureDrop(metadata: NFTContractDeployMetadata): Promise;
deploySplit(metadata: SplitContractDeployMetadata): Promise;
deployToken(metadata: TokenContractDeployMetadata): Promise;
diff --git a/src/core/classes/contract-deployer.ts b/src/core/classes/contract-deployer.ts
index 97a3bdb6b..31194bb47 100644
--- a/src/core/classes/contract-deployer.ts
+++ b/src/core/classes/contract-deployer.ts
@@ -12,8 +12,8 @@ import {
Marketplace,
NFTCollection,
NFTDrop,
- SignatureDrop,
Pack,
+ SignatureDrop,
Split,
Token,
Vote,
@@ -28,6 +28,13 @@ import {
} from "../../types/deploy/deploy-metadata";
import { TokenDrop } from "../../contracts/token-drop";
import { Multiwrap } from "../../contracts/multiwrap";
+import { ThirdwebSDK } from "../sdk";
+import invariant from "tiny-invariant";
+import {
+ extractConstructorParamsFromAbi,
+ fetchPreDeployMetadata,
+} from "../../common/index";
+import { BigNumber, BytesLike, ContractInterface, ethers } from "ethers";
/**
* Handles deploying new contracts
@@ -341,6 +348,26 @@ export class ContractDeployer extends RPCConnectionHandler {
return await factory.deploy(contractType, contractMetadata);
}
+ /**
+ * Deploy any released contract by its name
+ * @param releaserAddress the address of the releaser
+ * @param contractName the name of the contract to deploy
+ * @param constructorParams the constructor params to pass to the contract
+ */
+ public async deployReleasedContract(
+ releaserAddress: string,
+ contractName: string,
+ constructorParams: any[],
+ ): Promise {
+ const release = await new ThirdwebSDK("polygon")
+ .getPublisher()
+ .getLatest(releaserAddress, contractName);
+ return await this.deployContractFromUri(
+ release.metadataUri,
+ constructorParams,
+ );
+ }
+
/**
* @internal
*/
@@ -410,4 +437,93 @@ export class ContractDeployer extends RPCConnectionHandler {
registry.updateSignerOrProvider(this.getSignerOrProvider());
});
}
+
+ /**
+ * @internal
+ * @param publishMetadataUri
+ * @param constructorParamValues
+ */
+ public async deployContractFromUri(
+ publishMetadataUri: string,
+ constructorParamValues: any[],
+ ) {
+ const signer = this.getSigner();
+ invariant(signer, "A signer is required");
+ const metadata = await fetchPreDeployMetadata(
+ publishMetadataUri,
+ this.storage,
+ );
+ const bytecode = metadata.bytecode.startsWith("0x")
+ ? metadata.bytecode
+ : `0x${metadata.bytecode}`;
+ if (!ethers.utils.isHexString(bytecode)) {
+ throw new Error(`Contract bytecode is invalid.\n\n${bytecode}`);
+ }
+ const constructorParamTypes = extractConstructorParamsFromAbi(
+ metadata.abi,
+ ).map((p) => p.type);
+ const paramValues = this.convertParamValues(
+ constructorParamTypes,
+ constructorParamValues,
+ );
+ return this.deployContractWithAbi(metadata.abi, bytecode, paramValues);
+ }
+
+ private convertParamValues(
+ constructorParamTypes: string[],
+ constructorParamValues: any[],
+ ) {
+ // check that both arrays are same length
+ if (constructorParamTypes.length !== constructorParamValues.length) {
+ throw Error("Passed the wrong number of constructor arguments");
+ }
+ return constructorParamTypes.map((p, index) => {
+ if (p === "tuple" || p.endsWith("[]")) {
+ if (typeof constructorParamValues[index] === "string") {
+ return JSON.parse(constructorParamValues[index]);
+ } else {
+ return constructorParamValues[index];
+ }
+ }
+ if (p === "bytes32") {
+ invariant(
+ ethers.utils.isHexString(constructorParamValues[index]),
+ `Could not parse bytes32 value. Expected valid hex string but got "${constructorParamValues[index]}".`,
+ );
+ return ethers.utils.hexZeroPad(constructorParamValues[index], 32);
+ }
+ if (p.startsWith("bytes")) {
+ invariant(
+ ethers.utils.isHexString(constructorParamValues[index]),
+ `Could not parse bytes value. Expected valid hex string but got "${constructorParamValues[index]}".`,
+ );
+ return ethers.utils.toUtf8Bytes(constructorParamValues[index]);
+ }
+ if (p.startsWith("uint") || p.startsWith("int")) {
+ return BigNumber.from(constructorParamValues[index].toString());
+ }
+ return constructorParamValues[index];
+ });
+ }
+
+ /**
+ * @internal
+ * @param abi
+ * @param bytecode
+ * @param constructorParams
+ */
+ public async deployContractWithAbi(
+ abi: ContractInterface,
+ bytecode: BytesLike | { object: string },
+ constructorParams: Array,
+ ): Promise {
+ const signer = this.getSigner();
+ invariant(signer, "Signer is required to deploy contracts");
+ const deployer = await new ethers.ContractFactory(abi, bytecode)
+ .connect(signer)
+ .deploy(...constructorParams);
+ const deployedContract = await deployer.deployed();
+ // TODO parse transaction receipt
+ return deployedContract.address;
+ }
}
diff --git a/src/core/classes/contract-publisher.ts b/src/core/classes/contract-publisher.ts
index f70bd2f2b..a70f1851e 100644
--- a/src/core/classes/contract-publisher.ts
+++ b/src/core/classes/contract-publisher.ts
@@ -2,18 +2,10 @@ import { NetworkOrSignerOrProvider, TransactionResult } from "../types";
import { SDKOptions } from "../../schema/sdk-options";
import { IStorage } from "../interfaces";
import { RPCConnectionHandler } from "./rpc-connection-handler";
-import {
- BigNumber,
- BytesLike,
- constants,
- ContractInterface,
- ethers,
- utils,
-} from "ethers";
+import { constants, utils } from "ethers";
import invariant from "tiny-invariant";
import {
extractConstructorParams,
- extractConstructorParamsFromAbi,
extractFunctions,
fetchContractMetadataFromAddress,
fetchPreDeployMetadata,
@@ -376,119 +368,6 @@ export class ContractPublisher extends RPCConnectionHandler {
};
}
- /**
- * @internal
- * @param publisherAddress
- * @param contractId
- * @param constructorParamValues
- * @param contractMetadata
- */
- public async deployPublishedContract(
- publisherAddress: string,
- contractId: string,
- constructorParamValues: any[],
- ): Promise {
- // TODO this gets the latest version, should we allow deploying a certain version?
- const contract = await this.publisher.readContract.getPublishedContract(
- publisherAddress,
- contractId,
- );
- return this.deployContract(
- contract.publishMetadataUri,
- constructorParamValues,
- );
- }
-
- /**
- * @internal
- * @param publishMetadataUri
- * @param constructorParamValues
- */
- public async deployContract(
- publishMetadataUri: string,
- constructorParamValues: any[],
- ) {
- const signer = this.getSigner();
- invariant(signer, "A signer is required");
- const metadata = await fetchPreDeployMetadata(
- publishMetadataUri,
- this.storage,
- );
- const bytecode = metadata.bytecode.startsWith("0x")
- ? metadata.bytecode
- : `0x${metadata.bytecode}`;
- if (!ethers.utils.isHexString(bytecode)) {
- throw new Error(`Contract bytecode is invalid.\n\n${bytecode}`);
- }
- const constructorParamTypes = extractConstructorParamsFromAbi(
- metadata.abi,
- ).map((p) => p.type);
- const paramValues = this.convertParamValues(
- constructorParamTypes,
- constructorParamValues,
- );
-
- return this.deployContractWithAbi(metadata.abi, bytecode, paramValues);
- }
-
- private convertParamValues(
- constructorParamTypes: string[],
- constructorParamValues: any[],
- ) {
- // check that both arrays are same length
- if (constructorParamTypes.length !== constructorParamValues.length) {
- throw Error("Passed the wrong number of constructor arguments");
- }
- return constructorParamTypes.map((p, index) => {
- if (p === "tuple" || p.endsWith("[]")) {
- if (typeof constructorParamValues[index] === "string") {
- return JSON.parse(constructorParamValues[index]);
- } else {
- return constructorParamValues[index];
- }
- }
- if (p === "bytes32") {
- invariant(
- ethers.utils.isHexString(constructorParamValues[index]),
- `Could not parse bytes32 value. Expected valid hex string but got "${constructorParamValues[index]}".`,
- );
- return ethers.utils.hexZeroPad(constructorParamValues[index], 32);
- }
- if (p.startsWith("bytes")) {
- invariant(
- ethers.utils.isHexString(constructorParamValues[index]),
- `Could not parse bytes value. Expected valid hex string but got "${constructorParamValues[index]}".`,
- );
- return ethers.utils.toUtf8Bytes(constructorParamValues[index]);
- }
- if (p.startsWith("uint") || p.startsWith("int")) {
- return BigNumber.from(constructorParamValues[index].toString());
- }
- return constructorParamValues[index];
- });
- }
-
- /**
- * @internal
- * @param abi
- * @param bytecode
- * @param constructorParams
- */
- public async deployContractWithAbi(
- abi: ContractInterface,
- bytecode: BytesLike | { object: string },
- constructorParams: Array,
- ): Promise {
- const signer = this.getSigner();
- invariant(signer, "Signer is required to deploy contracts");
- const deployer = await new ethers.ContractFactory(abi, bytecode)
- .connect(signer)
- .deploy(...constructorParams);
- const deployedContract = await deployer.deployed();
- // TODO parse transaction receipt
- return deployedContract.address;
- }
-
private toPublishedContract(
contractModel: IContractPublisher.CustomContractInstanceStruct,
): PublishedContract {
diff --git a/test/custom.test.ts b/test/custom.test.ts
index 8564e2f8a..77194259b 100644
--- a/test/custom.test.ts
+++ b/test/custom.test.ts
@@ -42,8 +42,7 @@ describe("Custom Contracts", async () => {
beforeEach(async () => {
sdk.updateSignerOrProvider(adminWallet);
- const publisher = sdk.getPublisher();
- customContractAddress = await publisher.deployContract(
+ customContractAddress = await sdk.deployer.deployContractFromUri(
simpleContractUri,
[],
);
diff --git a/test/publisher.test.ts b/test/publisher.test.ts
index 1000a4804..1b475b0be 100644
--- a/test/publisher.test.ts
+++ b/test/publisher.test.ts
@@ -106,9 +106,8 @@ describe("Publishing", async () => {
version: "0.0.1",
});
const contract = await tx.data();
- const deployedAddr = await publisher.deployPublishedContract(
- adminWallet.address,
- contract.id,
+ const deployedAddr = await sdk.deployer.deployContractFromUri(
+ contract.metadataUri,
[],
);
expect(deployedAddr.length).to.be.gt(0);
@@ -132,9 +131,8 @@ describe("Publishing", async () => {
version: "0.0.2",
});
const contract = await tx.data();
- const deployedAddr = await publisher.deployPublishedContract(
- adminWallet.address,
- contract.id,
+ const deployedAddr = await sdk.deployer.deployContractFromUri(
+ contract.metadataUri,
[],
);
expect(deployedAddr.length).to.be.gt(0);
@@ -210,9 +208,8 @@ describe("Publishing", async () => {
version: "0.0.1",
});
const contract = await tx.data();
- const deployedAddr = await publisher.deployPublishedContract(
- bobWallet.address,
- contract.id,
+ const deployedAddr = await sdk.deployer.deployContractFromUri(
+ contract.metadataUri,
[
adminWallet.address,
"0x1234",
@@ -234,9 +231,8 @@ describe("Publishing", async () => {
version: "0.0.1",
});
const contract = await tx.data();
- const deployedAddr = await pub.deployPublishedContract(
- adminWallet.address,
- contract.id,
+ const deployedAddr = await sdk.deployer.deployContractFromUri(
+ contract.metadataUri,
[],
);
const c = await realSDK.getContract(deployedAddr);
@@ -253,9 +249,8 @@ describe("Publishing", async () => {
version: "0.0.1",
});
const contract = await tx.data();
- const deployedAddr = await pub.deployPublishedContract(
- adminWallet.address,
- contract.id,
+ const deployedAddr = await sdk.deployer.deployContractFromUri(
+ contract.metadataUri,
[10, "bar"],
);
const c = await sdk.getContract(deployedAddr);
@@ -284,9 +279,8 @@ describe("Publishing", async () => {
});
it("ERC721Dropable multiphase feature detection", async () => {
- const pub = sdk.getPublisher();
const ipfsUri = "ipfs://QmWaidQMSYHPzYYZCxMc2nSk2vrD28mS43Xc9k7QFyAGja/0";
- const addr = await pub.deployContract(ipfsUri, []);
+ const addr = await sdk.deployer.deployContractFromUri(ipfsUri, []);
const c = await sdk.getContract(addr);
invariant(c.nft, "nft must be defined");
@@ -312,9 +306,8 @@ describe("Publishing", async () => {
});
it("ERC721Drop base feature detection", async () => {
- const pub = sdk.getPublisher();
const ipfsUri = "ipfs://QmfQwWiMbKaSmng5GN1P5bgCfdEy4Uyg7BznwbaP1bvj7f/0";
- const addr = await pub.deployContract(ipfsUri, []);
+ const addr = await sdk.deployer.deployContractFromUri(ipfsUri, []);
const c = await sdk.getContract(addr);
invariant(c.nft, "nft must be defined");
@@ -353,9 +346,8 @@ describe("Publishing", async () => {
});
it("Constructor params with tuples", async () => {
- const pub = await sdk.getPublisher();
const ipfsUri = "ipfs://QmZQa56Cj1gFnZgKSkvGE5uzhaQrQV3nU6upDWDusCaCwY/0";
- const addr = await pub.deployContract(ipfsUri, [
+ const addr = await sdk.deployer.deployContractFromUri(ipfsUri, [
"0x1234",
"123",
JSON.stringify(["0x1234", "0x4567"]),