Compare commits

..
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
836484ba98 Version Packages (#504)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-23 13:59:28 -08:00
Nacho IacovinoandGitHub 5047686f2a Fix ConnectWallet network selector (#507) 2023-01-23 20:53:44 +00:00
7cab8d1679 [SDK] Plugin pattern: get composite ABI (#475)
Co-authored-by: Joaquim Verges <[email protected]>
2023-01-20 17:52:30 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
0f0125acf2 Version Packages (#477)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-01-18 21:03:46 -08:00
Joaquim VergesandGitHub 894cbefc63 [SDK] Add intermediate event for contract deploy transactions before confirmation (#502) 2023-01-18 20:54:21 -08:00
retocroomanandGitHub 73dc026ff9 [SDK] Fix getting domain separator error (#500) 2023-01-18 15:18:42 -08:00
Joaquim VergesandGitHub af32007b9f [SDK] remove debug logs (#501) 2023-01-18 22:57:07 +00:00
Joaquim VergesandGitHub 5fba324ac1 [SDK] Fix claim conditions detection on legacy contracts (#498) 2023-01-15 20:34:39 -08:00
Joaquim VergesandGitHub bff433e121 [SDK] Fix deploy transaction listener for all deploy types sdk.deployer.addDeployListener() (#494) 2023-01-13 19:32:50 -08:00
Joaquim VergesandGitHub 68c8e3c4d7 [SDK] resolve contracts via multichain registry (#491) 2023-01-11 17:41:08 -08:00
Jonas DanielsandGitHub 4e8ff58208 [CLI] attempt to fix usage of rm on windows (#483) 2023-01-10 15:57:33 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d0d5a3c23d Bump json5 from 1.0.1 to 1.0.2 (#476)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-09 12:56:52 +01:00
9eaa21d09a [SDK] Multichain registry (#311)
Co-authored-by: Yash <[email protected]>
2023-01-05 19:43:28 -08:00
557429b5cf [SDK] Support for PackVRF (#463)
Co-authored-by: Jonas Daniels <[email protected]>
2023-01-05 15:38:59 -08:00
71 changed files with 1472 additions and 302 deletions
+4
View File
@@ -1,5 +1,9 @@
# @thirdweb-dev/auth
## 2.0.41
## 2.0.40
## 2.0.39
## 2.0.38
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "2.0.39",
"version": "2.0.41",
"exports": {
"./next": {
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
+16
View File
@@ -1,5 +1,21 @@
# thirdweb
## 0.9.10
### Patch Changes
- Updated dependencies [[`7cab8d1`](https://github.com/thirdweb-dev/js/commit/7cab8d1679f8d007091aa03adb83add3822a504a)]:
- @thirdweb-dev/sdk@3.6.11
## 0.9.9
### Patch Changes
- [#483](https://github.com/thirdweb-dev/js/pull/483) [`4e8ff58`](https://github.com/thirdweb-dev/js/commit/4e8ff582084c3d201314e6c5dff5b79f15eb6292) Thanks [@jnsdls](https://github.com/jnsdls)! - `thirdweb create` with usage of `forge` should now function correctly in windows CMD shell
- Updated dependencies [[`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5), [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4), [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca), [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99)]:
- @thirdweb-dev/sdk@3.6.10
## 0.9.8
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.9.8",
"version": "0.9.10",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,5 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
import { runCommand } from "./run-command";
import { rm, writeFile } from "node:fs/promises";
/**
* Spawn a package manager installation with either Yarn or NPM.
@@ -8,12 +9,12 @@ import { runCommand } from "./run-command";
*/
export async function submodules(): Promise<void> {
// Remove script folder because it clashes with contract templates (and not needed with deploy)
await runCommand("rm", ["-rf", "script"]);
await rm("script", { recursive: true, force: true });
// Reset and recursively add forge submodules
await runCommand("rm", ["-rf", ".gitmodules"]);
await runCommand("rm", ["-rf", "lib"]);
await runCommand("touch", [".gitmodules"]);
await rm(".gitmodules", { recursive: true, force: true });
await rm("lib", { recursive: true, force: true });
await writeFile(".gitmodules", "");
await runCommand("git", [
"submodule",
"add",
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/contracts-js
## 1.2.3
### Patch Changes
- [#463](https://github.com/thirdweb-dev/js/pull/463) [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - PackVRF contract
- [#311](https://github.com/thirdweb-dev/js/pull/311) [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to v3.3.0 of contracts package
- [#311](https://github.com/thirdweb-dev/js/pull/311) [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to 3.1.10 of contracts package
## 1.2.2
### Patch Changes
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryLogic__factory.esm.js"
}
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryRouter__factory.esm.js"
}
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistryStorage__factory.esm.js"
}
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-contracts-js-factories-TWRegistryLegacy__factory.cjs.js",
"module": "dist/thirdweb-dev-contracts-js-factories-TWRegistryLegacy__factory.esm.js"
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@thirdweb-dev/contracts-js",
"version": "1.2.2",
"version": "1.2.3",
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
"module": "dist/thirdweb-dev-contracts-js.esm.js",
"license": "Apache-2.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
"dependencies": {
"@thirdweb-dev/contracts": "3.2.8-0"
"@thirdweb-dev/contracts": "3.3.0"
},
"devDependencies": {
"@babel/preset-env": "^7.18.10",
+8
View File
@@ -1,5 +1,13 @@
# @thirdweb-dev/react
## 3.6.11
### Patch Changes
- [#507](https://github.com/thirdweb-dev/js/pull/507) [`5047686`](https://github.com/thirdweb-dev/js/commit/5047686f2afbca4fa32b1bbb6bcf4419bd17cf8d) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix ConnectWallet network selector showing all options three times
## 3.6.10
## 3.6.9
## 3.6.8
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.6.9",
"version": "3.6.11",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -11,23 +11,17 @@ export const SupportedNetworkSelect: React.FC<SupportedNetworkSelectProps> = ({
disabledChainIds,
...selectProps
}) => {
const deprecatedNetworks = useMemo(() => {
return SUPPORTED_CHAIN_IDS.map((supportedChain) => {
const { mainnets, testnets } = useMemo(() => {
const networks = SUPPORTED_CHAIN_IDS.map((supportedChain) => {
return getChainFromChainId(supportedChain);
});
return {
mainnets: networks.filter((n) => !n.testnet),
testnets: networks.filter((n) => n.testnet),
};
}, []);
const testnets = useMemo(() => {
return SUPPORTED_CHAIN_IDS.map((supportedChain) => {
return getChainFromChainId(supportedChain);
});
}, []);
const mainnets = useMemo(() => {
return SUPPORTED_CHAIN_IDS.map((supportedChain) => {
return getChainFromChainId(supportedChain);
});
}, []);
return (
<Select {...selectProps}>
<option disabled value={-1}>
@@ -55,17 +49,6 @@ export const SupportedNetworkSelect: React.FC<SupportedNetworkSelectProps> = ({
</option>
))}
</optgroup>
<optgroup label="Deprecated">
{deprecatedNetworks.map((tn) => (
<option
key={tn.id}
value={tn.id}
disabled={disabledChainIds?.includes(tn.id)}
>
{tn.name} ({tn.nativeCurrency?.symbol})
</option>
))}
</optgroup>
</Select>
);
};
+27
View File
@@ -1,5 +1,32 @@
# @thirdweb-dev/sdk
## 3.6.11
### Patch Changes
- [#475](https://github.com/thirdweb-dev/js/pull/475) [`7cab8d1`](https://github.com/thirdweb-dev/js/commit/7cab8d1679f8d007091aa03adb83add3822a504a) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Support for Plugin Pattern based contracts
## 3.6.10
### Patch Changes
- [#498](https://github.com/thirdweb-dev/js/pull/498) [`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix claim condition detection on legacy contracts
- [#502](https://github.com/thirdweb-dev/js/pull/502) [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add intermediate event for contract deploy transactions before confirmation
- [#463](https://github.com/thirdweb-dev/js/pull/463) [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - PackVRF integration
- [#500](https://github.com/thirdweb-dev/js/pull/500) [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723) Thanks [@retocrooman](https://github.com/retocrooman)! - Getting the domain separator is not a required function. If an error occurs there, the permit it self will not be possible. Therefore, error handling should be performed appropriately.
- [#311](https://github.com/thirdweb-dev/js/pull/311) [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add support for multi chain registry
- [#491](https://github.com/thirdweb-dev/js/pull/491) [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Use multichain registry to resolve contracts that cannot be resolved via IPFS
- [#494](https://github.com/thirdweb-dev/js/pull/494) [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix deploy transaction listener for all deploy types `sdk.deployer.addDeployListener()`
- Updated dependencies [[`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857)]:
- @thirdweb-dev/contracts-js@1.2.3
## 3.6.9
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.6.9",
"version": "3.6.11",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -62,7 +62,7 @@
"build": "tsc && preconstruct build",
"test:evm:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 30000 --parallel './test/evm/**/*.test.ts'",
"test:evm": "make test-evm",
"test:evm:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 30000",
"test:evm:single": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/evm/.mocharc.json' --timeout 90000",
"node:solana:start": "DEBUG='amman:(info|error|debug)' amman start --forceClone",
"node:solana:stop": "amman stop",
"test:solana:all": "SWC_NODE_PROJECT=./tsconfig.test.json mocha --config './test/solana/.mocharc.json' --timeout 30000 --parallel './test/solana/**/*.test.ts'",
+12 -5
View File
@@ -286,7 +286,7 @@ export class TransactionError extends Error {
raw: string,
functionInfo: FunctionInfo | undefined,
) {
let builtErrorMsg = "Contract transaction failed\n\n";
let builtErrorMsg = "\n| Contract transaction failed |\n\n";
builtErrorMsg += `Message: ${reason}`;
builtErrorMsg += "\n\n| Transaction info |\n";
builtErrorMsg += withSpaces("from", from);
@@ -320,9 +320,6 @@ export class TransactionError extends Error {
// ignore if can't parse URL
}
builtErrorMsg += "\n\n";
builtErrorMsg +=
"Need help with this error? Join our community: https://discord.gg/thirdweb";
builtErrorMsg += "\n\n\n\n";
builtErrorMsg += "| Raw error |";
builtErrorMsg += "\n\n";
builtErrorMsg += raw;
@@ -401,9 +398,19 @@ export async function convertToTWError(
// not sure what this is, just throw it back
raw = error.toString();
}
const reason =
let reason =
parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw) ||
parseMessageParts(/.*?"reason[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
if (reason && reason.toLowerCase().includes("cannot estimate gas")) {
// the error might be in the next reason block
const nextReason = parseMessageParts(
/.*?"reason[^a-zA-Z0-9]*([^"\\]*).*?/,
raw.slice(raw.indexOf(reason) + reason.length),
);
if (nextReason) {
reason = nextReason;
}
}
const data = parseMessageParts(/.*?"data[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
const rpcUrl = parseMessageParts(/.*?"url[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
let from = parseMessageParts(/.*?"from[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
@@ -13,16 +13,14 @@ import {
AbiFunction,
AbiSchema,
AbiTypeSchema,
ContractInfoSchema,
ContractSource,
FullPublishMetadata,
FullPublishMetadataSchemaOutput,
PreDeployMetadata,
PreDeployMetadataFetched,
PreDeployMetadataFetchedSchema,
PublishedMetadata,
} from "../schema/contracts/custom";
import { ExtensionNotImplementedError } from "./error";
import { fetchContractMetadata } from "./metadata-resolver";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import bs58 from "bs58";
import { BaseContract, BigNumber, ethers } from "ethers";
@@ -411,109 +409,6 @@ function isHexStrict(hex: string | number) {
);
}
/**
* @internal
* @param address
* @param provider
* @param storage
*/
export async function fetchContractMetadataFromAddress(
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) {
const compilerMetadataUri = await resolveContractUriFromAddress(
address,
provider,
);
if (!compilerMetadataUri) {
throw new Error(`Could not resolve metadata for contract at ${address}`);
}
return await fetchContractMetadata(compilerMetadataUri, storage);
}
/**
* @internal
* @param compilerMetadataUri
* @param storage
*/
export async function fetchContractMetadata(
compilerMetadataUri: string,
storage: ThirdwebStorage,
): Promise<PublishedMetadata> {
const metadata = await storage.downloadJSON(compilerMetadataUri);
if (!metadata || !metadata.output) {
throw new Error(
`Could not resolve metadata for contract at ${compilerMetadataUri}`,
);
}
const abi = AbiSchema.parse(metadata.output.abi);
const compilationTarget = metadata.settings.compilationTarget;
const targets = Object.keys(compilationTarget);
const name = compilationTarget[targets[0]];
const info = ContractInfoSchema.parse({
title: metadata.output.devdoc.title,
author: metadata.output.devdoc.author,
details: metadata.output.devdoc.detail,
notice: metadata.output.userdoc.notice,
});
const licenses: string[] = [
...new Set(
Object.entries(metadata.sources).map(([, src]) => (src as any).license),
),
];
return {
name,
abi,
metadata,
info,
licenses,
};
}
/**
* @internal
* @param publishedMetadata
* @param storage
*/
export async function fetchSourceFilesFromMetadata(
publishedMetadata: PublishedMetadata,
storage: ThirdwebStorage,
): Promise<ContractSource[]> {
return await Promise.all(
Object.entries(publishedMetadata.metadata.sources).map(
async ([path, info]) => {
const urls = (info as any).urls as string[];
const ipfsLink = urls
? urls.find((url) => url.includes("ipfs"))
: undefined;
if (ipfsLink) {
const ipfsHash = ipfsLink.split("ipfs/")[1];
// 5 sec timeout for sources that haven't been uploaded to ipfs
const timeout = new Promise<string>((_r, rej) =>
setTimeout(() => rej("timeout"), 5000),
);
const source = await Promise.race([
(await storage.download(`ipfs://${ipfsHash}`)).text(),
timeout,
]);
return {
filename: path,
source,
};
} else {
return {
filename: path,
source:
(info as any).content ||
"Could not find source for this contract",
};
}
},
),
);
}
/**
* @internal
* @param publishMetadataUri
+1
View File
@@ -1,6 +1,7 @@
export * from "./error";
export * from "./snapshots";
export * from "./role";
export * from "./metadata-resolver";
export * from "./feature-detection";
export * from "./version-checker";
// export helpful util
@@ -0,0 +1,188 @@
import { ThirdwebSDK } from "../core";
import {
Abi,
PublishedMetadata,
AbiSchema,
ContractInfoSchema,
ContractSource,
} from "../schema";
import { resolveContractUriFromAddress } from "./feature-detection";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { providers } from "ethers";
// Internal static cache
const metadataCache: Record<string, PublishedMetadata> = {};
// polygonSDK to fetch metadata from the multichain registry
const polygonSDK = new ThirdwebSDK("polygon");
function getCacheKey(address: string, chainId: number) {
return `${address}-${chainId}`;
}
function putInCache(
address: string,
chainId: number,
metadata: PublishedMetadata,
) {
metadataCache[getCacheKey(address, chainId)] = metadata;
}
function getFromCache(address: string, chainId: number) {
return metadataCache[getCacheKey(address, chainId)];
}
/**
* @internal
* @param address
* @param provider
* @param storage
*/
export async function fetchContractMetadataFromAddress(
address: string,
provider: providers.Provider,
storage: ThirdwebStorage,
) {
const chainId = (await provider.getNetwork()).chainId;
const cached = getFromCache(address, chainId);
if (cached) {
return cached;
}
let metadata: PublishedMetadata | undefined;
try {
const compilerMetadataUri = await resolveContractUriFromAddress(
address,
provider,
);
if (!compilerMetadataUri) {
throw new Error(`Could not resolve metadata for contract at ${address}`);
}
metadata = await fetchContractMetadata(compilerMetadataUri, storage);
} catch (e) {
try {
// try from multichain registry
const importedUri =
await polygonSDK.multiChainRegistry.getContractMetadataURI(
chainId,
address,
);
metadata = await fetchContractMetadata(importedUri, storage);
} catch (err) {
throw new Error(`Could not resolve metadata for contract at ${address}`);
}
}
if (!metadata) {
throw new Error(`Could not resolve metadata for contract at ${address}`);
}
putInCache(address, chainId, metadata);
return metadata;
}
/**
* @internal
* @param address
* @param provider
* @param storage
* @returns
*/
export async function fetchAbiFromAddress(
address: string,
provider: providers.Provider,
storage: ThirdwebStorage,
): Promise<Abi | undefined> {
try {
const metadata = await fetchContractMetadataFromAddress(
address,
provider,
storage,
);
if (metadata && metadata.abi) {
return metadata.abi;
}
} catch (e) {
// ignore and return undefined
// will fallback to embedded ABIs for prebuilts
}
return undefined;
}
/**
* @internal
* @param compilerMetadataUri
* @param storage
*/
export async function fetchContractMetadata(
compilerMetadataUri: string,
storage: ThirdwebStorage,
): Promise<PublishedMetadata> {
const metadata = await storage.downloadJSON(compilerMetadataUri);
if (!metadata || !metadata.output) {
throw new Error(
`Could not resolve metadata for contract at ${compilerMetadataUri}`,
);
}
const abi = AbiSchema.parse(metadata.output.abi);
const compilationTarget = metadata.settings.compilationTarget;
const targets = Object.keys(compilationTarget);
const name = compilationTarget[targets[0]];
const info = ContractInfoSchema.parse({
title: metadata.output.devdoc.title,
author: metadata.output.devdoc.author,
details: metadata.output.devdoc.detail,
notice: metadata.output.userdoc.notice,
});
const licenses: string[] = [
...new Set(
Object.entries(metadata.sources).map(([, src]) => (src as any).license),
),
];
return {
name,
abi,
metadata,
info,
licenses,
};
}
/**
* @internal
* @param publishedMetadata
* @param storage
*/
export async function fetchSourceFilesFromMetadata(
publishedMetadata: PublishedMetadata,
storage: ThirdwebStorage,
): Promise<ContractSource[]> {
return await Promise.all(
Object.entries(publishedMetadata.metadata.sources).map(
async ([path, info]) => {
const urls = (info as any).urls as string[];
const ipfsLink = urls
? urls.find((url) => url.includes("ipfs"))
: undefined;
if (ipfsLink) {
const ipfsHash = ipfsLink.split("ipfs/")[1];
// 3 sec timeout for sources that haven't been uploaded to ipfs
const timeout = new Promise<string>((_r, rej) =>
setTimeout(() => rej("timeout"), 3000),
);
const source = await Promise.race([
(await storage.download(`ipfs://${ipfsHash}`)).text(),
timeout,
]);
return {
filename: path,
source,
};
} else {
return {
filename: path,
source:
(info as any).content ||
"Could not find source for this contract",
};
}
},
),
);
}
+8 -1
View File
@@ -71,7 +71,14 @@ async function getDomainSeperator(signer: Signer, contractAddress: string) {
try {
return await contract.DOMAIN_SEPARATOR();
} catch (err) {
return await contract.getDomainSeperator();
try {
return await contract.getDomainSeperator();
} catch (err2) {
console.error(
'Error getting domain separator',
err2
)
}
}
}
+89
View File
@@ -0,0 +1,89 @@
import { ContractWrapper } from "../core/classes/contract-wrapper";
import { Abi, AbiSchema, SDKOptions } from "../schema";
import { isFeatureEnabled } from "./feature-detection";
import { fetchContractMetadataFromAddress } from "./metadata-resolver";
import { unique } from "./utils";
import type { IRouter } from "@thirdweb-dev/contracts-js";
import RouterABI from "@thirdweb-dev/contracts-js/dist/abis/Router.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { ethers } from "ethers";
/**
* @internal
*/
export async function getCompositePluginABI(
address: string,
abi: Abi,
provider: ethers.providers.Provider,
options: SDKOptions,
storage: ThirdwebStorage,
): Promise<Abi> {
let pluginABIs: Abi[] = [];
try {
// check if contract is plugin-pattern
const isPluginRouter: boolean = isFeatureEnabled(
AbiSchema.parse(abi),
"PluginRouter",
);
if (isPluginRouter) {
const contract = new ContractWrapper<IRouter>(
provider,
address,
RouterABI,
options,
);
const pluginMap = await contract.readContract.getAllPlugins();
// get extension addresses
const allPlugins = pluginMap.map((item) => item.pluginAddress);
const plugins = Array.from(new Set(allPlugins));
// get ABIs of extension contracts
pluginABIs = await getPluginABI(plugins, provider, storage);
}
} catch (err) {}
return pluginABIs.length > 0 ? joinABIs([abi, ...pluginABIs]) : abi;
}
/**
* @internal
*/
async function getPluginABI(
addresses: string[],
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
): Promise<Abi[]> {
return (
await Promise.all(
addresses.map((address) =>
fetchContractMetadataFromAddress(address, provider, storage).catch(
(err) => {
console.error(`Failed to fetch plug-in for ${address}`, err);
return { abi: [] };
},
),
),
)
).map((metadata) => metadata.abi);
}
/**
* @internal
*/
export function joinABIs(abis: Abi[]): Abi {
const parsedABIs = abis.map((abi) => AbiSchema.parse(abi)).flat();
const filteredABIs = unique(parsedABIs, (a, b): boolean => {
return (
a.name === b.name &&
a.type === b.type &&
a.inputs.length === b.inputs.length
);
});
return AbiSchema.parse(filteredABIs);
}
+21
View File
@@ -8,3 +8,24 @@ export const isBrowser = () => typeof window !== "undefined";
* @internal
*/
export const isNode = () => !isBrowser();
/**
* @internal
*/
export function unique<T>(a: T[], fn: (a: T, b: T) => boolean): T[] {
if (a.length === 0 || a.length === 1) {
return a;
}
if (!fn) {
return a;
}
for (let i = 0; i < a.length; i++) {
for (let j = i + 1; j < a.length; j++) {
if (fn(a[i], a[j])) {
a.splice(j, 1);
}
}
}
return a;
}
@@ -11,6 +11,7 @@ export const OZ_DEFENDER_FORWARDER_ADDRESS =
const TWRegistry_address = "0x7c487845f98938Bb955B1D5AD069d9a30e4131fd";
const TWFactory_address = "0x5DBC7B840baa9daBcBe9D2492E45D7244B54A2A0";
const ContractPublisher_address = "0x664244560eBa21Bf82d7150C791bE1AbcD5B4cd7"; // Polygon only
const MultichainRegistry_address = "0xcdAD8FA86e18538aC207872E8ff3536501431B73"; // Polygon only
/**
* @internal
@@ -291,6 +292,19 @@ export function getContractPublisherAddress() {
}
}
/**
* @internal
*/
export function getMultichainRegistryAddress() {
// eslint-disable-next-line turbo/no-undeclared-env-vars
if (process.env.multiChainRegistryAddress) {
// eslint-disable-next-line turbo/no-undeclared-env-vars
return process.env.multiChainRegistryAddress as string;
} else {
return MultichainRegistry_address;
}
}
/**
*
* @param chainId - chain id
@@ -53,6 +53,8 @@ import {
FEATURE_ROYALTY,
FEATURE_APPURI,
FEATURE_GASLESS,
FEATURE_PACK_VRF,
FEATURE_PLUGIN_ROUTER,
} from "./thirdweb-features";
/**
@@ -106,7 +108,9 @@ export type Feature =
| typeof FEATURE_METADATA
| typeof FEATURE_APPURI
| typeof FEATURE_OWNER
| typeof FEATURE_GASLESS;
| typeof FEATURE_GASLESS
| typeof FEATURE_PACK_VRF
| typeof FEATURE_PLUGIN_ROUTER;
/**
* @internal
@@ -135,4 +139,6 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = {
[FEATURE_APPURI.name]: FEATURE_APPURI,
[FEATURE_OWNER.name]: FEATURE_OWNER,
[FEATURE_GASLESS.name]: FEATURE_GASLESS,
[FEATURE_PACK_VRF.name]: FEATURE_PACK_VRF,
[FEATURE_PLUGIN_ROUTER.name]: FEATURE_PLUGIN_ROUTER,
};
@@ -176,3 +176,16 @@ export const NATIVE_TOKENS: Record<
export function getNativeTokenByChainId(chainId: ChainId): NativeToken {
return NATIVE_TOKENS[chainId as SUPPORTED_CHAIN_ID];
}
export const LINK_TOKEN_ADDRESS: Record<number, string> = {
[ChainId.Mainnet]: "0x514910771AF9Ca656af840dff83E8264EcF986CA",
[ChainId.Goerli]: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
[ChainId.BinanceSmartChainMainnet]:
"0x404460C6A5EdE2D891e8297795264fDe62ADBB75",
[ChainId.Polygon]: "0xb0897686c545045aFc77CF20eC7A532E3120E0F1",
[ChainId.Mumbai]: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB",
[ChainId.Avalanche]: "0x5947BB275c521040051D82396192181b413227A3",
[ChainId.AvalancheFujiTestnet]: "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846",
[ChainId.Fantom]: "0x6F43FF82CCA38001B6699a8AC47A2d0E66939407",
[ChainId.FantomTestnet]: "0xfaFedb041c0DD4fA2Dc0d87a6B0979Ee6FA7af5F",
};
@@ -1,8 +1,8 @@
import DropERC1155_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155_V2.json";
import IBurnableERC1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC1155.json";
import IClaimableERC1155 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC1155.json";
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json";
import IDropMultiPhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDrop1155.json";
import DropERC1155_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/IDropERC1155_V2.json";
import IDropSinglePhase1155 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155.json";
import IDropSinglePhase1155_V1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase1155_V1.json";
import Erc1155Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC1155.json";
@@ -1,6 +1,6 @@
import DropERC20_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC20_V2.json";
import IBurnableERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC20.json";
import IDrop from "@thirdweb-dev/contracts-js/dist/abis/IDrop.json";
import DropERC20_V2Abi from "@thirdweb-dev/contracts-js/dist/abis/IDropERC20_V2.json";
import IDropSinglePhase from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json";
import IDropSinglePhaseV1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json";
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
@@ -1,8 +1,8 @@
import DropERC721_V3Abi from "@thirdweb-dev/contracts-js/dist/abis/DropERC721_V3.json";
import IBurnableERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IBurnableERC721.json";
import IClaimableERC721 from "@thirdweb-dev/contracts-js/dist/abis/IClaimableERC721.json";
import DelayedRevealAbi from "@thirdweb-dev/contracts-js/dist/abis/IDelayedReveal.json";
import IDrop from "@thirdweb-dev/contracts-js/dist/abis/IDrop.json";
import DropERC721_V3Abi from "@thirdweb-dev/contracts-js/dist/abis/IDropERC721_V3.json";
import IDropSinglePhase from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase.json";
import IDropSinglePhaseV1 from "@thirdweb-dev/contracts-js/dist/abis/IDropSinglePhase_V1.json";
import Erc721Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC721.json";
@@ -1,10 +1,12 @@
import IERC2771ContextAbi from "@thirdweb-dev/contracts-js/dist/abis/ERC2771Context.json";
import IAppURI from "@thirdweb-dev/contracts-js/dist/abis/IAppURI.json";
import IContractMetadataAbi from "@thirdweb-dev/contracts-js/dist/abis/IContractMetadata.json";
import IPackVRFAbi from "@thirdweb-dev/contracts-js/dist/abis/IPackVRFDirect.json";
import IPermissionsAbi from "@thirdweb-dev/contracts-js/dist/abis/IPermissions.json";
import IPermissionsEnumerableAbi from "@thirdweb-dev/contracts-js/dist/abis/IPermissionsEnumerable.json";
import IThirdwebPlatformFeeAbi from "@thirdweb-dev/contracts-js/dist/abis/IPlatformFee.json";
import IThirdwebPrimarySaleAbi from "@thirdweb-dev/contracts-js/dist/abis/IPrimarySale.json";
import IRouterAbi from "@thirdweb-dev/contracts-js/dist/abis/IRouter.json";
import IThirdwebRoyaltyAbi from "@thirdweb-dev/contracts-js/dist/abis/IRoyalty.json";
import IOwnableAbi from "@thirdweb-dev/contracts-js/dist/abis/Ownable.json";
@@ -109,3 +111,25 @@ export const FEATURE_GASLESS = {
abis: [IERC2771ContextAbi],
features: {},
} as const;
export const FEATURE_PACK_VRF = {
name: "PackVRF",
namespace: "pack.vrf",
docLinks: {
sdk: "sdk.packvrf",
contracts: "IPackVRFDirect",
},
abis: [IPackVRFAbi],
features: {},
} as const;
export const FEATURE_PLUGIN_ROUTER = {
name: "PluginRouter",
namespace: "plugin.router",
docLinks: {
sdk: "sdk.pluginrouter",
contracts: "IRouter",
},
abis: [IRouterAbi],
features: {},
} as const;
+18
View File
@@ -168,3 +168,21 @@ export function getReadOnlyProvider(network: string, chainId?: number) {
return ethers.getDefaultProvider(network);
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ChainIdToName: Record<number, string> = {
[ChainId.Polygon]: "polygon",
[ChainId.Mumbai]: "mumbai",
[ChainId.Goerli]: "goerli",
[ChainId.Mainnet]: "mainnet",
[ChainId.Optimism]: "optimism",
[ChainId.OptimismGoerli]: "optimism-goerli",
[ChainId.Arbitrum]: "arbitrum",
[ChainId.ArbitrumGoerli]: "arbitrum-goerli",
[ChainId.Fantom]: "fantom",
[ChainId.FantomTestnet]: "fantom-testnet",
[ChainId.Avalanche]: "avalanche",
[ChainId.AvalancheFujiTestnet]: "avalanche-fuji",
[ChainId.BinanceSmartChainMainnet]: "binance",
[ChainId.BinanceSmartChainTestnet]: "binance-testnet",
};
+136 -27
View File
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { getPrebuiltInfo } from "../common/legacy";
import { fetchAbiFromAddress } from "../common/metadata-resolver";
import { ALL_ROLES } from "../common/role";
import { getSignerAndProvider } from "../core/classes/rpc-connection-handler";
import type {
@@ -19,10 +20,10 @@ import {
TokenErc721ContractSchema,
VoteContractSchema,
} from "../schema";
import { CustomContractSchema } from "../schema/contracts/custom";
import { Abi, CustomContractSchema } from "../schema/contracts/custom";
import { DropErc20ContractSchema } from "../schema/contracts/drop-erc20";
import { MultiwrapContractSchema } from "../schema/contracts/multiwrap";
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { ethers } from "ethers";
type InitalizeParams = [
@@ -42,7 +43,7 @@ export const EditionDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
await EditionDropInitializer.getAbi(address, provider),
await EditionDropInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/edition-drop"),
provider.getNetwork(),
]);
@@ -56,7 +57,16 @@ export const EditionDropInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
const contractInfo = await getContractInfo(address, provider);
return !contractInfo || contractInfo.version > 2
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json"))
@@ -79,7 +89,7 @@ export const EditionInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
EditionInitializer.getAbi(address, provider),
EditionInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/edition"),
provider.getNetwork(),
]);
@@ -93,7 +103,16 @@ export const EditionInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json")
).default;
@@ -110,7 +129,7 @@ export const MarketplaceInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
MarketplaceInitializer.getAbi(address, provider),
MarketplaceInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/marketplace"),
provider.getNetwork(),
]);
@@ -124,7 +143,16 @@ export const MarketplaceInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (
await import("@thirdweb-dev/contracts-js/dist/abis/Marketplace.json")
).default;
@@ -141,7 +169,7 @@ export const MultiwrapInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
MultiwrapInitializer.getAbi(address, provider),
MultiwrapInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/multiwrap"),
provider.getNetwork(),
]);
@@ -155,7 +183,16 @@ export const MultiwrapInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (await import("@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json"))
.default;
},
@@ -172,7 +209,7 @@ export const NFTCollectionInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
NFTCollectionInitializer.getAbi(address, provider),
NFTCollectionInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/nft-collection"),
provider.getNetwork(),
]);
@@ -186,7 +223,16 @@ export const NFTCollectionInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json")
).default;
@@ -203,7 +249,7 @@ export const NFTDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
NFTDropInitializer.getAbi(address, provider),
NFTDropInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/nft-drop"),
provider.getNetwork(),
]);
@@ -217,7 +263,16 @@ export const NFTDropInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
const contractInfo = await getContractInfo(address, provider);
return !contractInfo || contractInfo.version > 3
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC721.json"))
@@ -241,7 +296,7 @@ export const PackInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
PackInitializer.getAbi(address, provider),
PackInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/pack"),
provider.getNetwork(),
]);
@@ -255,7 +310,16 @@ export const PackInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
): Promise<Abi> => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (await import("@thirdweb-dev/contracts-js/dist/abis/Pack.json"))
.default;
},
@@ -272,7 +336,7 @@ export const SignatureDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
SignatureDropInitializer.getAbi(address, provider),
SignatureDropInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/signature-drop"),
provider.getNetwork(),
]);
@@ -286,7 +350,16 @@ export const SignatureDropInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
const contractInfo = await getContractInfo(address, provider);
return !contractInfo || contractInfo.version > 4
? (
@@ -313,7 +386,7 @@ export const SplitInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
SplitInitializer.getAbi(address, provider),
SplitInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/split"),
provider.getNetwork(),
]);
@@ -327,7 +400,16 @@ export const SplitInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (await import("@thirdweb-dev/contracts-js/dist/abis/Split.json"))
.default;
},
@@ -344,7 +426,7 @@ export const TokenDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
TokenDropInitializer.getAbi(address, provider),
TokenDropInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/token-drop"),
provider.getNetwork(),
]);
@@ -358,7 +440,16 @@ export const TokenDropInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
const contractInfo = await getContractInfo(address, provider);
return !contractInfo || contractInfo.version > 2
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20.json"))
@@ -378,7 +469,7 @@ export const TokenInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
TokenInitializer.getAbi(address, provider),
TokenInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/token"),
provider.getNetwork(),
]);
@@ -392,7 +483,16 @@ export const TokenInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json")
).default;
@@ -410,7 +510,7 @@ export const VoteInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
VoteInitializer.getAbi(address, provider),
VoteInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/vote"),
provider.getNetwork(),
]);
@@ -424,7 +524,16 @@ export const VoteInitializer = {
_network.chainId,
);
},
getAbi: async (address: string, provider: ethers.providers.Provider) => {
getAbi: async (
address: string,
provider: ethers.providers.Provider,
storage: ThirdwebStorage,
) => {
const abi = await fetchAbiFromAddress(address, provider, storage);
if (abi) {
return abi;
}
// Deprecated - only needed for backwards compatibility with non-released contracts - should remove in v4
return (await import("@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json"))
.default;
},
@@ -463,7 +572,7 @@ export const PREBUILT_CONTRACTS_MAP = {
const SmartContract = {
name: "SmartContract" as const,
contractType: "custom" as const,
schema: CustomContractSchema,
schema: {},
roles: ALL_ROLES,
};
@@ -23,10 +23,10 @@ import {
TransactionResultWithId,
} from "../../core/types";
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
import { Abi } from "../../schema/contracts/custom";
import { DropErc1155ContractSchema } from "../../schema/contracts/drop-erc1155";
import { SDKOptions } from "../../schema/sdk-options";
import { PrebuiltEditionDrop } from "../../types/eips";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json";
import { ThirdwebStorage, UploadProgressEvent } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
@@ -47,7 +47,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
export class EditionDrop extends StandardErc1155<PrebuiltEditionDrop> {
private static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public sales: ContractPrimarySale<PrebuiltEditionDrop>;
public platformFees: ContractPlatformFee<PrebuiltEditionDrop>;
public encoder: ContractEncoder<PrebuiltEditionDrop>;
@@ -124,7 +124,7 @@ export class EditionDrop extends StandardErc1155<PrebuiltEditionDrop> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<PrebuiltEditionDrop>(
network,
@@ -20,11 +20,11 @@ import {
TransactionResult,
TransactionResultWithId,
} from "../../core/types";
import { Abi } from "../../schema/contracts/custom";
import { TokenErc1155ContractSchema } from "../../schema/contracts/token-erc1155";
import { SDKOptions } from "../../schema/sdk-options";
import { EditionMetadataOrUri } from "../../schema/tokens/edition";
import type { TokenERC1155 } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
@@ -45,7 +45,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
export class Edition extends StandardErc1155<TokenERC1155> {
static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<
TokenERC1155,
typeof TokenErc1155ContractSchema
@@ -104,7 +104,7 @@ export class Edition extends StandardErc1155<TokenERC1155> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<TokenERC1155>(
network,
@@ -17,6 +17,7 @@ import { MarketplaceDirect } from "../../core/classes/marketplace-direct";
import { UpdateableNetwork } from "../../core/interfaces/contract";
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
import { ListingType } from "../../enums";
import { Abi } from "../../schema/contracts/custom";
import { MarketplaceContractSchema } from "../../schema/contracts/marketplace";
import { SDKOptions } from "../../schema/sdk-options";
import { Price } from "../../types/currency";
@@ -24,7 +25,6 @@ import { AuctionListing, DirectListing, Offer } from "../../types/marketplace";
import { MarketplaceFilter } from "../../types/marketplace/MarketPlaceFilter";
import { UnmappedOffer } from "../../types/marketplace/UnmappedOffer";
import type { Marketplace as MarketplaceContract } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Marketplace.json";
import { NewOfferEventObject } from "@thirdweb-dev/contracts-js/dist/declarations/src/Marketplace";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
@@ -47,7 +47,7 @@ import invariant from "tiny-invariant";
export class Marketplace implements UpdateableNetwork {
static contractRoles = ["admin", "lister", "asset"] as const;
public abi: typeof ABI;
public abi: Abi;
private contractWrapper: ContractWrapper<MarketplaceContract>;
private storage: ThirdwebStorage;
@@ -148,7 +148,7 @@ export class Marketplace implements UpdateableNetwork {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<MarketplaceContract>(
network,
@@ -20,7 +20,7 @@ import {
TransactionResult,
TransactionResultWithId,
} from "../../core/types";
import { SDKOptions } from "../../schema";
import { Abi, SDKOptions } from "../../schema";
import { MultiwrapContractSchema } from "../../schema/contracts/multiwrap";
import {
ERC1155Wrappable,
@@ -30,7 +30,6 @@ import {
WrappedTokens,
} from "../../types/multiwrap";
import type { Multiwrap as MultiwrapContract } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json";
import {
ITokenBundle,
TokensWrappedEvent,
@@ -61,7 +60,7 @@ export class Multiwrap extends StandardErc721<MultiwrapContract> {
"asset",
] as const;
public abi: typeof ABI;
public abi: Abi;
public encoder: ContractEncoder<MultiwrapContract>;
public estimator: GasCostEstimator<MultiwrapContract>;
public metadata: ContractMetadata<
@@ -102,7 +101,7 @@ export class Multiwrap extends StandardErc721<MultiwrapContract> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<MultiwrapContract>(
network,
@@ -19,10 +19,10 @@ import type {
TransactionResult,
TransactionResultWithId,
} from "../../core/types";
import { Abi } from "../../schema/contracts/custom";
import { TokenErc721ContractSchema } from "../../schema/contracts/token-erc721";
import { SDKOptions } from "../../schema/sdk-options";
import type { TokenERC721 } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumberish, CallOverrides, constants } from "ethers";
@@ -43,7 +43,7 @@ import { BigNumberish, CallOverrides, constants } from "ethers";
export class NFTCollection extends StandardErc721<TokenERC721> {
static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<
TokenERC721,
typeof TokenErc721ContractSchema
@@ -107,7 +107,7 @@ export class NFTCollection extends StandardErc721<TokenERC721> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<TokenERC721>(
network,
@@ -27,11 +27,11 @@ import {
TransactionResultWithId,
} from "../../core/types";
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
import { Abi } from "../../schema/contracts/custom";
import { DropErc721ContractSchema } from "../../schema/contracts/drop-erc721";
import { SDKOptions } from "../../schema/sdk-options";
import { PrebuiltNFTDrop } from "../../types/eips";
import { UploadProgressEvent } from "../../types/events";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC721.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
@@ -52,7 +52,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
export class NFTDrop extends StandardErc721<PrebuiltNFTDrop> {
static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public encoder: ContractEncoder<PrebuiltNFTDrop>;
public estimator: GasCostEstimator<PrebuiltNFTDrop>;
public metadata: ContractMetadata<
@@ -161,7 +161,7 @@ export class NFTDrop extends StandardErc721<PrebuiltNFTDrop> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<PrebuiltNFTDrop>(
network,
@@ -1,5 +1,6 @@
import { QueryAllParams } from "../../../core/schema/QueryParams";
import { NFT } from "../../../core/schema/nft";
import { assertEnabled, detectContractFeature } from "../../common";
import {
fetchCurrencyMetadata,
hasERC20Allowance,
@@ -8,6 +9,7 @@ import {
import { isTokenApprovedForTransfer } from "../../common/marketplace";
import { uploadOrExtractURI } from "../../common/nft";
import { getRoleHash } from "../../common/role";
import { FEATURE_PACK_VRF } from "../../constants/thirdweb-features";
import { ContractEncoder } from "../../core/classes/contract-encoder";
import { ContractEvents } from "../../core/classes/contract-events";
import { ContractInterceptor } from "../../core/classes/contract-interceptor";
@@ -19,10 +21,12 @@ import { ContractWrapper } from "../../core/classes/contract-wrapper";
import { Erc1155 } from "../../core/classes/erc-1155";
import { StandardErc1155 } from "../../core/classes/erc-1155-standard";
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
import { PackVRF } from "../../core/classes/pack-vrf";
import {
NetworkOrSignerOrProvider,
TransactionResultWithId,
} from "../../core/types";
import { Abi } from "../../schema";
import { PackContractSchema } from "../../schema/contracts/packs";
import { SDKOptions } from "../../schema/sdk-options";
import {
@@ -32,8 +36,10 @@ import {
PackRewardsOutput,
PackRewardsOutputSchema,
} from "../../schema/tokens/pack";
import type { Pack as PackContract } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Pack.json";
import type {
IPackVRFDirect,
Pack as PackContract,
} from "@thirdweb-dev/contracts-js";
import { PackUpdatedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/IPack";
import {
ITokenBundle,
@@ -60,7 +66,7 @@ import { BigNumber, BigNumberish, CallOverrides, ethers } from "ethers";
export class Pack extends StandardErc1155<PackContract> {
static contractRoles = ["admin", "minter", "asset", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<PackContract, typeof PackContractSchema>;
public roles: ContractRoles<PackContract, typeof Pack.contractRoles[number]>;
public encoder: ContractEncoder<PackContract>;
@@ -92,12 +98,21 @@ export class Pack extends StandardErc1155<PackContract> {
public erc1155: Erc1155<PackContract>;
public owner: ContractOwner<PackContract>;
private _vrf?: PackVRF;
/**
* If enabled in the contract, use the Chainlink VRF functionality to open packs
*/
get vrf(): PackVRF {
return assertEnabled(this._vrf, FEATURE_PACK_VRF);
}
constructor(
network: NetworkOrSignerOrProvider,
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<PackContract>(
network,
@@ -131,6 +146,7 @@ export class Pack extends StandardErc1155<PackContract> {
this.events = new ContractEvents(this.contractWrapper);
this.interceptor = new ContractInterceptor(this.contractWrapper);
this.owner = new ContractOwner(this.contractWrapper);
this._vrf = this.detectVrf();
}
/**
@@ -138,6 +154,7 @@ export class Pack extends StandardErc1155<PackContract> {
*/
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
this.contractWrapper.updateSignerOrProvider(network);
this._vrf?.onNetworkUpdated(network);
}
getAddress(): string {
@@ -536,6 +553,11 @@ export class Pack extends StandardErc1155<PackContract> {
tokenId: BigNumberish,
amount: BigNumberish = 1,
): Promise<PackRewards> {
if (this._vrf) {
throw new Error(
"This contract is using Chainlink VRF, use `contract.vrf.open()` or `contract.vrf.openAndClaim()` instead",
);
}
const receipt = await this.contractWrapper.sendTransaction(
"openPack",
[tokenId, amount],
@@ -716,4 +738,19 @@ export class Pack extends StandardErc1155<PackContract> {
): Promise<any> {
return this.contractWrapper.call(functionName, ...args);
}
private detectVrf() {
if (
detectContractFeature<IPackVRFDirect>(this.contractWrapper, "PackVRF")
) {
return new PackVRF(
this.contractWrapper.getSignerOrProvider(),
this.contractWrapper.readContract.address,
this.storage,
this.contractWrapper.options,
this.chainId,
);
}
return undefined;
}
}
@@ -28,11 +28,11 @@ import {
TransactionResultWithId,
} from "../../core/types";
import { PaperCheckout } from "../../integrations/thirdweb-checkout";
import { Abi } from "../../schema/contracts/custom";
import { DropErc721ContractSchema } from "../../schema/contracts/drop-erc721";
import { SDKOptions } from "../../schema/sdk-options";
import { ClaimOptions, UploadProgressEvent } from "../../types";
import type { SignatureDrop as SignatureDropContract } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/SignatureDrop.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
@@ -55,7 +55,7 @@ import { BigNumber, BigNumberish, CallOverrides, constants } from "ethers";
export class SignatureDrop extends StandardErc721<SignatureDropContract> {
static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public erc721: Erc721<SignatureDropContract>;
public owner: ContractOwner<SignatureDropContract>;
public encoder: ContractEncoder<SignatureDropContract>;
@@ -171,7 +171,7 @@ export class SignatureDrop extends StandardErc721<SignatureDropContract> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<SignatureDropContract>(
network,
@@ -8,6 +8,7 @@ import { ContractWrapper } from "../../core/classes/contract-wrapper";
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
import { UpdateableNetwork } from "../../core/interfaces/contract";
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
import { Abi } from "../../schema/contracts/custom";
import { SplitsContractSchema } from "../../schema/contracts/splits";
import { SDKOptions } from "../../schema/sdk-options";
import { SplitRecipient } from "../../types/SplitRecipient";
@@ -17,7 +18,6 @@ import type {
Split as SplitContract,
} from "@thirdweb-dev/contracts-js";
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/Split.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, CallOverrides, Contract } from "ethers";
@@ -41,7 +41,7 @@ export class Split implements UpdateableNetwork {
private contractWrapper: ContractWrapper<SplitContract>;
private storage: ThirdwebStorage;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<SplitContract, typeof SplitsContractSchema>;
public encoder: ContractEncoder<SplitContract>;
public estimator: GasCostEstimator<SplitContract>;
@@ -65,7 +65,7 @@ export class Split implements UpdateableNetwork {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<SplitContract>(
network,
@@ -11,11 +11,11 @@ import { DropClaimConditions } from "../../core/classes/drop-claim-conditions";
import { StandardErc20 } from "../../core/classes/erc-20-standard";
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
import { Abi } from "../../schema/contracts/custom";
import { DropErc20ContractSchema } from "../../schema/contracts/drop-erc20";
import { SDKOptions } from "../../schema/sdk-options";
import { Amount, CurrencyValue } from "../../types";
import { PrebuiltTokenDrop } from "../../types/eips";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/DropERC20.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { CallOverrides, constants } from "ethers";
@@ -35,7 +35,7 @@ import { CallOverrides, constants } from "ethers";
export class TokenDrop extends StandardErc20<PrebuiltTokenDrop> {
static contractRoles = ["admin", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<
PrebuiltTokenDrop,
typeof DropErc20ContractSchema
@@ -82,7 +82,7 @@ export class TokenDrop extends StandardErc20<PrebuiltTokenDrop> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<PrebuiltTokenDrop>(
network,
@@ -12,12 +12,12 @@ import { Erc20SignatureMintable } from "../../core/classes/erc-20-signature-mint
import { StandardErc20 } from "../../core/classes/erc-20-standard";
import { GasCostEstimator } from "../../core/classes/gas-cost-estimator";
import { NetworkOrSignerOrProvider, TransactionResult } from "../../core/types";
import { Abi } from "../../schema/contracts/custom";
import { TokenErc20ContractSchema } from "../../schema/contracts/token-erc20";
import { SDKOptions } from "../../schema/sdk-options";
import { TokenMintInput } from "../../schema/tokens/token";
import { Amount, CurrencyValue } from "../../types";
import type { TokenERC20 } from "@thirdweb-dev/contracts-js";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { CallOverrides, constants } from "ethers";
@@ -38,7 +38,7 @@ import { CallOverrides, constants } from "ethers";
export class Token extends StandardErc20<TokenERC20> {
static contractRoles = ["admin", "minter", "transfer"] as const;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<
TokenERC20,
typeof TokenErc20ContractSchema
@@ -74,7 +74,7 @@ export class Token extends StandardErc20<TokenERC20> {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<TokenERC20>(
network,
@@ -15,6 +15,7 @@ import {
TransactionResultWithId,
} from "../../core/types";
import { VoteType } from "../../enums";
import { Abi } from "../../schema";
import { VoteContractSchema } from "../../schema/contracts/vote";
import { SDKOptions } from "../../schema/sdk-options";
import { CurrencyValue } from "../../types/currency";
@@ -26,7 +27,6 @@ import {
} from "../../types/vote";
import type { IERC20, VoteERC20 } from "@thirdweb-dev/contracts-js";
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
import type ABI from "@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json";
import { ProposalCreatedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/VoteERC20";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import {
@@ -55,7 +55,7 @@ export class Vote implements UpdateableNetwork {
private contractWrapper: ContractWrapper<VoteERC20>;
private storage: ThirdwebStorage;
public abi: typeof ABI;
public abi: Abi;
public metadata: ContractMetadata<VoteERC20, typeof VoteContractSchema>;
public encoder: ContractEncoder<VoteERC20>;
public estimator: GasCostEstimator<VoteERC20>;
@@ -75,7 +75,7 @@ export class Vote implements UpdateableNetwork {
address: string,
storage: ThirdwebStorage,
options: SDKOptions = {},
abi: typeof ABI,
abi: Abi,
chainId: number,
contractWrapper = new ContractWrapper<VoteERC20>(
network,
@@ -4,7 +4,7 @@ import {
fetchExtendedReleaseMetadata,
fetchPreDeployMetadata,
} from "../../common/index";
import { ChainId } from "../../constants";
import { ChainId, EventType } from "../../constants";
import { getContractAddressByChainId } from "../../constants/addresses";
import {
EditionDropInitializer,
@@ -24,6 +24,7 @@ import {
} from "../../contracts";
import { FactoryDeploymentSchema } from "../../schema/contracts/custom";
import { SDKOptions } from "../../schema/sdk-options";
import { DeployEvent, DeployEvents } from "../../types";
import {
MarketplaceContractDeployMetadata,
MultiwrapContractDeployMetadata,
@@ -39,7 +40,6 @@ import {
PrebuiltContractType,
} from "../types";
import { ContractFactory } from "./factory";
import { FactoryEvents } from "./factory-events";
import { ContractRegistry } from "./registry";
import { RPCConnectionHandler } from "./rpc-connection-handler";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
@@ -50,6 +50,7 @@ import {
ContractInterface,
ethers,
} from "ethers";
import { EventEmitter } from "eventemitter3";
import invariant from "tiny-invariant";
import { z } from "zod";
@@ -68,8 +69,16 @@ export class ContractDeployer extends RPCConnectionHandler {
* should never be accessed directly, use {@link ContractDeployer.getRegistry} instead
*/
private _registry: Promise<ContractRegistry> | undefined;
public events: FactoryEvents | undefined;
private storage: ThirdwebStorage;
private events: EventEmitter<DeployEvents>;
private transactionListener = (event: any) => {
if (event.status === "submitted") {
this.events.emit("contractDeployed", {
status: "submitted",
transactionHash: event.transactionHash,
});
}
};
constructor(
network: NetworkOrSignerOrProvider,
@@ -78,7 +87,7 @@ export class ContractDeployer extends RPCConnectionHandler {
) {
super(network, options);
this.storage = storage;
this.events = new EventEmitter();
// Initialize factory and registry (we don't need to make these calls async)
this.getFactory();
this.getRegistry();
@@ -427,7 +436,15 @@ export class ContractDeployer extends RPCConnectionHandler {
} catch (e) {
parsedVersion = undefined;
}
return await factory.deploy(contractType, parsedMetadata, parsedVersion);
factory.on(EventType.Transaction, this.transactionListener);
const deployedAddress = await factory.deploy(
contractType,
parsedMetadata,
this.events,
parsedVersion,
);
factory.off(EventType.Transaction, this.transactionListener);
return deployedAddress;
}
//
@@ -517,12 +534,16 @@ export class ContractDeployer extends RPCConnectionHandler {
this.storage,
this.options,
);
return await proxyFactory.deployProxyByImplementation(
proxyFactory.on(EventType.Transaction, this.transactionListener);
const deployedAddress = await proxyFactory.deployProxyByImplementation(
implementationAddress,
implementationAbi,
initializerFunction,
initializerArgs,
this.events,
);
proxyFactory.off(EventType.Transaction, this.transactionListener);
return deployedAddress;
}
/**
@@ -602,8 +623,6 @@ export class ContractDeployer extends RPCConnectionHandler {
this.storage,
this.options,
);
this.events = new FactoryEvents(factory);
return factory;
}));
}
@@ -800,8 +819,39 @@ export class ContractDeployer extends RPCConnectionHandler {
const deployer = await new ethers.ContractFactory(abi, bytecode)
.connect(signer)
.deploy(...constructorParams);
this.events.emit("contractDeployed", {
status: "submitted",
transactionHash: deployer.deployTransaction.hash,
});
const deployedContract = await deployer.deployed();
// TODO parse transaction receipt
this.events.emit("contractDeployed", {
status: "completed",
contractAddress: deployedContract.address,
transactionHash: deployedContract.deployTransaction.hash,
});
return deployedContract.address;
}
/**
* Listen to all deploy transactions from this deployer
* @param listener the listener to add
*/
public addDeployListener(listener: (event: DeployEvent) => void) {
this.events.on("contractDeployed", listener);
}
/**
* Remove a deploy listener
* @param listener the listener to remove
*/
public removeDeployListener(listener: (event: DeployEvent) => void) {
this.events.off("contractDeployed", listener);
}
/**
* Remove all deploy listeners
*/
public removeAllDeployListeners() {
this.events.removeAllListeners("contractDeployed");
}
}
@@ -85,7 +85,11 @@ export class ContractEvents<TContract extends BaseContract> {
const parsedLog =
this.contractWrapper.readContract.interface.parseLog(log);
listener(
this.toContractEvent(parsedLog.eventFragment, parsedLog.args, log),
this.toContractEvent<TEvent>(
parsedLog.eventFragment,
parsedLog.args,
log,
),
);
};
@@ -333,15 +337,31 @@ export class ContractEvents<TContract extends BaseContract> {
const results: Record<string, any> = {};
event.inputs.forEach((param, index) => {
if (Array.isArray(args[index])) {
const obj: Record<string, unknown> = {};
const components = param.components;
if (components) {
const arr = args[index];
for (let i = 0; i < components.length; i++) {
const name = components[i].name;
obj[name] = arr[i];
if (param.type === "tuple[]") {
// tuple[]
const objArray: Record<string, unknown>[] = [];
for (let i = 0; i < arr.length; i++) {
const tuple = arr[i];
const obj: Record<string, unknown> = {};
for (let j = 0; j < components.length; j++) {
const name = components[j].name;
obj[name] = tuple[j];
}
objArray.push(obj);
}
results[param.name] = objArray;
} else {
// simple tuple
const obj: Record<string, unknown> = {};
for (let i = 0; i < components.length; i++) {
const name = components[i].name;
obj[name] = arr[i];
}
results[param.name] = obj;
}
results[param.name] = obj;
}
} else {
results[param.name] = args[index];
@@ -1,8 +1,8 @@
import {
detectContractFeature,
ExtensionNotImplementedError,
fetchContractMetadataFromAddress,
hasFunction,
fetchContractMetadataFromAddress,
} from "../../common";
import { FEATURE_METADATA } from "../../constants/thirdweb-features";
import { DetectableFeature } from "../interfaces/DetectableFeature";
@@ -7,7 +7,7 @@ import {
fetchRawPredeployMetadata,
fetchSourceFilesFromMetadata,
resolveContractUriFromAddress,
} from "../../common/feature-detection";
} from "../../common";
import { isIncrementalVersion } from "../../common/version-checker";
import { getContractPublisherAddress } from "../../constants";
import {
@@ -1,38 +0,0 @@
import { ContractEvents } from "./contract-events";
import { ContractWrapper } from "./contract-wrapper";
import type { TWFactory } from "@thirdweb-dev/contracts-js";
import { ProxyDeployedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/TWFactory";
interface DeployEvent {
transactionHash: string;
contractAddress: string;
}
export class FactoryEvents extends ContractEvents<TWFactory> {
constructor(contractWrapper: ContractWrapper<TWFactory>) {
super(contractWrapper);
}
public addDeployListener(listener: (event: DeployEvent) => void) {
this.addTransactionListener(async (event) => {
if (!event.transactionHash) {
return;
}
const receipt = await this.contractWrapper
.getProvider()
.getTransactionReceipt(event.transactionHash);
const events = this.contractWrapper.parseLogs<ProxyDeployedEvent>(
"ProxyDeployed",
receipt.logs,
);
if (events.length > 0) {
listener({
...event,
contractAddress: events[0].args.proxy,
});
}
});
}
}
+21 -2
View File
@@ -19,6 +19,7 @@ import {
VoteInitializer,
} from "../../contracts";
import { SDKOptions } from "../../schema/sdk-options";
import { DeployEvents } from "../../types";
import {
DeploySchemaForPrebuiltContractType,
NetworkOrSignerOrProvider,
@@ -36,6 +37,7 @@ import {
ContractInterface,
ethers,
} from "ethers";
import { EventEmitter } from "eventemitter3";
import { z } from "zod";
/**
@@ -75,6 +77,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
contractMetadata: z.input<
DeploySchemaForPrebuiltContractType<TContractType>
>,
eventEmitter: EventEmitter<DeployEvents>,
version?: number,
): Promise<string> {
const contract = PREBUILT_CONTRACTS_MAP[contractType];
@@ -98,6 +101,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
const ABI = await contract.getAbi(
implementationAddress,
this.getProvider(),
this.storage,
);
const encodedFunc = Contract.getInterface(ABI).encodeFunctionData(
@@ -121,7 +125,14 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
throw new Error("No ProxyDeployed event found");
}
return events[0].args.proxy;
const contractAddress = events[0].args.proxy;
eventEmitter.emit("contractDeployed", {
status: "completed",
contractAddress,
transactionHash: receipt.transactionHash,
});
return contractAddress;
}
// TODO once IContractFactory is implemented, this can be probably be moved to its own class
@@ -130,6 +141,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
implementationAbi: ContractInterface,
initializerFunction: string,
initializerArgs: any[],
eventEmitter: EventEmitter<DeployEvents>,
): Promise<string> {
const encodedFunc = Contract.getInterface(
implementationAbi,
@@ -150,7 +162,14 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
throw new Error("No ProxyDeployed event found");
}
return events[0].args.proxy;
const contractAddress = events[0].args.proxy;
eventEmitter.emit("contractDeployed", {
status: "completed",
contractAddress,
transactionHash: receipt.transactionHash,
});
return contractAddress;
}
/**
@@ -0,0 +1,141 @@
import { NetworkOrSignerOrProvider, TransactionResult } from "..";
import { getMultichainRegistryAddress } from "../../constants/addresses";
import { PublishedMetadata } from "../../schema/contracts/custom";
import { SDKOptions } from "../../schema/sdk-options";
import { AddContractInput, ContractInput, DeployedContract } from "../../types";
import { ContractWrapper } from "./contract-wrapper";
import type {
TWMultichainRegistryRouter,
TWMultichainRegistryLogic,
} from "@thirdweb-dev/contracts-js";
import TWRegistryABI from "@thirdweb-dev/contracts-js/dist/abis/TWMultichainRegistryLogic.json";
import TWRegistryRouterABI from "@thirdweb-dev/contracts-js/dist/abis/TWMultichainRegistryRouter.json";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { constants, utils } from "ethers";
/**
* @internal
*/
export class MultichainRegistry {
private registryLogic: ContractWrapper<TWMultichainRegistryLogic>;
private registryRouter: ContractWrapper<TWMultichainRegistryRouter>;
private storage: ThirdwebStorage;
constructor(
network: NetworkOrSignerOrProvider,
storage: ThirdwebStorage,
options: SDKOptions = {},
) {
this.storage = storage;
this.registryLogic = new ContractWrapper<TWMultichainRegistryLogic>(
network,
getMultichainRegistryAddress(),
TWRegistryABI,
options,
);
this.registryRouter = new ContractWrapper<TWMultichainRegistryRouter>(
network,
getMultichainRegistryAddress(),
TWRegistryRouterABI,
options,
);
}
public async updateSigner(signer: NetworkOrSignerOrProvider) {
this.registryLogic.updateSignerOrProvider(signer);
this.registryRouter.updateSignerOrProvider(signer);
}
public async getContractMetadataURI(
chainId: number,
address: string,
): Promise<string> {
return await this.registryLogic.readContract.getMetadataUri(
chainId,
address,
);
}
public async getContractMetadata(
chainId: number,
address: string,
): Promise<PublishedMetadata> {
const uri = await this.getContractMetadataURI(chainId, address);
if (!uri) {
throw new Error(
`No metadata URI found for contract ${address} on chain ${chainId}`,
);
}
// TODO define the metadata JSON schema
return await this.storage.downloadJSON<PublishedMetadata>(uri);
}
public async getContractAddresses(
walletAddress: string,
): Promise<DeployedContract[]> {
return (await this.registryLogic.readContract.getAll(walletAddress))
.filter(
(result) =>
utils.isAddress(result.deploymentAddress) &&
result.deploymentAddress.toLowerCase() !== constants.AddressZero,
)
.map((result) => ({
address: result.deploymentAddress,
chainId: result.chainId.toNumber(),
}));
}
public async addContract(
contract: AddContractInput,
): Promise<TransactionResult> {
return await this.addContracts([contract]);
}
public async addContracts(
contracts: AddContractInput[],
): Promise<TransactionResult> {
const deployerAddress = await this.registryRouter.getSignerAddress();
const encoded: string[] = [];
contracts.forEach((contact) => {
encoded.push(
this.registryLogic.readContract.interface.encodeFunctionData("add", [
deployerAddress,
contact.address,
contact.chainId,
contact.metadataURI || "",
]),
);
});
return {
receipt: await this.registryRouter.multiCall(encoded),
};
}
public async removeContract(
contract: ContractInput,
): Promise<TransactionResult> {
return await this.removeContracts([contract]);
}
public async removeContracts(
contracts: ContractInput[],
): Promise<TransactionResult> {
const deployerAddress = await this.registryRouter.getSignerAddress();
const encoded: string[] = [];
contracts.forEach((contract) => {
encoded.push(
this.registryLogic.readContract.interface.encodeFunctionData("remove", [
deployerAddress,
contract.address,
contract.chainId,
]),
);
});
return {
receipt: await this.registryRouter.multiCall(encoded),
};
}
}
@@ -0,0 +1,278 @@
import {
ContractEvents,
NetworkOrSignerOrProvider,
TransactionResultWithId,
} from "..";
import { fetchCurrencyMetadata } from "../../common";
import { LINK_TOKEN_ADDRESS } from "../../constants";
import { FEATURE_PACK_VRF } from "../../constants/thirdweb-features";
import { PackRewards, SDKOptions } from "../../schema";
import { Amount, CurrencyValue } from "../../types";
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { UpdateableNetwork } from "../interfaces/contract";
import { ContractWrapper } from "./contract-wrapper";
import { Erc20 } from "./erc-20";
import type { ERC20, PackVRFDirect } from "@thirdweb-dev/contracts-js";
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/ERC20.json";
import IPackAbi from "@thirdweb-dev/contracts-js/dist/abis/IPackVRFDirect.json";
import {
ITokenBundle,
PackOpenedEvent,
PackOpenedEventObject,
PackOpenRequestedEvent,
} from "@thirdweb-dev/contracts-js/dist/declarations/src/IPackVRFDirect";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, ethers } from "ethers";
export class PackVRF implements UpdateableNetwork, DetectableFeature {
featureName = FEATURE_PACK_VRF.name;
private contractWrapper: ContractWrapper<PackVRFDirect>;
private storage: ThirdwebStorage;
public chainId: number;
private events: ContractEvents<PackVRFDirect>;
constructor(
network: NetworkOrSignerOrProvider,
address: string,
storage: ThirdwebStorage,
options: SDKOptions,
chainId: number,
contractWrapper: ContractWrapper<PackVRFDirect> = new ContractWrapper(
network,
address,
IPackAbi,
options,
),
) {
this.contractWrapper = contractWrapper;
this.storage = storage;
this.chainId = chainId;
this.events = new ContractEvents(this.contractWrapper);
}
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
this.contractWrapper.updateSignerOrProvider(network);
}
getAddress(): string {
return this.contractWrapper.readContract.address;
}
/**
* Open a pack using Chainlink VRFs random number generation
* This will return a transaction result with the requestId of the open request, NOT the contents of the pack
* To get the contents of the pack, you must call claimRewards once the VRF request has been fulfilled
* You can use the canClaimRewards method to check if the VRF request has been fulfilled
* @param tokenId
* @param amount
* @returns
*/
public async open(
tokenId: BigNumberish,
amount: BigNumberish = 1,
): Promise<TransactionResultWithId> {
const receipt = await this.contractWrapper.sendTransaction(
"openPack",
[tokenId, amount],
{
// Higher gas limit for opening packs
gasLimit: 500000,
},
);
let id = BigNumber.from(0);
try {
const event = this.contractWrapper.parseLogs<PackOpenRequestedEvent>(
"PackOpenRequested",
receipt?.logs,
);
id = event[0].args.requestId;
} catch (e) {}
return {
receipt,
id,
};
}
/**
* Claim the rewards from a pack that has been opened
* This will return the contents of the pack
* Make sure to check if the VRF request has been fulfilled using canClaimRewards() before calling this method
* @returns the random rewards from opening a pack
*/
public async claimRewards(): Promise<PackRewards> {
const receipt = await this.contractWrapper.sendTransaction(
"claimRewards",
[],
{
// Higher gas limit for opening packs
gasLimit: 500000,
},
);
const event = this.contractWrapper.parseLogs<PackOpenedEvent>(
"PackOpened",
receipt?.logs,
);
if (event.length === 0) {
throw new Error("PackOpened event not found");
}
const rewards = event[0].args.rewardUnitsDistributed;
return this.parseRewards(rewards);
}
private async parseRewards(
rewards: ITokenBundle.TokenStructOutput[],
): Promise<PackRewards> {
const erc20Rewards: PackRewards["erc20Rewards"] = [];
const erc721Rewards: PackRewards["erc721Rewards"] = [];
const erc1155Rewards: PackRewards["erc1155Rewards"] = [];
for (const reward of rewards) {
switch (reward.tokenType) {
case 0: {
const tokenMetadata = await fetchCurrencyMetadata(
this.contractWrapper.getProvider(),
reward.assetContract,
);
erc20Rewards.push({
contractAddress: reward.assetContract,
quantityPerReward: ethers.utils
.formatUnits(reward.totalAmount, tokenMetadata.decimals)
.toString(),
});
break;
}
case 1: {
erc721Rewards.push({
contractAddress: reward.assetContract,
tokenId: reward.tokenId.toString(),
});
break;
}
case 2: {
erc1155Rewards.push({
contractAddress: reward.assetContract,
tokenId: reward.tokenId.toString(),
quantityPerReward: reward.totalAmount.toString(),
});
break;
}
}
}
return {
erc20Rewards,
erc721Rewards,
erc1155Rewards,
};
}
/**
* Setup a listener that will be called everytime a pack is opened in this contract
* @param callback the listener to call when a pack is opened
* @returns a unsubscribe function to cleanup the listener
*/
public async addPackOpenEventListener(
callback: (
packId: string,
openerAddress: string,
rewards: PackRewards,
) => void,
) {
return this.events.addEventListener<PackOpenedEventObject>(
"PackOpened",
async (event) => {
callback(
event.data.packId.toString(),
event.data.opener,
await this.parseRewards(event.data.rewardUnitsDistributed),
);
},
);
}
/**
* Check if the connected address can claim rewards after opening a pack
* @param claimerAddress Optional: the address to check if they can claim rewards, defaults to the connected address
* @returns whether the connected address can claim rewards after opening a pack
*/
public async canClaimRewards(claimerAddress?: string): Promise<boolean> {
const address =
claimerAddress || (await this.contractWrapper.getSignerAddress());
return await this.contractWrapper.readContract.canClaimRewards(address);
}
/**
* Open a pack and claim the rewards in one transaction.
* This function will only start the flow of opening a pack, the rewards will be granted automatically to the connected address after VRF request is fulfilled
* @param packId The id of the pack to open
* @param amount Optional: the amount of packs to open, defaults to 1
* @param gasLimit Optional: the gas limit to use for the VRF callback transaction, defaults to 500000
* @returns
*/
public async openAndClaim(
packId: BigNumberish,
amount: BigNumberish = 1,
gasLimit: BigNumberish = 500000,
): Promise<TransactionResultWithId> {
const receipt = await this.contractWrapper.sendTransaction(
"openPackAndClaimRewards",
[packId, amount, gasLimit],
{
// Higher gas limit for opening packs
gasLimit: BigNumber.from(500000),
},
);
let id = BigNumber.from(0);
try {
const event = this.contractWrapper.parseLogs<PackOpenRequestedEvent>(
"PackOpenRequested",
receipt?.logs,
);
id = event[0].args.requestId;
} catch (e) {}
return {
receipt,
id,
};
}
/**
* Get the balance of LINK in the contract
* @returns the balance of LINK in the contract
*/
public async getLinkBalance(): Promise<CurrencyValue> {
return this.getLinkContract().balanceOf(
this.contractWrapper.readContract.address,
);
}
/**
* Transfer LINK to this contract
* @param amount the amount of LINK to transfer to the contract
*/
public async transferLink(amount: Amount) {
await this.getLinkContract().transfer(
this.contractWrapper.readContract.address,
amount,
);
}
private getLinkContract(): Erc20 {
const linkAddress = LINK_TOKEN_ADDRESS[this.chainId];
if (!linkAddress) {
throw new Error(
`No LINK token address found for chainId ${this.chainId}`,
);
}
const contract = new ContractWrapper<ERC20>(
this.contractWrapper.getSignerOrProvider(),
linkAddress,
ERC20Abi,
this.contractWrapper.options,
);
return new Erc20(contract, this.storage, this.chainId);
}
}
+25 -3
View File
@@ -1,4 +1,5 @@
import { fetchCurrencyValue } from "../common/currency";
import { getCompositePluginABI } from "../common/plugin";
import {
ChainOrRpc,
getProviderForNetwork,
@@ -10,6 +11,7 @@ import {
PREBUILT_CONTRACTS_MAP,
} from "../contracts";
import { SmartContract } from "../contracts/smart-contract";
import { AbiSchema } from "../schema";
import { SDKOptions } from "../schema/sdk-options";
import { CurrencyValue } from "../types/index";
import type { AbstractWallet } from "../wallets";
@@ -17,6 +19,7 @@ import { WalletAuthenticator } from "./auth/wallet-authenticator";
import type { ContractMetadata } from "./classes";
import { ContractDeployer } from "./classes/contract-deployer";
import { ContractPublisher } from "./classes/contract-publisher";
import { MultichainRegistry } from "./classes/multichain-registry";
import {
getSignerAndProvider,
RPCConnectionHandler,
@@ -162,6 +165,10 @@ export class ThirdwebSDK extends RPCConnectionHandler {
* New contract deployer
*/
public deployer: ContractDeployer;
/**
* The registry of deployed contracts
*/
public multiChainRegistry: MultichainRegistry;
/**
* Interact with the connected wallet
*/
@@ -187,6 +194,11 @@ export class ThirdwebSDK extends RPCConnectionHandler {
this.wallet = new UserWallet(signerOrProvider, options);
this.deployer = new ContractDeployer(signerOrProvider, options, storage);
this.auth = new WalletAuthenticator(signerOrProvider, this.wallet, options);
this.multiChainRegistry = new MultichainRegistry(
signerOrProvider,
this.storageHandler,
this.options,
);
this._publisher = new ContractPublisher(
signerOrProvider,
this.options,
@@ -425,7 +437,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
if (!contractTypeOrABI || contractTypeOrABI === "custom") {
const resolvedContractType = await this.resolveContractType(address);
if (resolvedContractType === "custom") {
// if it's a custom contract we gotta fetch the compilet metadata
// if it's a custom contract we gotta fetch the compiler metadata
try {
const publisher = this.getPublisher();
const metadata = await publisher.fetchCompilerMetadataFromAddress(
@@ -439,7 +451,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
// otherwise if it's a prebuilt contract we can just use the contract type
const contractAbi = await PREBUILT_CONTRACTS_MAP[
resolvedContractType
].getAbi(address, this.getProvider());
].getAbi(address, this.getProvider(), this.storage);
newContract = await this.getContractFromAbi(address, contractAbi);
}
}
@@ -515,6 +527,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
* ```
*/
public async getContractList(walletAddress: string) {
// TODO - this only reads from the current registry chain, not the multichain registry
const addresses =
(await (
await this.deployer.getRegistry()
@@ -574,6 +587,7 @@ export class ThirdwebSDK extends RPCConnectionHandler {
this.auth.updateSignerOrProvider(this.getSignerOrProvider());
this.deployer.updateSignerOrProvider(this.getSignerOrProvider());
this._publisher.updateSignerOrProvider(this.getSignerOrProvider());
this.multiChainRegistry.updateSigner(this.getSignerOrProvider());
for (const [, contract] of this.contractCache) {
contract.onNetworkUpdated(this.getSignerOrProvider());
}
@@ -605,11 +619,19 @@ export class ThirdwebSDK extends RPCConnectionHandler {
this.getSignerOrProvider(),
this.options,
);
const parsedABI = typeof abi === "string" ? JSON.parse(abi) : abi;
// TODO we still might want to lazy-fy this
const contract = new SmartContract(
this.getSignerOrProvider(),
address,
abi,
await getCompositePluginABI(
address,
AbiSchema.parse(parsedABI),
provider,
this.options,
this.storage,
),
this.storageHandler,
this.options,
(await provider.getNetwork()).chainId,
+1 -2
View File
@@ -5,14 +5,13 @@ export type { ContractType, NetworkOrSignerOrProvider } from "./core/types";
export type { Role } from "./common/role";
export { CommonContractSchema } from "./schema/contracts/common";
export * from "./schema/contracts/custom";
export * from "./schema/contracts/common/claim-conditions";
export * from "./schema/tokens/common/properties";
export * from "./constants/chains";
export * from "./schema/tokens/token";
export * from "./schema/tokens/edition";
export * from "./schema/contracts/common";
export * from "./schema/contracts/custom";
export * from "./schema/auth";
export type {
SDKOptions,
@@ -0,0 +1,12 @@
import { BytesLikeSchema } from "../../../../core/schema/shared";
import { AddressSchema } from "../../shared";
import { z } from "zod";
/**
* @internal
*/
export const PluginMapInput = z.object({
functionSelector: BytesLikeSchema,
functionSignature: z.string(),
pluginAddress: AddressSchema,
});
@@ -1,3 +1,4 @@
export * from "./custom";
export * from "./common";
export * from "./drop-erc721";
export * from "./drop-erc1155";
@@ -8,4 +9,3 @@ export * from "./token-erc20";
export * from "./token-erc721";
export * from "./token-erc1155";
export * from "./vote";
export * from "./custom";
@@ -0,0 +1,9 @@
export interface DeployEvent {
status: "submitted" | "completed";
transactionHash: string;
contractAddress?: string;
}
export interface DeployEvents {
contractDeployed: [DeployEvent];
}
@@ -1 +1,2 @@
export * from "./deploy-metadata";
export * from "./deploy-events";
+1
View File
@@ -8,3 +8,4 @@ export * from "./SplitRecipient";
export * from "./deploy";
export * from "./events";
export * from "./multiwrap";
export * from "./registry";
+8
View File
@@ -0,0 +1,8 @@
import { PluginMapInput } from "../schema/contracts/common/plugins";
import { z } from "zod";
/**
* Input model to pass a list of extension-addresses + function-selectors
* @public
*/
export type Plugin = z.input<typeof PluginMapInput>;
+13
View File
@@ -0,0 +1,13 @@
export type ContractInput = {
address: string;
chainId: number;
};
export type AddContractInput = ContractInput & {
metadataURI?: string;
};
export type DeployedContract = {
address: string;
chainId: number;
};
+71
View File
@@ -1,4 +1,5 @@
import {
Abi,
ChainId,
CONTRACTS_MAP,
ContractType,
@@ -18,6 +19,7 @@ import {
TokenInitializer,
VoteInitializer,
} from "../../src/evm";
import { Plugin } from "../../src/evm/types/plugins";
import { MockStorage } from "./mock/MockStorage";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import {
@@ -44,10 +46,17 @@ import {
TWFactory__factory,
TWRegistry,
TWRegistry__factory,
TWMultichainRegistryRouter,
TWMultichainRegistryRouter__factory,
TWMultichainRegistryLogic,
TWMultichainRegistryLogic__factory,
PluginMap,
PluginMap__factory,
VoteERC20__factory,
} from "@thirdweb-dev/contracts-js";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { ethers } from "ethers";
import { FormatTypes, FunctionFragment, Interface } from "ethers/lib/utils";
import hardhat from "hardhat";
// it's there, trust me bro
@@ -227,6 +236,10 @@ export const mochaHooks = {
process.env.factoryAddress = thirdwebFactoryDeployer.address;
// eslint-disable-next-line turbo/no-undeclared-env-vars
process.env.contractPublisherAddress = contractPublisher.address;
// eslint-disable-next-line turbo/no-undeclared-env-vars
process.env.multiChainRegistryAddress = await setupMultichainRegistry(
trustedForwarderAddress,
);
storage = MockStorage();
sdk = new ThirdwebSDK(
@@ -241,6 +254,64 @@ export const mochaHooks = {
},
};
const generatePluginFunctions = (
pluginAddress: string,
pluginAbi: Abi,
): Plugin[] => {
const pluginInterface = new ethers.utils.Interface(pluginAbi);
const pluginFunctions: Plugin[] = [];
// TODO - filter out common functions like _msgSender(), contractType(), etc.
for (const fnFragment of Object.values(pluginInterface.functions)) {
const fn = pluginInterface.getFunction(fnFragment.name);
pluginFunctions.push({
functionSelector: pluginInterface.getSighash(fn),
functionSignature:
fn.name + "(" + fn.inputs.map((i) => i.type).join(",") + ")",
pluginAddress,
});
}
return pluginFunctions;
};
// Setup multichain registry for tests
async function setupMultichainRegistry(
trustedForwarderAddress: string,
): Promise<string> {
const multichainRegistryLogicDeployer = (await new ethers.ContractFactory(
TWMultichainRegistryLogic__factory.abi,
TWMultichainRegistryLogic__factory.bytecode,
)
.connect(signer)
.deploy()) as TWMultichainRegistryLogic;
const multichainRegistryLogic =
await multichainRegistryLogicDeployer.deployed();
const plugins: Plugin[] = generatePluginFunctions(
multichainRegistryLogic.address,
TWMultichainRegistryLogic__factory.abi,
);
const pluginMapDeployer = (await new ethers.ContractFactory(
PluginMap__factory.abi,
PluginMap__factory.bytecode,
)
.connect(signer)
.deploy(plugins)) as PluginMap;
const pluginMap = await pluginMapDeployer.deployed();
const multichainRegistryRouterDeployer = (await new ethers.ContractFactory(
TWMultichainRegistryRouter__factory.abi,
TWMultichainRegistryRouter__factory.bytecode,
)
.connect(signer)
.deploy(pluginMap.address, [
trustedForwarderAddress,
])) as TWMultichainRegistryRouter;
const multichainRegistryRouter =
await multichainRegistryRouterDeployer.deployed();
return multichainRegistryRouter.address;
}
export {
ipfsGatewayUrl,
sdk,
+17 -5
View File
@@ -8,6 +8,10 @@ import {
import { sdk, signers } from "./before-setup";
import { AddressZero } from "@ethersproject/constants";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import {
MockContractPublisher__factory,
TWFactory__factory,
} from "@thirdweb-dev/contracts-js";
import { expect } from "chai";
import { ethers } from "ethers";
@@ -92,17 +96,25 @@ describe("Events", async () => {
it("Should emit deploy events", async () => {
// eslint-disable-next-line no-unused-expressions
expect(sdk.deployer.events).to.not.be.undefined;
let txAddress = "";
sdk.deployer.events?.addDeployListener((event) => {
txAddress = event.contractAddress;
const txAddress: string[] = [];
sdk.deployer.addDeployListener((event) => {
if (event.status === "completed") {
txAddress.push(event.contractAddress || "");
}
});
const address = await sdk.deployer.deployMarketplace({
name: "Marketplace",
});
const secondAddress = await sdk.deployer.deployContractWithAbi(
MockContractPublisher__factory.abi,
MockContractPublisher__factory.bytecode,
[],
);
// Wait for the async listener to get called
await new Promise((res) => setTimeout(res, 2000));
expect(txAddress).to.equal(address);
sdk.deployer.removeAllDeployListeners();
expect(txAddress[0]).to.equal(address);
expect(txAddress[1]).to.equal(secondAddress);
});
it("should emit Contract events", async () => {
+1 -1
View File
@@ -14,7 +14,7 @@ import {
TokenERC721__factory,
} from "@thirdweb-dev/contracts-js";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { assert, expect } from "chai";
import { expect } from "chai";
import { ethers } from "ethers";
import { readFileSync } from "fs";
import invariant from "tiny-invariant";
+44
View File
@@ -1,11 +1,17 @@
import { getMultichainRegistryAddress, ThirdwebSDK } from "../../src/evm";
import { joinABIs } from "../../src/evm/common/plugin";
import { MultichainRegistry } from "../../src/evm/core/classes/multichain-registry";
import { ContractRegistry } from "../../src/evm/core/classes/registry";
import { sdk, signers } from "./before-setup";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import RouterABI from "@thirdweb-dev/contracts-js/dist/abis/Router.json";
import { expect } from "chai";
describe("Contract Registry", () => {
let registry: ContractRegistry;
let multichainRegistry: MultichainRegistry;
let adminWallet: SignerWithAddress;
let address: string;
@@ -14,6 +20,44 @@ describe("Contract Registry", () => {
[adminWallet] = signers;
});
it("multichain registry", async () => {
sdk.updateSignerOrProvider(adminWallet);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
multichainRegistry = sdk.multiChainRegistry;
console.log(
"multichain registry address: ",
getMultichainRegistryAddress(),
);
address = await sdk.deployer.deployNFTCollection({
name: "Test1",
primary_sale_recipient: adminWallet.address,
});
const chainId: number = await adminWallet.getChainId();
const metadataURI: string = "ipfs://metadata";
await multichainRegistry.addContract({
address,
chainId,
metadataURI,
});
let uri = await multichainRegistry.getContractMetadataURI(chainId, address);
expect(uri).to.equal(metadataURI);
let contracts = await multichainRegistry.getContractAddresses(
adminWallet.address,
);
expect(contracts[0].address).to.equal(address);
});
it("check abi merge", async () => {
const abi = joinABIs([RouterABI, RouterABI]);
expect(abi.length).to.equal(RouterABI.length);
});
it("should allow adding and removing contracts", async () => {
sdk.updateSignerOrProvider(adminWallet);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -189,7 +189,7 @@ describe("Signature drop tests (v5)", async () => {
name: "Test1",
});
const toSign = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri,
quantity: 1,
};
@@ -207,12 +207,12 @@ describe("Signature drop tests (v5)", async () => {
name: "Test2",
});
const toSign1 = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri1,
quantity: 1,
};
const toSign2 = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri2,
quantity: 1,
};
@@ -195,12 +195,12 @@ describe("Edition sig minting", async () => {
name: "Test2",
});
const toSign1 = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri1,
quantity: 10,
};
const toSign2 = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri2,
quantity: 10,
};
@@ -236,7 +236,7 @@ describe("Edition sig minting", async () => {
it("should mint additional supply", async () => {
const tx = await editionContract.signature.mint(v1);
const additional = await editionContract.signature.generateFromTokenId({
to: adminWallet.address,
to: samWallet.address,
tokenId: tx.id,
quantity: 100,
});
@@ -46,6 +46,7 @@ describe("NFT sig minting", async () => {
currencyAddress: NATIVE_TOKEN_ADDRESS,
metadata: {
name: "OUCH VOUCH",
background_color: "ff0000",
},
price: "1",
to: samWallet.address,
@@ -195,7 +196,7 @@ describe("NFT sig minting", async () => {
name: "Test1",
});
const toSign = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri,
};
const payload = await nftContract.signature.generate(toSign);
@@ -212,11 +213,11 @@ describe("NFT sig minting", async () => {
name: "Test2",
});
const toSign1 = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri1,
};
const toSign2 = {
to: adminWallet.address,
to: samWallet.address,
metadata: uri2,
};
const payloads = await nftContract.signature.generateBatch([
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/unity-js-bridge
## 0.1.7
### Patch Changes
- Updated dependencies [[`7cab8d1`](https://github.com/thirdweb-dev/js/commit/7cab8d1679f8d007091aa03adb83add3822a504a)]:
- @thirdweb-dev/sdk@3.6.11
## 0.1.6
### Patch Changes
- Updated dependencies [[`5fba324`](https://github.com/thirdweb-dev/js/commit/5fba324ac17ab02bc8f13d82a232bc5c6970c8e5), [`894cbef`](https://github.com/thirdweb-dev/js/commit/894cbefc6361f23fc528a6c6819f5c71793d46e4), [`557429b`](https://github.com/thirdweb-dev/js/commit/557429b5cfb3af2983ee01cf7d12d41ee0557593), [`73dc026`](https://github.com/thirdweb-dev/js/commit/73dc026ff9d0ac9099bd5a9a9cab8fdbfc0ae723), [`9eaa21d`](https://github.com/thirdweb-dev/js/commit/9eaa21d09ab9c700aea61a2a25f8ca9859d20857), [`68c8e3c`](https://github.com/thirdweb-dev/js/commit/68c8e3c4d79f5d56dc4414241bcca0d88285fcca), [`bff433e`](https://github.com/thirdweb-dev/js/commit/bff433e12150dc029e33a578219c8437a510da99)]:
- @thirdweb-dev/sdk@3.6.10
## 0.1.5
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/unity-js-bridge",
"version": "0.1.5",
"version": "0.1.7",
"main": "dist/thirdweb-unity-bridge.js",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
"license": "Apache-2.0",
+7 -7
View File
@@ -5574,10 +5574,10 @@
"@tanstack/query-core" "4.19.1"
use-sync-external-store "^1.2.0"
"@thirdweb-dev/contracts@3.2.8-0":
version "3.2.8-0"
resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-3.2.8-0.tgz#74a5775deece2f143d1f4d2d96920efc1f8659df"
integrity sha512-yoFoXx2s/baxZx05WYgTGIb6ze/pUwLQ2CkiK9ms3IoUY51mlmN0T8bbRBAJlQBMqJuHRGk4HzB+BUrpFYAZig==
"@thirdweb-dev/contracts@3.3.0":
version "3.3.0"
resolved "https://registry.yarnpkg.com/@thirdweb-dev/contracts/-/contracts-3.3.0.tgz#acbec36746bd8a9176b01c792d3d8aa6483a16c3"
integrity sha512-UE6Gc4xUTJ3PYJ6WWHtrIXynid5z7TZSnoaqzhiMcLnch6YqMJI1b7uB9NunYEY1N3SB75BYOgun9e1T3RoSbQ==
"@tootallnate/once@1":
version "1.1.2"
@@ -13970,9 +13970,9 @@ json-stringify-safe@^5.0.1:
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
dependencies:
minimist "^1.2.0"