Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
836484ba98 | ||
|
|
5047686f2a | ||
|
|
7cab8d1679 | ||
|
|
0f0125acf2 | ||
|
|
894cbefc63 | ||
|
|
73dc026ff9 | ||
|
|
af32007b9f | ||
|
|
5fba324ac1 | ||
|
|
bff433e121 | ||
|
|
68c8e3c4d7 | ||
|
|
4e8ff58208 | ||
|
|
d0d5a3c23d | ||
|
|
9eaa21d09a | ||
|
|
557429b5cf |
@@ -1,5 +1,9 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.41
|
||||
|
||||
## 2.0.40
|
||||
|
||||
## 2.0.39
|
||||
|
||||
## 2.0.38
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'",
|
||||
|
||||
@@ -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,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",
|
||||
};
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -8,3 +8,4 @@ export * from "./SplitRecipient";
|
||||
export * from "./deploy";
|
||||
export * from "./events";
|
||||
export * from "./multiwrap";
|
||||
export * from "./registry";
|
||||
|
||||
@@ -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>;
|
||||
@@ -0,0 +1,13 @@
|
||||
export type ContractInput = {
|
||||
address: string;
|
||||
chainId: number;
|
||||
};
|
||||
|
||||
export type AddContractInput = ContractInput & {
|
||||
metadataURI?: string;
|
||||
};
|
||||
|
||||
export type DeployedContract = {
|
||||
address: string;
|
||||
chainId: number;
|
||||
};
|
||||
@@ -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,
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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([
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user