Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a20be881e3 | ||
|
|
47f8945325 | ||
|
|
1b0bf99da7 | ||
|
|
f03be398e0 | ||
|
|
27996cb4bc | ||
|
|
2eaa67acbe | ||
|
|
aafd7968df | ||
|
|
48797c7cf0 | ||
|
|
4edcb37100 | ||
|
|
83dcaf7058 |
@@ -13,6 +13,6 @@ runs:
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: yarn install
|
||||
run: yarn install --frozen-lockfile
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
| [/storage](./packages/storage) | Best in class decentralized storage SDK for Browser and Node | <a href="https://www.npmjs.com/package/@thirdweb-dev/storage"><img src="https://img.shields.io/npm/v/@thirdweb-dev/storage?color=red&label=npm&logo=npm" alt="npm version"/></a> |
|
||||
| [/cli](./packages/cli) | Publish and deploy smart contracts without dealing with private keys | <a href="https://www.npmjs.com/package/thirdweb"><img src="https://img.shields.io/npm/v/thirdweb?color=red&label=npm&logo=npm" alt="npm version"/></a> |
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
Visit [https://portal.thirdweb.com/](https://portal.thirdweb.com/) to view the full documentation.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.33
|
||||
|
||||
## 2.0.32
|
||||
|
||||
## 2.0.31
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.32",
|
||||
"version": "2.0.33",
|
||||
"exports": {
|
||||
"./next": {
|
||||
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#403](https://github.com/thirdweb-dev/js/pull/403) [`27996cb`](https://github.com/thirdweb-dev/js/commit/27996cb4bc6f054f45b0a66b0a3aca732e0ef610) Thanks [@avneesh0612](https://github.com/avneesh0612)! - Add CLI Support for solana with --solana flag on create command
|
||||
|
||||
- [#283](https://github.com/thirdweb-dev/js/pull/283) [`2eaa67a`](https://github.com/thirdweb-dev/js/commit/2eaa67acbe21464e7715d0b696afdc024460a44d) Thanks [@adam-maj](https://github.com/adam-maj)! - add capability to deploy apps
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#413](https://github.com/thirdweb-dev/js/pull/413) [`1b0bf99`](https://github.com/thirdweb-dev/js/commit/1b0bf99da796df88ff307dd004cdaf239fb2b1ba) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - small interface tweaks
|
||||
|
||||
- Updated dependencies [[`48797c7`](https://github.com/thirdweb-dev/js/commit/48797c7cf0695965e33eb6bf602be4652f7085a8), [`83dcaf7`](https://github.com/thirdweb-dev/js/commit/83dcaf70584af8c5cdf0427f9fc1ba82d89f0887), [`f03be39`](https://github.com/thirdweb-dev/js/commit/f03be398e0d7b75c9c5b4fb643f35903e89a17e7), [`2eaa67a`](https://github.com/thirdweb-dev/js/commit/2eaa67acbe21464e7715d0b696afdc024460a44d), [`47f8945`](https://github.com/thirdweb-dev/js/commit/47f8945325447874b1ace0359919c1d54fd55436)]:
|
||||
- @thirdweb-dev/sdk@3.6.3
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export const CREATE_MESSAGES = {
|
||||
typeOfProject: "What type of project do you want to create?",
|
||||
projectName: "What is your project named?",
|
||||
chain: "Which blockchain do you want to use?",
|
||||
framework: "What framework do you want to use?",
|
||||
language: "What language do you want to use?",
|
||||
contract: "What type of contract do you want to start from?",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CREATE_MESSAGES } from "../constants/constants";
|
||||
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
|
||||
|
||||
// this creates an app, can take some time that's fine
|
||||
jest.setTimeout(120_000);
|
||||
jest.setTimeout(150_000);
|
||||
|
||||
describe("npx thirdweb create", () => {
|
||||
it("should create app (CRA) successfully", async () => {
|
||||
@@ -16,6 +16,8 @@ describe("npx thirdweb create", () => {
|
||||
await pressKey("enter");
|
||||
await waitForText(CREATE_MESSAGES.projectName);
|
||||
await pressKey("enter");
|
||||
await waitForText(CREATE_MESSAGES.chain);
|
||||
await pressKey("enter");
|
||||
await waitForText(CREATE_MESSAGES.framework);
|
||||
// select CRA
|
||||
await pressKey("arrowDown");
|
||||
|
||||
@@ -15,8 +15,8 @@ describe("npx thirdweb deploy", () => {
|
||||
`${path}/BasicContract.sol`,
|
||||
);
|
||||
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStdout } =
|
||||
await spawn("node", "./dist/cli/index.js deploy");
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
|
||||
await spawn("node", "./dist/cli/index.js deploy --contract");
|
||||
|
||||
expect(await exists("BasicContract.sol")).toEqual(true);
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("npx thirdweb deploy", () => {
|
||||
// wait for program to finish
|
||||
await waitForFinish();
|
||||
|
||||
expect(getStdout().at(-1)).toContain(
|
||||
expect(getStderr().at(-1)).toContain(
|
||||
"https://thirdweb.com/contracts/deploy/",
|
||||
);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("npx thirdweb release", () => {
|
||||
`${path}/BasicContract.sol`,
|
||||
);
|
||||
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStdout } =
|
||||
const { waitForText, waitForFinish, getExitCode, writeText, getStderr } =
|
||||
await spawn("node", "./dist/cli/index.js release");
|
||||
|
||||
expect(await exists("BasicContract.sol")).toEqual(true);
|
||||
@@ -26,7 +26,7 @@ describe("npx thirdweb release", () => {
|
||||
// wait for program to finish
|
||||
await waitForFinish();
|
||||
|
||||
expect(getStdout().at(-1)).toContain(
|
||||
expect(getStderr().at(-1)).toContain(
|
||||
"https://thirdweb.com/contracts/release/",
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.8.18",
|
||||
"version": "0.9.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { processProject } from "../common/processor";
|
||||
import { cliVersion, pkg } from "../constants/urls";
|
||||
import { info, logger } from "../core/helpers/logger";
|
||||
import { twCreate } from "../create/command";
|
||||
import generateDashboardUrl from "../helpers/generate-dashboard-url";
|
||||
import { deploy } from "../deploy";
|
||||
import { upload } from "../storage/command";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import chalk from "chalk";
|
||||
@@ -40,34 +40,36 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
.version(cliVersion, "-v, --version");
|
||||
|
||||
program
|
||||
.command("create [projectPath]")
|
||||
.command("create [projectType] [projectPath]")
|
||||
.description(
|
||||
"Create a web3 app from any of our official templates: https://github.com/thirdweb-example/",
|
||||
)
|
||||
.option("--app", `Create a web3 app.`)
|
||||
.option("--contract", `Create a web3 contract project`)
|
||||
.option("--ts, --typescript", `Initialize as a TypeScript project.`)
|
||||
.option("--js, --javascript", `Initialize as a JavaScript project.`)
|
||||
.option("--forge", `Initialize as a Forge project.`)
|
||||
.option("--hardhat", `Initialize as a Hardhat project.`)
|
||||
.option("--cra", `Initialize as a Create React App project.`)
|
||||
.option("--next", `Initialize as a Next.js project.`)
|
||||
.option("--vite", `Initialize as a Vite project.`)
|
||||
.option("--app", "Create a web3 app.")
|
||||
.option("--contract", "Create a web3 contract project")
|
||||
.option("--ts, --typescript", "Initialize as a TypeScript project.")
|
||||
.option("--js, --javascript", "Initialize as a JavaScript project.")
|
||||
.option("--forge", "Initialize as a Forge project.")
|
||||
.option("--hardhat", "Initialize as a Hardhat project.")
|
||||
.option("--cra", "Initialize as a Create React App project.")
|
||||
.option("--next", "Initialize as a Next.js project.")
|
||||
.option("--vite", "Initialize as a Vite project.")
|
||||
.option(
|
||||
"--use-npm",
|
||||
`Explicitly tell the CLI to bootstrap the app using npm`,
|
||||
"Explicitly tell the CLI to bootstrap the app using npm",
|
||||
)
|
||||
.option(
|
||||
"--use-pnpm",
|
||||
`Explicitly tell the CLI to bootstrap the app using pnpm`,
|
||||
"Explicitly tell the CLI to bootstrap the app using pnpm",
|
||||
)
|
||||
.option("--framework [name]", `The preferred framework.`)
|
||||
.option("--framework [name]", "The preferred framework.")
|
||||
.option("--solana", "Initialize as a Solana project.")
|
||||
.option("--evm", "Initialize as an Ethereum project.")
|
||||
.option(
|
||||
"-t, --template [name]",
|
||||
`A template to start your project from. You can use an template repository name from the official thirdweb-example org.`,
|
||||
"A template to start your project from. You can use an template repository name from the official thirdweb-example org.",
|
||||
)
|
||||
.action(async (path, options) => {
|
||||
await twCreate(path, options);
|
||||
.action(async (type, path, options) => {
|
||||
await twCreate(type, path, options);
|
||||
});
|
||||
|
||||
program
|
||||
@@ -87,6 +89,10 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
.option("--dry-run", "dry run (skip actually publishing)")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
.option("--ci", "Continuous Integration mode")
|
||||
.option(
|
||||
"--dist-path [distPath]",
|
||||
"Path to the dist folder where the HTML based App is",
|
||||
)
|
||||
.option(
|
||||
"-n, --name [name]",
|
||||
"Name of the pre-built or released contract (such as nft-drop)",
|
||||
@@ -95,31 +101,13 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
"-cv, --contract-version [version]",
|
||||
"Version of the released contract",
|
||||
)
|
||||
.option("--app", "Deploy a web app to decentralized storage")
|
||||
.option("--contract", "Deploy a smart contract to blockchains")
|
||||
.action(async (options) => {
|
||||
if (options.name) {
|
||||
const url = generateDashboardUrl(options.name, options.contractVersion);
|
||||
|
||||
if (!url) {
|
||||
logger.error(
|
||||
chalk.red(
|
||||
`Could not find a contract named ${options.name} ${
|
||||
options.contractVersion || ""
|
||||
}`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
info(`Open this link to deploy your contract:`);
|
||||
logger.info(chalk.blueBright(url));
|
||||
open(url.toString());
|
||||
return;
|
||||
const url = await deploy(options);
|
||||
if (url) {
|
||||
open(url);
|
||||
}
|
||||
|
||||
const url = await processProject(options, "deploy");
|
||||
info(`Open this link to deploy your contracts:`);
|
||||
logger.info(chalk.blueBright(url.toString()));
|
||||
open(url.toString());
|
||||
});
|
||||
|
||||
program
|
||||
@@ -133,8 +121,11 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
.option("--ci", "Continuous Integration mode")
|
||||
.action(async (options) => {
|
||||
const url = await processProject(options, "release");
|
||||
info(`Open this link to release your contracts:`);
|
||||
logger.info(chalk.blueBright(url.toString()));
|
||||
info(
|
||||
`Open this link to release your contracts: ${chalk.blueBright(
|
||||
url.toString(),
|
||||
)}`,
|
||||
);
|
||||
open(url.toString());
|
||||
});
|
||||
|
||||
@@ -144,13 +135,35 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
.argument("[upload]", "path to file or directory to upload")
|
||||
.action(async (path) => {
|
||||
const storage = new ThirdwebStorage();
|
||||
const uri = await upload(storage, path);
|
||||
info(`Files stored at the following IPFS URI:`);
|
||||
logger.info(chalk.blueBright(uri.toString()));
|
||||
try {
|
||||
const uri = await upload(storage, path);
|
||||
info(
|
||||
`Files stored at the following IPFS URI: ${chalk.blueBright(
|
||||
uri.toString(),
|
||||
)}`,
|
||||
);
|
||||
|
||||
const url = storage.resolveScheme(uri);
|
||||
info(`Open this link to view your upload:`);
|
||||
logger.info(chalk.blueBright(url.toString()));
|
||||
const url = storage.resolveScheme(uri);
|
||||
info(
|
||||
`Open this link to view your upload: ${chalk.blueBright(
|
||||
url.toString(),
|
||||
)}`,
|
||||
);
|
||||
} catch (err: any) {
|
||||
if (
|
||||
err.toString() === "No files detected in specified directory" ||
|
||||
err.toString() === "Invalid path provided" ||
|
||||
err.toString() === "No path provided"
|
||||
) {
|
||||
logger.error(
|
||||
chalk.redBright(
|
||||
"Please specify a valid file or directory to upload",
|
||||
),
|
||||
);
|
||||
} else {
|
||||
logger.error(chalk.redBright("Failed to upload files"), err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ProjectType } from "../types/ProjectType";
|
||||
import { Detector } from "./detector";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
export default class NPMDetector implements Detector {
|
||||
public projectType: ProjectType = "npm";
|
||||
|
||||
public matches(path: string): boolean {
|
||||
return existsSync(path + "/package-lock.json");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { ProjectType } from "../types/ProjectType";
|
||||
import { Detector } from "./detector";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
export default class ViteDetector implements Detector {
|
||||
public projectType: ProjectType = "vite";
|
||||
|
||||
public matches(path: string): boolean {
|
||||
return (
|
||||
existsSync(path + "/vite.config.js") ||
|
||||
existsSync(path + "/vite.config.ts")
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ProjectType } from "../types/ProjectType";
|
||||
import { Detector } from "./detector";
|
||||
import { existsSync } from "fs";
|
||||
|
||||
export default class YarnDetector implements Detector {
|
||||
public projectType: ProjectType = "yarn";
|
||||
|
||||
public matches(path: string): boolean {
|
||||
return existsSync(path + "/yarn.lock");
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,10 @@ export type ProjectType =
|
||||
| "hardhat"
|
||||
| "truffle"
|
||||
| "solc"
|
||||
| "vite"
|
||||
| "next"
|
||||
| "cra"
|
||||
| "spa-webapp"
|
||||
| "yarn"
|
||||
| "npm"
|
||||
| "none";
|
||||
|
||||
@@ -10,52 +10,67 @@ import chalk from "chalk";
|
||||
import path from "path";
|
||||
import prompts from "prompts";
|
||||
|
||||
let projectType: string = "";
|
||||
let projectPath: string = "";
|
||||
let projectType: string = "";
|
||||
let framework: string = "";
|
||||
let language: string = "";
|
||||
let baseContract: string = "";
|
||||
let chain: string = "";
|
||||
/* let createType: string = "app"; */
|
||||
|
||||
export async function twCreate(pPath: string = "", options: any) {
|
||||
export async function twCreate(
|
||||
pType: string,
|
||||
pPath: string = "",
|
||||
options: any,
|
||||
) {
|
||||
if (typeof pPath === "string") {
|
||||
projectPath = pPath;
|
||||
}
|
||||
|
||||
if (options.app) {
|
||||
if (pType === "app" || options.app) {
|
||||
projectType = "app";
|
||||
}
|
||||
|
||||
if (options.contract) {
|
||||
} else if (pType === "contract" || options.contract) {
|
||||
projectType = "contract";
|
||||
}
|
||||
|
||||
if (options.typescript) {
|
||||
language = "typescript";
|
||||
if (projectType === "app") {
|
||||
if (options.typescript) {
|
||||
language = "typescript";
|
||||
}
|
||||
|
||||
if (options.javascript) {
|
||||
language = "javascript";
|
||||
}
|
||||
|
||||
if (options.next) {
|
||||
framework = "next";
|
||||
}
|
||||
|
||||
if (options.cra) {
|
||||
framework = "cra";
|
||||
}
|
||||
|
||||
if (options.vite) {
|
||||
framework = "vite";
|
||||
}
|
||||
|
||||
if (options.solana) {
|
||||
chain = "solana";
|
||||
}
|
||||
|
||||
if (options.evm) {
|
||||
chain = "evm";
|
||||
}
|
||||
}
|
||||
|
||||
if (options.javascript) {
|
||||
language = "javascript";
|
||||
}
|
||||
if (projectType === "contract") {
|
||||
if (options.forge) {
|
||||
framework = "forge";
|
||||
}
|
||||
|
||||
if (options.next) {
|
||||
framework = "next";
|
||||
}
|
||||
|
||||
if (options.cra) {
|
||||
framework = "cra";
|
||||
}
|
||||
|
||||
if (options.vite) {
|
||||
framework = "vite";
|
||||
}
|
||||
|
||||
if (options.forge) {
|
||||
framework = "forge";
|
||||
}
|
||||
|
||||
if (options.hardhat) {
|
||||
framework = "hardhat";
|
||||
if (options.hardhat) {
|
||||
framework = "hardhat";
|
||||
}
|
||||
}
|
||||
|
||||
if (options.framework) {
|
||||
@@ -76,7 +91,7 @@ export async function twCreate(pPath: string = "", options: any) {
|
||||
if (typeof res.projectType === "string") {
|
||||
projectType = res.projectType.trim();
|
||||
}
|
||||
} else if (!projectType && options.template) {
|
||||
} else if (!projectType || options.template) {
|
||||
// If no project type is specified, but a template is, we assume the user wants to create an app.
|
||||
// We do this so old users can still use the --template flag to create an app.
|
||||
projectType = "app";
|
||||
@@ -123,16 +138,42 @@ export async function twCreate(pPath: string = "", options: any) {
|
||||
}
|
||||
|
||||
if (!options.template) {
|
||||
if (projectType === "app" && !chain) {
|
||||
const res = await prompts({
|
||||
type: "select",
|
||||
name: "chain",
|
||||
message: CREATE_MESSAGES.chain,
|
||||
choices: [
|
||||
{ title: "EVM", value: "evm" },
|
||||
{ title: "Solana", value: "solana" },
|
||||
],
|
||||
});
|
||||
|
||||
if (res.chain === "solana") {
|
||||
chain = "solana";
|
||||
} else {
|
||||
chain = "evm";
|
||||
}
|
||||
}
|
||||
|
||||
if (projectType === "app" && !framework) {
|
||||
const res = await prompts({
|
||||
type: "select",
|
||||
name: "framework",
|
||||
message: CREATE_MESSAGES.framework,
|
||||
choices: [
|
||||
{ title: "Next.js", value: "next" },
|
||||
{ title: "Create React App", value: "cra" },
|
||||
{ title: "Vite", value: "vite" },
|
||||
],
|
||||
choices:
|
||||
// Solana doesn't support Vite just yet:
|
||||
chain === "solana"
|
||||
? [
|
||||
{ title: "Next.js", value: "next" },
|
||||
{ title: "Create React App", value: "cra" },
|
||||
// { title: "Vite", value: "vite" },
|
||||
]
|
||||
: [
|
||||
{ title: "Next.js", value: "next" },
|
||||
{ title: "Create React App", value: "cra" },
|
||||
{ title: "Vite", value: "vite" },
|
||||
],
|
||||
});
|
||||
|
||||
if (typeof res.framework === "string") {
|
||||
@@ -288,6 +329,7 @@ export async function twCreate(pPath: string = "", options: any) {
|
||||
framework,
|
||||
language,
|
||||
template,
|
||||
chain,
|
||||
});
|
||||
} else {
|
||||
await createContract({
|
||||
|
||||
@@ -22,12 +22,14 @@ export async function createApp({
|
||||
framework,
|
||||
language,
|
||||
template,
|
||||
chain,
|
||||
}: {
|
||||
appPath: string;
|
||||
packageManager: PackageManager;
|
||||
framework?: string;
|
||||
language?: string;
|
||||
template?: string;
|
||||
chain?: string;
|
||||
}): Promise<void> {
|
||||
let frameworkPath = "";
|
||||
|
||||
@@ -43,7 +45,9 @@ export async function createApp({
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (framework) {
|
||||
frameworkPath = `${framework}-${language || "javascript"}-starter`;
|
||||
frameworkPath = `${framework}-${language || "javascript"}-${
|
||||
chain === "solana" ? "solana-" : ""
|
||||
}starter`;
|
||||
const found = await hasTemplate(frameworkPath);
|
||||
|
||||
if (!found) {
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
import spawn from "cross-spawn";
|
||||
|
||||
export async function runCommand(command: string, args: string[]) {
|
||||
export async function runCommand(
|
||||
command: string,
|
||||
args: string[],
|
||||
logging: boolean = false,
|
||||
) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
/**
|
||||
* Spawn the installation process.
|
||||
*/
|
||||
const child = spawn(command, args);
|
||||
child.stdout?.on("data", (data) => {
|
||||
if (logging) {
|
||||
console.log(data.toString());
|
||||
}
|
||||
});
|
||||
|
||||
child.stderr?.on("error", (err) => {
|
||||
if (logging) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
reject({ command: `${command}` });
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import NPMDetector from "../core/detection/npm";
|
||||
import ViteDetector from "../core/detection/vite";
|
||||
import YarnDetector from "../core/detection/yarn";
|
||||
import { runCommand } from "../create/helpers/run-command";
|
||||
import { upload } from "../storage/command";
|
||||
import { DEFAULT_IPFS_GATEWAY } from "@thirdweb-dev/sdk";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
|
||||
export async function deployApp(distPath = "dist", projectPath = ".") {
|
||||
const storage = new ThirdwebStorage();
|
||||
const supportedFrameworks = [new ViteDetector()];
|
||||
|
||||
const possibleProjects = supportedFrameworks
|
||||
.filter((detector) => detector.matches(projectPath))
|
||||
.map((detector) => detector.projectType);
|
||||
|
||||
const hasYarn = new YarnDetector().matches(".");
|
||||
const hasNPM = new NPMDetector().matches(".");
|
||||
|
||||
if (possibleProjects.length === 0) {
|
||||
throw new Error("No supported project detected");
|
||||
}
|
||||
|
||||
try {
|
||||
if (hasYarn) {
|
||||
await runCommand("yarn", ["build"]);
|
||||
} else if (hasNPM) {
|
||||
await runCommand("npm", ["build"]);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Can't build project");
|
||||
return Promise.reject("Can't build project");
|
||||
}
|
||||
|
||||
try {
|
||||
let url = await upload(storage, distPath);
|
||||
url = url.replace("ipfs://", DEFAULT_IPFS_GATEWAY);
|
||||
return `${url}/index.html`;
|
||||
} catch (err) {
|
||||
console.error("Can't upload project");
|
||||
return Promise.reject("Can't upload project");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { processProject } from "../common/processor";
|
||||
import { info, logger } from "../core/helpers/logger";
|
||||
import generateDashboardUrl from "../helpers/generate-dashboard-url";
|
||||
import { deployApp } from "./app";
|
||||
import chalk from "chalk";
|
||||
|
||||
type DeployOptions = {
|
||||
name: string;
|
||||
contractVersion: string;
|
||||
app: boolean;
|
||||
path?: string;
|
||||
distPath?: string;
|
||||
};
|
||||
|
||||
export async function deploy(options: DeployOptions) {
|
||||
if (options.name) {
|
||||
const url = generateDashboardUrl(options.name, options.contractVersion);
|
||||
|
||||
if (!url) {
|
||||
logger.error(
|
||||
chalk.red(
|
||||
`Could not find a contract named ${options.name} ${
|
||||
options.contractVersion || ""
|
||||
}`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
info(`Open this link to deploy your contract: ${chalk.blueBright(url)}`);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
if (options.app) {
|
||||
try {
|
||||
let url = await deployApp(options.distPath, options.path);
|
||||
info(`Here is the link to your app: ${chalk.blueBright(url.toString())}`);
|
||||
return url.toString();
|
||||
} catch (err) {
|
||||
logger.error("Failed to deploy app, No compatible project found", err);
|
||||
return Promise.reject(
|
||||
"failed to deploy app, no compatible project found",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const url = await processProject(options, "deploy");
|
||||
info(
|
||||
`Open this link to deploy your contracts: ${chalk.blueBright(
|
||||
url.toString(),
|
||||
)}`,
|
||||
);
|
||||
return url.toString();
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import { logger } from "../core/helpers/logger";
|
||||
import { spinner } from "../core/helpers/logger";
|
||||
import { BufferOrStringWithName, ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import chalk from "chalk";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
@@ -33,22 +31,12 @@ export async function upload(
|
||||
uploadPath: string,
|
||||
): Promise<string> {
|
||||
if (!uploadPath) {
|
||||
logger.error(
|
||||
`Please pass a path to a file or directory to upload with the following format:\n ${chalk.blueBright(
|
||||
`npx thirdweb@latest upload path/to/file.jpg`,
|
||||
)}\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
return Promise.reject("No path provided");
|
||||
}
|
||||
|
||||
const pathExists = fs.existsSync(uploadPath);
|
||||
if (!pathExists) {
|
||||
logger.error(
|
||||
`Invalid path ${chalk.blueBright(
|
||||
uploadPath,
|
||||
)} provided. Please provide a valid path to a file or directory to upload."`,
|
||||
);
|
||||
process.exit(1);
|
||||
return Promise.reject("Invalid path provided");
|
||||
}
|
||||
|
||||
let uri = "";
|
||||
@@ -57,12 +45,7 @@ export async function upload(
|
||||
const files = recurseFiles(uploadPath, uploadPath);
|
||||
|
||||
if (files.length === 0) {
|
||||
logger.error(
|
||||
`No files detected in specified directory ${chalk.blueBright(
|
||||
uploadPath,
|
||||
)} to upload.`,
|
||||
);
|
||||
process.exit(1);
|
||||
return Promise.reject("No files detected in specified directory");
|
||||
}
|
||||
|
||||
const spin = spinner(
|
||||
@@ -82,12 +65,7 @@ export async function upload(
|
||||
uri = await storage.upload(file, { uploadWithoutDirectory: true });
|
||||
spin.succeed("Succesfully uploaded file to IPFS.");
|
||||
} else {
|
||||
logger.error(
|
||||
`Path ${chalk.blueBright(
|
||||
uploadPath,
|
||||
)} does not point to a valid file or directory. Please provide a valid path to a file or directory to upload`,
|
||||
);
|
||||
process.exit(1);
|
||||
return Promise.reject("Invalid path provided");
|
||||
}
|
||||
|
||||
return uri;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.6.3
|
||||
|
||||
## 3.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.6.2",
|
||||
"version": "3.6.3",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
|
||||
/**
|
||||
* This component can be used to render NFTs from the thirdweb SDK.
|
||||
* Props: {@link ThirdwebNftMediaProps}
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { ThirdwebNftMedia, useContract, useNFT } from "@thirdweb-dev/react";
|
||||
|
||||
@@ -106,7 +106,7 @@ export function useUnclaimedNFTSupply(contract: RequiredParam<DropContract>) {
|
||||
invariant(erc721, "No ERC721 Contract instance provided");
|
||||
return erc721.totalUnclaimedSupply();
|
||||
}
|
||||
invariant(false, "Contract is not an instance of ERC721")
|
||||
invariant(false, "Contract is not an instance of ERC721");
|
||||
},
|
||||
{ enabled: !!erc721 },
|
||||
);
|
||||
@@ -129,7 +129,7 @@ export function useClaimedNFTSupply(contract: RequiredParam<DropContract>) {
|
||||
if (erc721) {
|
||||
return erc721.totalClaimedSupply();
|
||||
}
|
||||
invariant(false, "Contract is not an instance of ERC721")
|
||||
invariant(false, "Contract is not an instance of ERC721");
|
||||
},
|
||||
{ enabled: !!erc721 },
|
||||
);
|
||||
|
||||
@@ -40,6 +40,8 @@ export function nftRoyaltyQuery(
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useRoyaltySettings(program: RequiredParam<NFTCollection | NFTDrop>) {
|
||||
export function useRoyaltySettings(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
) {
|
||||
return useQuery(nftRoyaltyQuery(program));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,11 @@ import {
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop, UpdateCreatorInput } from "@thirdweb-dev/sdk/solana";
|
||||
import type {
|
||||
NFTCollection,
|
||||
NFTDrop,
|
||||
UpdateCreatorInput,
|
||||
} from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
/**
|
||||
* Update the creators for an NFT program
|
||||
|
||||
@@ -4,7 +4,11 @@ import {
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop, UpdateRoyaltySettingsInput } from "@thirdweb-dev/sdk/solana";
|
||||
import type {
|
||||
NFTCollection,
|
||||
NFTDrop,
|
||||
UpdateRoyaltySettingsInput,
|
||||
} from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
/**
|
||||
* Update the royalty for an NFT program
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#408](https://github.com/thirdweb-dev/js/pull/408) [`48797c7`](https://github.com/thirdweb-dev/js/commit/48797c7cf0695965e33eb6bf602be4652f7085a8) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - add `Gasless` as a possible extension to feature-detect
|
||||
|
||||
- [#402](https://github.com/thirdweb-dev/js/pull/402) [`83dcaf7`](https://github.com/thirdweb-dev/js/commit/83dcaf70584af8c5cdf0427f9fc1ba82d89f0887) Thanks [@adam-maj](https://github.com/adam-maj)! - Expose sdk.fromWallet and wallet classes
|
||||
|
||||
- [#412](https://github.com/thirdweb-dev/js/pull/412) [`f03be39`](https://github.com/thirdweb-dev/js/commit/f03be398e0d7b75c9c5b4fb643f35903e89a17e7) Thanks [@easonchai](https://github.com/easonchai)! - Correctly handle contracts using the SignatureMintERC1155 ContractKit extension
|
||||
|
||||
- [#283](https://github.com/thirdweb-dev/js/pull/283) [`2eaa67a`](https://github.com/thirdweb-dev/js/commit/2eaa67acbe21464e7715d0b696afdc024460a44d) Thanks [@adam-maj](https://github.com/adam-maj)! - AppURI detection now enabled
|
||||
|
||||
- [#414](https://github.com/thirdweb-dev/js/pull/414) [`47f8945`](https://github.com/thirdweb-dev/js/commit/47f8945325447874b1ace0359919c1d54fd55436) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Improve error parsing
|
||||
|
||||
## 3.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-sdk-evm-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-sdk-evm-wallets.esm.js"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.6.2",
|
||||
"version": "3.6.3",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
@@ -19,6 +19,10 @@
|
||||
"module": "./solana/dist/thirdweb-dev-sdk-solana.esm.js",
|
||||
"default": "./solana/dist/thirdweb-dev-sdk-solana.cjs.js"
|
||||
},
|
||||
"./evm/wallets": {
|
||||
"module": "./evm/wallets/dist/thirdweb-dev-sdk-evm-wallets.esm.js",
|
||||
"default": "./evm/wallets/dist/thirdweb-dev-sdk-evm-wallets.cjs.js"
|
||||
},
|
||||
"./solana/server": {
|
||||
"module": "./solana/server/dist/thirdweb-dev-sdk-solana-server.esm.js",
|
||||
"default": "./solana/server/dist/thirdweb-dev-sdk-solana-server.cjs.js"
|
||||
@@ -28,6 +32,7 @@
|
||||
"files": [
|
||||
"dist/",
|
||||
"evm/",
|
||||
"wallets/",
|
||||
"solana/",
|
||||
"server/"
|
||||
],
|
||||
@@ -35,6 +40,7 @@
|
||||
"entrypoints": [
|
||||
"index.ts",
|
||||
"evm/index.ts",
|
||||
"evm/wallets/index.ts",
|
||||
"solana/index.ts",
|
||||
"solana/server/index.ts"
|
||||
],
|
||||
@@ -65,6 +71,7 @@
|
||||
"push": "yalc push"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.216.0",
|
||||
"@metaplex-foundation/amman": "^0.12.0",
|
||||
"@metaplex-foundation/amman-client": "^0.2.1",
|
||||
"@microsoft/api-documenter": "^7.19.4",
|
||||
@@ -96,8 +103,14 @@
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.215.0",
|
||||
"ethers": "^5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@aws-sdk/client-secrets-manager": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@metaplex-foundation/js": "^0.17.6",
|
||||
"@metaplex-foundation/mpl-token-metadata": "^2.3.3",
|
||||
|
||||
@@ -394,20 +394,14 @@ export async function convertToTWError(
|
||||
contractInterface: ethers.utils.Interface,
|
||||
): Promise<TransactionError> {
|
||||
let raw: string;
|
||||
if (error.data) {
|
||||
if (typeof error === "object") {
|
||||
// metamask errors comes as objects, apply parsing on data object
|
||||
// TODO test errors from other wallets
|
||||
raw = JSON.stringify(error.data);
|
||||
} else if (error instanceof Error) {
|
||||
// regular ethers.js error
|
||||
raw = error.message;
|
||||
raw = JSON.stringify(error);
|
||||
} else {
|
||||
// not sure what this is, just throw it back
|
||||
raw = error.toString();
|
||||
}
|
||||
const reason =
|
||||
error.reason ||
|
||||
parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
|
||||
const reason = parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw);
|
||||
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);
|
||||
|
||||
@@ -438,8 +438,8 @@ export async function fetchSourceFilesFromMetadata(
|
||||
async ([path, info]) => {
|
||||
const urls = (info as any).urls as string[];
|
||||
const ipfsLink = urls
|
||||
? urls.find((url) => url.includes("ipfs"))
|
||||
: undefined;
|
||||
? 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
|
||||
@@ -458,8 +458,8 @@ export async function fetchSourceFilesFromMetadata(
|
||||
return {
|
||||
filename: path,
|
||||
source:
|
||||
(info as any).content ||
|
||||
"Could not find source for this contract",
|
||||
(info as any).content ||
|
||||
"Could not find source for this contract",
|
||||
};
|
||||
}
|
||||
},
|
||||
@@ -543,8 +543,8 @@ export function detectFeatures(
|
||||
}
|
||||
|
||||
function extractFeatures(
|
||||
input: Record<string, FeatureWithEnabled>,
|
||||
enabledExtensions: FeatureWithEnabled[],
|
||||
input: Record<string, FeatureWithEnabled>,
|
||||
enabledExtensions: FeatureWithEnabled[],
|
||||
) {
|
||||
if (!input) {
|
||||
return;
|
||||
@@ -556,10 +556,7 @@ function extractFeatures(
|
||||
enabledExtensions.push(extension);
|
||||
}
|
||||
// recurse
|
||||
extractFeatures(
|
||||
extension.features,
|
||||
enabledExtensions,
|
||||
);
|
||||
extractFeatures(extension.features, enabledExtensions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
FEATURE_PRIMARY_SALE,
|
||||
FEATURE_ROYALTY,
|
||||
FEATURE_APPURI,
|
||||
FEATURE_GASLESS,
|
||||
} from "./thirdweb-features";
|
||||
|
||||
/**
|
||||
@@ -104,7 +105,8 @@ export type Feature =
|
||||
| typeof FEATURE_PERMISSIONS_ENUMERABLE
|
||||
| typeof FEATURE_METADATA
|
||||
| typeof FEATURE_APPURI
|
||||
| typeof FEATURE_OWNER;
|
||||
| typeof FEATURE_OWNER
|
||||
| typeof FEATURE_GASLESS;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -132,4 +134,5 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = {
|
||||
[FEATURE_METADATA.name]: FEATURE_METADATA,
|
||||
[FEATURE_APPURI.name]: FEATURE_APPURI,
|
||||
[FEATURE_OWNER.name]: FEATURE_OWNER,
|
||||
[FEATURE_GASLESS.name]: FEATURE_GASLESS,
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
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 IPermissionsAbi from "@thirdweb-dev/contracts-js/dist/abis/IPermissions.json";
|
||||
@@ -96,3 +97,15 @@ export const FEATURE_OWNER = {
|
||||
abis: [IOwnableAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_GASLESS = {
|
||||
name: "Gasless",
|
||||
namespace: "gasless",
|
||||
docLinks: {
|
||||
sdk: "sdk.gaslesstransaction",
|
||||
// TODO add the correct name for this once it's added to portal
|
||||
contracts: "",
|
||||
},
|
||||
abis: [IERC2771ContextAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { FEATURE_TOKEN } from "../constants/erc20-features";
|
||||
import { FEATURE_NFT } from "../constants/erc721-features";
|
||||
import { FEATURE_EDITION } from "../constants/erc1155-features";
|
||||
import {
|
||||
FEATURE_APPURI,
|
||||
FEATURE_OWNER,
|
||||
FEATURE_PERMISSIONS,
|
||||
FEATURE_PLATFORM_FEE,
|
||||
@@ -39,7 +38,6 @@ import type {
|
||||
IPrimarySale,
|
||||
IRoyalty,
|
||||
Ownable,
|
||||
AppURI,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract, CallOverrides, ContractInterface } from "ethers";
|
||||
@@ -84,6 +82,7 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
public publishedMetadata: ContractPublishedMetadata<TContract>;
|
||||
public abi: ContractInterface;
|
||||
public metadata: ContractMetadata<BaseContract, any>;
|
||||
public appURI: ContractAppURI<BaseContract>;
|
||||
|
||||
/**
|
||||
* Handle royalties
|
||||
@@ -120,13 +119,6 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
return assertEnabled(this.detectOwnable(), FEATURE_OWNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and get the appuri of the contract
|
||||
*/
|
||||
get appURI(): ContractAppURI<AppURI> {
|
||||
return assertEnabled(this.detectAppURI(), FEATURE_APPURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detects ERC20 standard functions.
|
||||
*/
|
||||
@@ -186,6 +178,8 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
CustomContractSchema,
|
||||
this.storage,
|
||||
);
|
||||
|
||||
this.appURI = new ContractAppURI(this.contractWrapper, this.metadata);
|
||||
}
|
||||
|
||||
onNetworkUpdated(network: NetworkOrSignerOrProvider): void {
|
||||
@@ -290,12 +284,6 @@ export class SmartContract<TContract extends BaseContract = BaseContract>
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectAppURI() {
|
||||
if (detectContractFeature<AppURI>(this.contractWrapper, "AppURI")) {
|
||||
return new ContractAppURI(this.contractWrapper);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
private detectOwnable() {
|
||||
if (detectContractFeature<Ownable>(this.contractWrapper, "Ownable")) {
|
||||
return new ContractOwner(this.contractWrapper);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { detectContractFeature } from "../../common";
|
||||
import { FEATURE_APPURI } from "../../constants/thirdweb-features";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { TransactionResult } from "../types";
|
||||
import { ContractMetadata } from "./contract-metadata";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import type { AppURI } from "@thirdweb-dev/contracts-js";
|
||||
import { BaseContract } from "ethers";
|
||||
|
||||
/**
|
||||
* Have an official Application URI for this contract.
|
||||
@@ -17,14 +20,19 @@ import type { AppURI } from "@thirdweb-dev/contracts-js";
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ContractAppURI<TContract extends AppURI>
|
||||
export class ContractAppURI<TContract extends BaseContract>
|
||||
implements DetectableFeature
|
||||
{
|
||||
featureName = FEATURE_APPURI.name;
|
||||
private contractWrapper;
|
||||
metadata: ContractMetadata<BaseContract, any>;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<TContract>) {
|
||||
constructor(
|
||||
contractWrapper: ContractWrapper<TContract>,
|
||||
metadata: ContractMetadata<BaseContract, any>,
|
||||
) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,7 +40,11 @@ export class ContractAppURI<TContract extends AppURI>
|
||||
* @returns the appURI object
|
||||
*/
|
||||
public async get() {
|
||||
return await this.contractWrapper.readContract.appURI();
|
||||
if (detectContractFeature<AppURI>(this.contractWrapper, "AppURI")) {
|
||||
return await this.contractWrapper.readContract.appURI();
|
||||
}
|
||||
|
||||
return (await this.metadata.get()).appURI || "";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,10 +52,14 @@ export class ContractAppURI<TContract extends AppURI>
|
||||
* @param appURI - the uri to set (typically an IPFS hash)
|
||||
*/
|
||||
public async set(appURI: string): Promise<TransactionResult> {
|
||||
return {
|
||||
receipt: await this.contractWrapper.sendTransaction("setAppURI", [
|
||||
appURI,
|
||||
]),
|
||||
};
|
||||
if (detectContractFeature<AppURI>(this.contractWrapper, "AppURI")) {
|
||||
return {
|
||||
receipt: await this.contractWrapper.sendTransaction("setAppURI", [
|
||||
appURI,
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
return await this.metadata.update({ appURI });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export class ContractOwner<TContract extends Ownable>
|
||||
* Return the current owner of the contract
|
||||
* @returns the owner address
|
||||
*/
|
||||
public async get(): Promise<string> {
|
||||
public async get(): Promise<string> {
|
||||
return this.contractWrapper.readContract.owner();
|
||||
}
|
||||
|
||||
@@ -31,13 +31,13 @@ export class ContractOwner<TContract extends Ownable>
|
||||
* @remarks Can only be called by the current owner.
|
||||
*
|
||||
* @param address - the address of the new owner
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* await contract.owner.set("0x1234567890123456789012345678901234567890");
|
||||
* ```
|
||||
*/
|
||||
public async set(address: string): Promise<TransactionResult> {
|
||||
public async set(address: string): Promise<TransactionResult> {
|
||||
return {
|
||||
receipt: await this.contractWrapper.sendTransaction("setOwner", [
|
||||
address,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { normalizePriceValue, setErc20Allowance } from "../../common/currency";
|
||||
import { getPrebuiltInfo } from "../../common/legacy";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import { FEATURE_EDITION_SIGNATURE_MINTABLE } from "../../constants/erc1155-features";
|
||||
import type { NFTCollectionInitializer } from "../../contracts";
|
||||
@@ -330,6 +331,12 @@ export class Erc1155SignatureMintable implements DetectableFeature {
|
||||
const signer = this.contractWrapper.getSigner();
|
||||
invariant(signer, "No signer available");
|
||||
|
||||
const contractInfo = await getPrebuiltInfo(
|
||||
this.contractWrapper.readContract.address,
|
||||
this.contractWrapper.getProvider(),
|
||||
);
|
||||
const isLegacyContract = contractInfo?.type === "TokenERC1155";
|
||||
|
||||
return await Promise.all(
|
||||
parsedRequests.map(async (m, i) => {
|
||||
const uri = uris[i];
|
||||
@@ -340,7 +347,7 @@ export class Erc1155SignatureMintable implements DetectableFeature {
|
||||
const signature = await this.contractWrapper.signTypedData(
|
||||
signer,
|
||||
{
|
||||
name: "TokenERC1155",
|
||||
name: isLegacyContract ? "TokenERC1155" : "SignatureMintERC1155",
|
||||
version: "1",
|
||||
chainId,
|
||||
verifyingContract: this.contractWrapper.readContract.address,
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
import { SmartContract } from "../contracts/smart-contract";
|
||||
import { SDKOptions } from "../schema/sdk-options";
|
||||
import { CurrencyValue } from "../types/index";
|
||||
import type { AbstractWallet } from "../wallets";
|
||||
import { WalletAuthenticator } from "./auth/wallet-authenticator";
|
||||
import type { ContractMetadata } from "./classes";
|
||||
import { ContractDeployer } from "./classes/contract-deployer";
|
||||
@@ -39,6 +40,43 @@ import invariant from "tiny-invariant";
|
||||
* @public
|
||||
*/
|
||||
export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
/**
|
||||
* Get an instance of the thirdweb SDK based on an AbstractWallet
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk"
|
||||
*
|
||||
* const wallet = new AbstractWalletImplementation();
|
||||
* const sdk = await ThirdwebSDK.fromWallet(wallet, "mainnet");
|
||||
* ```
|
||||
*
|
||||
* @param wallet - the implementation of the AbstractWallet class to use for signing
|
||||
* @param network - the network (chain) to connect to (e.g. "mainnet", "rinkeby", "polygon", "mumbai"...) or a fully formed RPC url
|
||||
* @param options - the SDK options to use
|
||||
* @param storage - optional storage implementation to use
|
||||
* @returns an instance of the SDK
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
static async fromWallet(
|
||||
wallet: AbstractWallet,
|
||||
network: ChainOrRpc,
|
||||
options: SDKOptions = {},
|
||||
storage: ThirdwebStorage = new ThirdwebStorage(),
|
||||
) {
|
||||
const signerOrProvider = getProviderForNetwork(network);
|
||||
const provider = Signer.isSigner(signerOrProvider)
|
||||
? signerOrProvider.provider
|
||||
: typeof signerOrProvider === "string"
|
||||
? getReadOnlyProvider(signerOrProvider)
|
||||
: signerOrProvider;
|
||||
|
||||
const signer = await wallet.getSigner(provider);
|
||||
|
||||
return ThirdwebSDK.fromSigner(signer, network, options, storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance of the thirdweb SDK based on an existing ethers signer
|
||||
*
|
||||
@@ -54,8 +92,8 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
* @param signer - a ethers Signer to be used for transactions
|
||||
* @param network - the network (chain) to connect to (e.g. "mainnet", "rinkeby", "polygon", "mumbai"...) or a fully formed RPC url
|
||||
* @param options - the SDK options to use
|
||||
* @returns an instance of the SDK
|
||||
* @param storage - optional storage implementation to use
|
||||
* @returns an instance of the SDK
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
|
||||
@@ -80,4 +80,4 @@ export type SDKOptions = z.input<typeof SDKOptionsSchema>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type SDKOptionsOutput = z.output<typeof SDKOptionsSchema>;
|
||||
export type SDKOptionsOutput = z.output<typeof SDKOptionsSchema>;
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { AbstractWallet } from "../types/abstract";
|
||||
import {
|
||||
GetSecretValueCommand,
|
||||
SecretsManagerClient,
|
||||
SecretsManagerClientConfig,
|
||||
} from "@aws-sdk/client-secrets-manager";
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export type AwsSecretsManagerWalletOptions = {
|
||||
secretId: string;
|
||||
secretKeyName: string;
|
||||
awsConfig: SecretsManagerClientConfig;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a wallet instance using a private key stored in AWS Secrets Manager.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { ThirdwebSDK } from "@thirdweb-dev/sdk"
|
||||
* import { AWSSecretsManagerWallet } from "@thirdweb-dev/sdk/evm/wallets"
|
||||
*
|
||||
* const wallet = new AwsSecretsManagerWallet({
|
||||
* secretName: "my-secret",
|
||||
* secretKeyName: "private-key",
|
||||
* awsConfig: {
|
||||
* region: "us-east-1",
|
||||
* credentials: {
|
||||
* accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||
* secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
* },
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* const sdk = await ThirdwebSDK.fromWallet(wallet, "mainnet");
|
||||
* ```
|
||||
*/
|
||||
export class AWSSecretsManagerWallet extends AbstractWallet {
|
||||
private secretId: string;
|
||||
private secretKeyName: string;
|
||||
private client: SecretsManagerClient;
|
||||
private cachedSigner: ethers.Signer | undefined;
|
||||
|
||||
constructor(options: AwsSecretsManagerWalletOptions) {
|
||||
super();
|
||||
this.secretId = options.secretId;
|
||||
this.secretKeyName = options.secretKeyName;
|
||||
this.client = new SecretsManagerClient(options.awsConfig);
|
||||
}
|
||||
|
||||
async getSigner(
|
||||
provider?: ethers.providers.Provider,
|
||||
): Promise<ethers.Signer> {
|
||||
if (this.cachedSigner) {
|
||||
return this.cachedSigner;
|
||||
}
|
||||
|
||||
const res = await this.client.send(
|
||||
new GetSecretValueCommand({
|
||||
SecretId: this.secretId,
|
||||
VersionStage: "AWSCURRENT", // VersionStage defaults to AWSCURRENT if unspecified
|
||||
}),
|
||||
);
|
||||
|
||||
if (!res || !res.SecretString) {
|
||||
throw new Error(`No secret found at ${this.secretId}`);
|
||||
}
|
||||
|
||||
const privateKey = JSON.parse(res.SecretString)[this.secretKeyName];
|
||||
if (!privateKey) {
|
||||
throw new Error(
|
||||
`Secret ${this.secretId} does not have key ${this.secretKeyName}`,
|
||||
);
|
||||
}
|
||||
|
||||
this.cachedSigner = new ethers.Wallet(privateKey, provider);
|
||||
return this.cachedSigner;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./aws";
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./core";
|
||||
export * from "./types";
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export abstract class AbstractWallet {
|
||||
abstract getSigner(
|
||||
provider?: ethers.providers.Provider,
|
||||
): Promise<ethers.Signer>;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./abstract";
|
||||
@@ -80,7 +80,7 @@ export type UpdateCreatorInput = {
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type UpdateRoyaltySettingsInput = {
|
||||
export type UpdateRoyaltySettingsInput = {
|
||||
sellerFeeBasisPoints: number;
|
||||
updateAll: boolean;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import {
|
||||
ChainId, getAllDetectedFeatureNames, isFeatureEnabled,
|
||||
ChainId,
|
||||
getAllDetectedFeatureNames,
|
||||
isFeatureEnabled,
|
||||
resolveContractUriFromAddress,
|
||||
ThirdwebSDK,
|
||||
} from "../../src/evm";
|
||||
@@ -7,11 +9,12 @@ import { implementations, signers } from "./before-setup";
|
||||
import { AddressZero } from "@ethersproject/constants";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
import {
|
||||
DropERC721__factory, DropERC721_V3__factory,
|
||||
DropERC721__factory,
|
||||
DropERC721_V3__factory,
|
||||
TokenERC721__factory,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { expect } from "chai";
|
||||
import { assert, expect } from "chai";
|
||||
import { ethers } from "ethers";
|
||||
import { readFileSync } from "fs";
|
||||
import invariant from "tiny-invariant";
|
||||
@@ -95,15 +98,13 @@ describe("Publishing", async () => {
|
||||
|
||||
it("should extract all features", async () => {
|
||||
const tokenFeatures = getAllDetectedFeatureNames(TokenERC721__factory.abi);
|
||||
expect(
|
||||
tokenFeatures
|
||||
).to.contain("ERC721Enumerable");
|
||||
expect(
|
||||
getAllDetectedFeatureNames(DropERC721__factory.abi)
|
||||
).to.contain("ERC721ClaimPhasesV2");
|
||||
expect(
|
||||
getAllDetectedFeatureNames(DropERC721_V3__factory.abi)
|
||||
).to.contain("ERC721ClaimPhasesV1");
|
||||
expect(tokenFeatures).to.contain("ERC721Enumerable");
|
||||
expect(getAllDetectedFeatureNames(DropERC721__factory.abi)).to.contain(
|
||||
"ERC721ClaimPhasesV2",
|
||||
);
|
||||
expect(getAllDetectedFeatureNames(DropERC721_V3__factory.abi)).to.contain(
|
||||
"ERC721ClaimPhasesV1",
|
||||
);
|
||||
});
|
||||
|
||||
it("should update bio", async () => {
|
||||
@@ -466,6 +467,30 @@ describe("Publishing", async () => {
|
||||
expect(nftsAfter[1].supply).to.equal(0);
|
||||
});
|
||||
|
||||
it("ERC1155Signature mint feature detection", async () => {
|
||||
const ipfsUri = "ipfs://QmNuKYGZoiHyumKjT7gPk3vwy3WKt7gTf1hKGZ2eyGZGRd";
|
||||
const addr = await sdk.deployer.deployContractFromUri(ipfsUri, [
|
||||
"test",
|
||||
"test",
|
||||
]);
|
||||
const c = await sdk.getContract(addr);
|
||||
const payload = {
|
||||
metadata: {
|
||||
name: "SigMinted Edition",
|
||||
},
|
||||
to: samWallet.address, // Who will receive the NFT (or AddressZero for anyone)
|
||||
price: 0.5, // the price to pay for minting
|
||||
royaltyBps: 100, // custom royalty fees for this NFT (in bps)
|
||||
quantity: "1",
|
||||
};
|
||||
|
||||
const goodPayload = await c.erc1155.signature.generate(payload);
|
||||
const valid = await c.erc1155.signature.verify(goodPayload);
|
||||
expect(valid).to.eq(true);
|
||||
const tx = await c.erc1155.signature.mint(goodPayload);
|
||||
expect(tx.id.toNumber()).to.eq(0);
|
||||
});
|
||||
|
||||
it("Constructor params with tuples", async () => {
|
||||
const ipfsUri = "ipfs://QmZQa56Cj1gFnZgKSkvGE5uzhaQrQV3nU6upDWDusCaCwY/0";
|
||||
const addr = await sdk.deployer.deployContractFromUri(ipfsUri, [
|
||||
|
||||
Reference in New Issue
Block a user