Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
517e2a75e3 Version Packages (#442)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-02 16:26:33 -08:00
Jonas DanielsandGitHub 72692689e9 initialize pay package (#433) 2022-12-02 22:52:04 +00:00
Jonas DanielsandGitHub efc56fa580 [SDK] - improved batch provider (#440) 2022-12-02 22:39:11 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
564b17e760 Version Packages (#419)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-12-03 11:25:35 +13:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
b4507db963 Bump decode-uri-component from 0.2.0 to 0.2.2 (#439)
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-02 14:10:31 -08:00
Joaquim VergesandGitHub f451da6395 [SDK] Fix SigMint detection for OSRF NFT Collection contracts (#438) 2022-12-03 11:00:21 +13:00
Jonas DanielsandGitHub ee47840767 fix #429 (#436) 2022-12-02 10:47:46 -08:00
Adam MajmudarandGitHub bef97f72f4 [CLI] Make solc default backup compilation (#435) 2022-12-02 17:11:20 +00:00
Adam MajmudarandGitHub 3e87dd3ccb [CLI] display file names when there are duplicate contract names (#432) 2022-12-02 03:21:40 +00:00
Joaquim VergesandGitHub def42511ff [SOL] Remove workaround for getLargestAccount (#434) 2022-12-02 02:42:26 +00:00
Adam MajmudarandGitHub c42545a647 [CLI] fix CLI on paths with spaces for windows (#431) 2022-12-01 19:03:54 -05:00
Adam MajmudarandGitHub 6e20d1f21a [CLI] allow adding contracts to existing projects (#428) 2022-12-01 20:31:06 +00:00
Joaquim VergesandGitHub e684d7e1ca [CLI] Fix metadata upload with special characters (#430) 2022-12-01 20:14:27 +00:00
Adam MajmudarandGitHub f545a67e9f [Auth] include credentials when requesting user (#425) 2022-12-01 18:17:31 +00:00
Jake LooandGitHub 034a257442 [SDK] Support more types of Minimal Proxy (#429) 2022-12-01 20:30:09 +13:00
1bfb91dbf3 [SDK] Improve error parsing for other wallets (#424)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-30 18:02:21 -08:00
Jonas DanielsandGitHub 5d95cbcdb2 fix turbo (#427) 2022-12-01 00:52:06 +00:00
Jonas DanielsandGitHub 639e535ed5 init unity-js-bridge package (#420) 2022-11-30 22:36:16 +00:00
af3acc6b10 [SDK] Add support for AWS KMS (#416)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-28 20:34:24 -08:00
Joaquim VergesandGitHub eca47763cd [SDK] increase gas limit for opening packs (#418) 2022-11-29 03:30:15 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
37b5bec897 Version Packages (#417)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-28 18:23:23 -08:00
Nacho IacovinoandGitHub 48d91e4431 [CLI] Fix mappings for prebuilts (#415) 2022-11-28 18:13:00 -08:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
a20be881e3 Version Packages (#406)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-11-29 12:09:38 +13:00
Joaquim VergesandGitHub 47f8945325 [SDK] improve error parsing (#414) 2022-11-29 11:45:33 +13:00
Furqan RydhanandGitHub 1b0bf99da7 Improvements to the App Deploy Flow (#413) 2022-11-28 14:23:44 -08:00
f03be398e0 [SDK] Update signTypedData domain to match contract (#412)
Co-authored-by: Joaquim Verges <[email protected]>
2022-11-28 03:00:50 +00:00
27996cb4bc [CLI] - feat: add solana support to cli (#403)
Co-authored-by: Jarrod Watts <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-24 01:01:00 +00:00
2eaa67acbe [CLI+SDK] app-uri (#283)
Co-authored-by: Furqan Rydhan <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-23 09:17:29 +00:00
Jonas Daniels aafd7968df bring the registry-url back apparently it was important 2022-11-23 01:05:43 -08:00
Jonas DanielsandGitHub 48797c7cf0 [SDK] - add gasless feature detection (#408) 2022-11-23 00:53:59 -08:00
Jonas DanielsandGitHub 4edcb37100 update prettier & format (#407) 2022-11-23 06:41:39 +00:00
83dcaf7058 Expose sdk.fromWallet and wallet classes (#402)
Co-authored-by: Jonas Daniels <[email protected]>
2022-11-22 18:15:28 -05:00
129 changed files with 5976 additions and 1425 deletions
+1 -1
View File
@@ -13,6 +13,6 @@ runs:
- name: Install dependencies
shell: bash
run: yarn install
run: yarn install --frozen-lockfile
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
-1
View File
@@ -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.
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/auth
## 2.0.35
## 2.0.34
### Patch Changes
- [#425](https://github.com/thirdweb-dev/js/pull/425) [`f545a67`](https://github.com/thirdweb-dev/js/commit/f545a67e9fb597d27bb39ca515d24d306fbb121a) Thanks [@adam-maj](https://github.com/adam-maj)! - Add sameSite none and include credentials
## 2.0.33
## 2.0.32
## 2.0.31
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "2.0.32",
"version": "2.0.35",
"exports": {
"./next": {
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
@@ -43,7 +43,7 @@ export default async function handler(
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
sameSite: "none",
}),
);
+2 -2
View File
@@ -40,11 +40,11 @@ export function ThirdwebNextAuth(cfg: ThirdwebNextAuthConfig) {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
sameSite: "none",
}),
);
return { address };
return { id: address, address };
} catch (err) {
return null;
}
+1 -1
View File
@@ -47,7 +47,7 @@ export default async function handler(
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
sameSite: "none",
}),
);
@@ -47,7 +47,7 @@ export default async function handler(
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
sameSite: "none",
}),
);
+45
View File
@@ -1,5 +1,50 @@
# thirdweb
## 0.9.3
### Patch Changes
- Updated dependencies [[`efc56fa`](https://github.com/thirdweb-dev/js/commit/efc56fa5802490ac8ef50037658d046afd89e9a1)]:
- @thirdweb-dev/sdk@3.6.5
## 0.9.2
### Patch Changes
- [#430](https://github.com/thirdweb-dev/js/pull/430) [`e684d7e`](https://github.com/thirdweb-dev/js/commit/e684d7e1ca16f633bce8698529d16bf60c264aa4) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix uploading contracts with special characters in the comments
- [#435](https://github.com/thirdweb-dev/js/pull/435) [`bef97f7`](https://github.com/thirdweb-dev/js/commit/bef97f72f4860596b7b70b05e67fcadf2c9a012e) Thanks [@adam-maj](https://github.com/adam-maj)! - Make solc default backup compilation
- [#432](https://github.com/thirdweb-dev/js/pull/432) [`3e87dd3`](https://github.com/thirdweb-dev/js/commit/3e87dd3ccb04a2b73437357c939a251e83b783b2) Thanks [@adam-maj](https://github.com/adam-maj)! - Display file name on duplicate contract names
- [#431](https://github.com/thirdweb-dev/js/pull/431) [`c42545a`](https://github.com/thirdweb-dev/js/commit/c42545a64725492136025fa1dcdb4d46ba674a30) Thanks [@adam-maj](https://github.com/adam-maj)! - Add changeset
- [#428](https://github.com/thirdweb-dev/js/pull/428) [`6e20d1f`](https://github.com/thirdweb-dev/js/commit/6e20d1f21a622f102e94b956f700d0871c0dc779) Thanks [@adam-maj](https://github.com/adam-maj)! - Update CLI to enable create contract inside contracts project
- Updated dependencies [[`f451da6`](https://github.com/thirdweb-dev/js/commit/f451da6395689a5f89800ee63f34b6175b61f703), [`af3acc6`](https://github.com/thirdweb-dev/js/commit/af3acc6b10751b840e56aef6400da5eea6040df2), [`1bfb91d`](https://github.com/thirdweb-dev/js/commit/1bfb91dbf3a39160c987a425813ac5dbb84703ad), [`ee47840`](https://github.com/thirdweb-dev/js/commit/ee478407673b0416e0c1cfe2be11bd6963395348), [`def4251`](https://github.com/thirdweb-dev/js/commit/def42511ff5a20d83f9094164dafb87e412571b5), [`034a257`](https://github.com/thirdweb-dev/js/commit/034a257442314c67729f7bafcbe3740cff33fa32), [`eca4776`](https://github.com/thirdweb-dev/js/commit/eca47763cd89cc3b7aa57b542971837987540b55), [`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45)]:
- @thirdweb-dev/sdk@3.6.4
## 0.9.1
### Patch Changes
- [#415](https://github.com/thirdweb-dev/js/pull/415) [`48d91e4`](https://github.com/thirdweb-dev/js/commit/48d91e4431a3740d0f98f730ed5abf5b287f92c1) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix mappings for prebuilts redirect on deploy
## 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
+3 -1
View File
@@ -1,6 +1,8 @@
export const CREATE_MESSAGES = {
typeOfProject: "What type of project do you want to create?",
projectName: "What is your project named?",
contractName: "What will be the name of your new smart contract?",
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?",
@@ -9,5 +11,5 @@ export const CREATE_MESSAGES = {
export const ERROR_MESSAGES = {
noConfiguration:
"Failed to find a supported project configuration file in current directory",
"No contract project configuration file found in current directory",
};
+3 -1
View File
@@ -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");
+3 -3
View File
@@ -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/",
);
+4
View File
@@ -19,6 +19,10 @@ describe("npx thirdweb detect", () => {
await create.waitForText(CREATE_MESSAGES.framework);
// select hardhat
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.contractName);
// set contract name
await create.writeText("Contract");
await create.pressKey("enter");
await create.waitForText(CREATE_MESSAGES.contract);
// select ERC721
await create.pressKey("arrowDown");
+2 -2
View File
@@ -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 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.8.18",
"version": "0.9.3",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+81 -48
View File
@@ -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,40 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
.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);
.option(
"-c, --contract-name [name]",
"Name of the new smart contract to create",
)
.action(async (type, path, options) => {
await twCreate(type, path, options);
});
program
@@ -87,39 +93,33 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
.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)",
)
.option(
"-f, --file [name]",
"Filter for contract files that contain this file name",
)
.option(
"-cn, --contract-name [name]",
"Filter for contracts that contain this contract name",
)
.option(
"-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
@@ -128,13 +128,24 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
"Release your protocol so other devs can deploy them and unlock SDKs, Dashboards and Analytics",
)
.option("-p, --path <project-path>", "path to project", ".")
.option(
"-f, --file [name]",
"Filter for contract files that contain the file name",
)
.option(
"-cn, --contract-name [name]",
"Filter for contracts that contain this contract name",
)
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
.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 +155,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
+43 -6
View File
@@ -76,7 +76,7 @@ export async function processProject(
}
}
let compiledResult;
let compiledResult: { contracts: ContractPayload[] };
const compileLoader = spinner("Compiling project...");
try {
compiledResult = await build(projectPath, projectType);
@@ -106,10 +106,47 @@ export async function processProject(
if (options.ci) {
selectedContracts = compiledResult.contracts;
} else {
const choices = compiledResult.contracts.map((c) => ({
name: c.name,
value: c,
}));
const filtered = compiledResult.contracts
.filter((c) => {
if (typeof options.file === "string" && options.file.length > 0) {
return c.fileName.includes(options.file);
}
return true;
})
.filter((c) => {
if (
typeof options.contractName === "string" &&
options.contractName.length > 0
) {
return c.name.includes(options.contractName);
}
return true;
});
if (filtered.length === 0) {
logger.error(`No contracts found matching the specified filters.`);
process.exit(1);
}
const choices = filtered.map((c) => {
if (
compiledResult.contracts.filter(
(other: ContractPayload) => other.name === c.name,
).length > 1
) {
return {
name: `${c.name} - ${chalk.gray(c.fileName)}`,
value: c,
};
}
return {
name: c.name,
value: c,
};
});
const prompt = createContractsPrompt(
choices,
`Choose which contract(s) to ${command}`,
@@ -164,7 +201,7 @@ export async function processProject(
const metadataURIs = await Promise.all(
selectedContracts.map(async (c) => {
logger.debug(`Uploading ${c.name}...`);
return await storage.upload(JSON.parse(c.metadata), {
return await storage.upload(JSON.parse(JSON.stringify(c.metadata)), {
uploadWithoutDirectory: true,
});
}),
+1
View File
@@ -55,6 +55,7 @@ export class BrownieBuilder extends BaseBuilder {
metadata: {},
bytecode,
sources: [], // TODO
fileName: "", // TODO
name: contractName,
});
break;
+6
View File
@@ -88,6 +88,11 @@ export class FoundryBuilder extends BaseBuilder {
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
parsedMetadata?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (
this.shouldProcessContract(
contractInfo.abi,
@@ -99,6 +104,7 @@ export class FoundryBuilder extends BaseBuilder {
name: contractName,
metadata,
bytecode,
fileName,
sources,
});
}
+7 -1
View File
@@ -25,7 +25,7 @@ export class HardhatBuilder extends BaseBuilder {
//the hardhat extractor **logs out** the runtime config of hardhat, we take that stdout and parse it
const stringifiedConfig = (
await execute(
`npx hardhat run ${configExtractorScriptPath} --no-compile`,
`npx hardhat run "${configExtractorScriptPath}" --no-compile`,
options.projectPath,
)
).stdout;
@@ -123,11 +123,17 @@ export class HardhatBuilder extends BaseBuilder {
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
meta?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
fileName,
sources,
});
}
+6
View File
@@ -140,11 +140,17 @@ export class SolcBuilder extends BaseBuilder {
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
parsedMetadata?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
fileName,
sources: _sources,
});
}
+7
View File
@@ -72,11 +72,18 @@ export class TruffleBuilder extends BaseBuilder {
return undefined;
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
parsedMetadata?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
fileName,
sources,
});
}
@@ -36,6 +36,7 @@ export default async function detect(
name: "continue",
message:
"Do you want to continue and compile this project with solc instead?",
initial: true,
});
const shouldCompile = await prompt.run();
if (!shouldCompile) {
+11
View File
@@ -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");
}
}
+14
View File
@@ -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")
);
}
}
+11
View File
@@ -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");
}
}
@@ -17,6 +17,11 @@ export interface ContractPayload {
*/
metadata: any;
/**
* The file name of the original compiled file
*/
fileName: string;
/**
* The source file paths
*/
@@ -4,4 +4,10 @@ export type ProjectType =
| "hardhat"
| "truffle"
| "solc"
| "vite"
| "next"
| "cra"
| "spa-webapp"
| "yarn"
| "npm"
| "none";
+262 -131
View File
@@ -2,60 +2,82 @@
/* eslint-disable import/no-extraneous-dependencies */
import { CREATE_MESSAGES } from "../../constants/constants";
import detect from "../core/detection/detect";
import { DownloadError, createApp } from "./helpers/create-app";
import { createContract } from "./helpers/create-contract";
import { createContractProject } from "./helpers/create-contract";
import { getPkgManager } from "./helpers/get-pkg-manager";
import { validateNpmName } from "./helpers/validate-pkg";
import chalk from "chalk";
import fs from "fs";
import path from "path";
import prompts from "prompts";
let projectType: string = "";
let contractName: 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) {
projectType = "app";
if (typeof options.contractName === "string") {
contractName = options.contractName;
}
if (options.contract) {
if (pType === "app" || options.app) {
projectType = "app";
} 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,103 +98,216 @@ 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";
}
if (!projectPath) {
const defaultName =
projectType === "contract" ? "thirdweb-contracts" : "thirdweb-app";
const res = await prompts({
type: "text",
name: "path",
message: CREATE_MESSAGES.projectName,
initial: options.template || defaultName,
format: (name: string) => name.toLowerCase(),
validate: (name: string) => {
const validation = validateNpmName(
path.basename(path.resolve(name.toLowerCase())),
);
if (validation.valid) {
return true;
}
return "Invalid project name: " + validation.problems?.[0];
},
});
if (typeof res.path === "string") {
projectPath = res.path.trim();
}
}
if (!projectPath) {
console.log(
"\nPlease specify the project directory:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"<project-directory>",
)}\n` +
"For example:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"my-thirdweb-app",
)}\n\n` +
`Run ${chalk.cyan("npx thirdweb --help")} to see all options.`,
);
process.exit(1);
}
if (!options.template) {
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" },
],
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
}
}
if (projectType === "app" && !language) {
const res = await prompts({
type: "select",
name: "language",
message: CREATE_MESSAGES.language,
choices: [
{ title: "JavaScript", value: "javascript" },
{ title: "TypeScript", value: "typescript" },
],
});
if (typeof res.language === "string") {
language = res.language.trim();
}
}
// Whether to only create a new contract without the project
let onlyContract = false;
if (projectType === "contract") {
const resolvedProjectPath = path.resolve(projectPath);
const contractProjectType = await detect(resolvedProjectPath, {});
if (
projectType === "contract" &&
framework !== "forge" &&
framework !== "hardhat"
contractProjectType === "foundry" &&
fs.existsSync(path.join(resolvedProjectPath, "src"))
) {
onlyContract = true;
projectPath = path.join(projectPath, "src");
} else if (contractProjectType === "hardhat") {
if (fs.existsSync(path.join(resolvedProjectPath, "contracts"))) {
onlyContract = true;
projectPath = path.join(projectPath, "contracts");
} else {
// If there's no expected contracts folder, check for a directory that has .sol files
const directories = fs
.readdirSync(resolvedProjectPath)
.filter((file) =>
fs.lstatSync(path.join(resolvedProjectPath, file)).isDirectory(),
);
for (const directory of directories) {
const files = fs.readdirSync(path.join(projectPath, directory));
if (files.some((file) => file.endsWith(".sol"))) {
onlyContract = true;
projectPath = path.join(projectPath, directory);
break;
}
}
}
}
}
if (!onlyContract) {
if (!projectPath) {
const defaultName =
projectType === "contract" ? "thirdweb-contracts" : "thirdweb-app";
const res = await prompts({
type: "select",
name: "framework",
message: CREATE_MESSAGES.framework,
choices: [
{ title: "Hardhat", value: "hardhat" },
{ title: "Forge", value: "forge" },
],
type: "text",
name: "path",
message: CREATE_MESSAGES.projectName,
initial: options.template || defaultName,
format: (name: string) => name.toLowerCase(),
validate: (name: string) => {
const validation = validateNpmName(
path.basename(path.resolve(name.toLowerCase())),
);
if (validation.valid) {
return true;
}
return "Invalid project name: " + validation.problems?.[0];
},
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
if (typeof res.path === "string") {
projectPath = path.join(projectPath, res.path.trim());
}
}
if (!projectPath) {
console.log(
"\nPlease specify the project directory:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"<project-directory>",
)}\n` +
"For example:\n" +
` ${chalk.cyan("npx thirdweb create")} ${chalk.green(
"my-thirdweb-app",
)}\n\n` +
`Run ${chalk.cyan("npx thirdweb --help")} to see all options.`,
);
process.exit(1);
}
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:
// 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") {
framework = res.framework.trim();
}
}
if (projectType === "app" && !language) {
const res = await prompts({
type: "select",
name: "language",
message: CREATE_MESSAGES.language,
choices: [
{ title: "JavaScript", value: "javascript" },
{ title: "TypeScript", value: "typescript" },
],
});
if (typeof res.language === "string") {
language = res.language.trim();
}
}
if (
projectType === "contract" &&
framework !== "forge" &&
framework !== "hardhat"
) {
const res = await prompts({
type: "select",
name: "framework",
message: CREATE_MESSAGES.framework,
choices: [
{ title: "Hardhat", value: "hardhat" },
{ title: "Forge", value: "forge" },
],
});
if (typeof res.framework === "string") {
framework = res.framework.trim();
}
}
}
if (!framework) {
console.log("Please specify a framework");
process.exit(1);
}
if (!language) {
// Default = JavaScript
language = "javascript";
}
}
if (projectType === "contract") {
// Select contract name
if (!contractName) {
const defaultName = "MyContract";
const res = await prompts({
type: "text",
name: "path",
message: CREATE_MESSAGES.contractName,
initial: defaultName,
validate: (name: string) => {
const isValid = /(^[a-z0-9A-Z]+$)|(^[a-z0-9A-Z]+\.sol$)/.test(name);
if (isValid) {
return true;
}
return `Invalid contract name 'contractName' (only alphanumeric characters allowed)`;
},
});
if (typeof res.path === "string") {
contractName = res.path.trim();
}
if (!contractName) {
console.log(
"\nPlease specify the contract name:\n" +
` ${chalk.cyan(
"npx thirdweb create --contract --name",
)} ${chalk.green("<contract-name>")}\n` +
"For example:\n" +
` ${chalk.cyan(
"npx thirdweb create --contract --name",
)} ${chalk.green("MyContract")}\n\n` +
`Run ${chalk.cyan("npx thirdweb --help")} to see all options.`,
);
process.exit(1);
}
}
@@ -184,7 +319,9 @@ export async function twCreate(pPath: string = "", options: any) {
name: "standard",
message: CREATE_MESSAGES.contract,
choices: [
{ title: "Empty Contract", value: "none" },
...(!onlyContract
? [{ title: "Empty Contract", value: "none" }]
: []),
{ title: "ERC721", value: "erc721" },
{ title: "ERC20", value: "erc20" },
{ title: "ERC1155", value: "erc1155" },
@@ -238,20 +375,11 @@ export async function twCreate(pPath: string = "", options: any) {
}
}
}
if (!framework) {
console.log("Please specify a framework");
process.exit(1);
}
if (!language) {
// Default = JavaScript
language = "javascript";
}
}
const resolvedProjectPath = path.resolve(projectPath);
const projectName = path.basename(resolvedProjectPath);
// Resolve project path
projectPath = path.resolve(projectPath);
const projectName = path.basename(projectPath);
const { valid, problems } = validateNpmName(projectName);
if (!valid) {
@@ -283,18 +411,21 @@ export async function twCreate(pPath: string = "", options: any) {
try {
if (projectType === "app") {
await createApp({
appPath: resolvedProjectPath,
appPath: projectPath,
packageManager,
framework,
language,
template,
chain,
});
} else {
await createContract({
contractPath: resolvedProjectPath,
await createContractProject({
contractPath: projectPath,
packageManager,
framework,
contractName,
baseContract,
onlyContract,
});
}
} catch (reason) {
@@ -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) {
@@ -11,22 +11,28 @@ import { submodules } from "./submodules";
import { downloadAndExtractRepo } from "./templates";
import retry from "async-retry";
import chalk from "chalk";
import fs from "fs";
import { writeFile } from "fs/promises";
import path from "path";
interface ICreateContract {
interface ICreateContractProject {
contractPath: string;
packageManager: PackageManager;
contractName: string;
framework?: string;
baseContract?: string;
onlyContract: boolean;
}
export async function createContract({
export async function createContractProject({
contractPath,
packageManager,
framework,
contractName,
baseContract,
}: ICreateContract) {
onlyContract,
}: ICreateContractProject) {
// Check that the selected base contract is valid
if (baseContract) {
const found = hasBaseContract(baseContract);
@@ -40,8 +46,8 @@ export async function createContract({
}
}
// Check that we have write permission for the specified directory
const root = path.resolve(contractPath);
if (!(await isWriteable(path.dirname(root)))) {
console.error(
"The application path is not writable, please check folder permissions and try again.",
@@ -52,117 +58,165 @@ export async function createContract({
process.exit(1);
}
const projectName = path.basename(root);
await makeDir(root);
if (!isFolderEmpty(root, projectName)) {
// Check that the contract name is valid
if (!/(^[a-z0-9A-Z]+$)|(^[a-z0-9A-Z]+\.sol$)/.test(contractName)) {
console.error(
`Contract name ${chalk.red(
`"${contractName}"`,
)} is not valid (only alphanumeric characters are allowed).`,
);
process.exit(1);
}
// Clean the contract name
contractName = contractName.replace(/\.sol$/, "") + ".sol";
const useYarn = packageManager === "yarn";
const isOnline = !useYarn || (await getOnline());
const originalDirectory = process.cwd();
console.log(
`Creating a new thirdweb contracts project in ${chalk.green(root)}.`,
);
console.log();
process.chdir(root);
function isErrorLike(err: unknown): err is { message: string } {
return (
typeof err === "object" &&
err !== null &&
typeof (err as { message?: unknown }).message === "string"
);
}
try {
console.log(`Downloading files. This might take a moment.`);
let starter = "";
if (framework === "hardhat") {
starter = "hardhat-javascript-starter";
} else if (framework === "forge") {
starter = "forge-starter";
} else {
console.error("Please provide a valid contracts framework.");
if (onlyContract) {
// If we are already in a contracts project, just create a new contract
if (fs.existsSync(path.join(root, contractName))) {
console.error(
`A contract with the name ${chalk.red(
`"${contractName}"`,
)} already exists in specified directory.`,
);
process.exit(1);
}
await retry(
() => downloadAndExtractRepo(root, { name: starter, filePath: "" }),
{
retries: 3,
},
);
// Modify the /contracts/MyContract.sol contents to match the base contract
if (baseContract && baseContract.length > 0) {
const baseContractText = readBaseContract(baseContract);
// Set the contents of the Contract.sol file to the base contract
let contractFile = "";
if (framework === "hardhat") {
contractFile = path.join(root, "contracts", "Contract.sol");
}
if (framework === "forge") {
contractFile = path.join(root, "src", "Contract.sol");
}
contractFile = path.join(root, contractName);
// Write the base contract to the MyContract.sol file
await writeFile(contractFile, baseContractText);
console.log(
`${chalk.green("Success!")} Created ${contractName} at ${contractPath}`,
);
}
} catch (reason) {
throw new DownloadError(
isErrorLike(reason) ? reason.message : String(reason),
} else {
// Otherwise, create a new contracts project
const projectName = path.basename(root);
await makeDir(root);
if (!isFolderEmpty(root, projectName)) {
process.exit(1);
}
const isOnline = !useYarn || (await getOnline());
const originalDirectory = process.cwd();
console.log(
`Creating a new thirdweb contracts project in ${chalk.green(root)}.`,
);
}
console.log();
console.log("Installing packages. This might take a couple of minutes.");
console.log();
process.chdir(root);
await install(root, null, { packageManager, isOnline });
console.log();
function isErrorLike(err: unknown): err is { message: string } {
return (
typeof err === "object" &&
err !== null &&
typeof (err as { message?: unknown }).message === "string"
);
}
if (tryGitInit(root)) {
console.log("Initialized a git repository.");
try {
console.log(`Downloading files. This might take a moment.`);
let starter = "";
if (framework === "hardhat") {
starter = "hardhat-javascript-starter";
} else if (framework === "forge") {
starter = "forge-starter";
} else {
console.error("Please provide a valid contracts framework.");
process.exit(1);
}
await retry(
() => downloadAndExtractRepo(root, { name: starter, filePath: "" }),
{
retries: 3,
},
);
// Add in a new contracts file with specific base contract
if (baseContract && baseContract.length > 0) {
const baseContractText = readBaseContract(baseContract);
// Set the filename of the new file and delete the dummy Contract.sol file
let contractFile = "";
if (framework === "hardhat") {
fs.unlinkSync(path.join(root, "contracts", "Contract.sol"));
contractFile = path.join(root, "contracts", contractName);
}
if (framework === "forge") {
fs.unlinkSync(path.join(root, "src", "Contract.sol"));
contractFile = path.join(root, "src", contractName);
}
// Write the base contract to the new file
await writeFile(contractFile, baseContractText);
}
} catch (reason) {
throw new DownloadError(
isErrorLike(reason) ? reason.message : String(reason),
);
}
console.log("Installing packages. This might take a couple of minutes.");
console.log();
await install(root, null, { packageManager, isOnline });
console.log();
if (tryGitInit(root)) {
console.log("Initialized a git repository.");
console.log();
}
if (framework === "forge") {
await submodules();
}
let cdpath: string;
if (path.join(originalDirectory, projectName) === contractPath) {
cdpath = projectName;
} else {
cdpath = contractPath;
}
console.log(
`${chalk.green("Success!")} Created ${projectName} at ${contractPath}`,
);
console.log();
console.log("Inside that directory, you can run several commands:");
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} build`),
);
console.log(
" Compiles your contracts and detects thirdweb extensions implemented on them.",
);
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} deploy`),
);
console.log(" Deploys your contracts with the thirdweb deploy flow.");
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} release`),
);
console.log(" Releases your contracts with the thirdweb release flow.");
console.log();
console.log("We suggest that you begin by typing:");
console.log();
console.log(chalk.cyan(" cd"), cdpath);
console.log();
}
if (framework === "forge") {
await submodules();
}
let cdpath: string;
if (path.join(originalDirectory, projectName) === contractPath) {
cdpath = projectName;
} else {
cdpath = contractPath;
}
console.log(
`${chalk.green("Success!")} Created ${projectName} at ${contractPath}`,
);
console.log();
console.log("Inside that directory, you can run several commands:");
console.log();
console.log(chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} build`));
console.log(
" Compiles your contracts and detects thirdweb extensions implemented on them.",
);
console.log();
console.log(chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} deploy`));
console.log(" Deploys your contracts with the thirdweb deploy flow.");
console.log();
console.log(
chalk.cyan(` ${packageManager}${useYarn ? "" : " run"} release`),
);
console.log(" Releases your contracts with the thirdweb release flow.");
console.log();
console.log("We suggest that you begin by typing:");
console.log();
console.log(chalk.cyan(" cd"), cdpath);
console.log();
}
+16 -1
View File
@@ -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}` });
+43
View File
@@ -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");
}
}
+54
View File
@@ -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();
}
@@ -13,18 +13,18 @@ export default function generateDashboardUrl(
// Pre-built contract
else {
const mapping = {
"signature-drop": "drop/signature-drop",
"nft-drop": "drop/nft-drop",
"edition-drop": "drop/edition-drop",
"nft-collection": "token/nft-collection",
edition: "token/edition",
"token-drop": "drop/token-drop",
token: "token/token",
marketplace: "marketplace/marketplace",
split: "governance/split",
vote: "governance/vote",
pack: "token/pack",
multiwrap: "token/multiwrap",
"signature-drop": "SignatureDrop",
"nft-drop": "DropERC721",
"edition-drop": "DropERC1155",
"nft-collection": "TokenERC721",
edition: "TokenERC1155",
"token-drop": "DropERC20",
token: "TokenERC20",
marketplace: "Marketplace",
split: "Split",
vote: "VoteERC20",
pack: "Pack",
multiwrap: "Multiwrap",
};
const foundMapping = mapping[name as keyof typeof mapping];
@@ -33,6 +33,6 @@ export default function generateDashboardUrl(
return undefined;
}
return `https://thirdweb.com/contracts/new/pre-built/${foundMapping}`;
return `https://thirdweb.com/thirdweb.eth/${foundMapping}`;
}
}
+4 -26
View File
@@ -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;
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/contracts-js
## 1.2.2
### Patch Changes
- [#438](https://github.com/thirdweb-dev/js/pull/438) [`f451da6`](https://github.com/thirdweb-dev/js/commit/f451da6395689a5f89800ee63f34b6175b61f703) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix SigMint detection for OSRF NFT Collection contracts
## 1.2.1
### Patch Changes
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "@thirdweb-dev/contracts-js",
"version": "1.2.1",
"version": "1.2.2",
"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.2"
"@thirdweb-dev/contracts": "3.2.8-0"
},
"devDependencies": {
"@babel/preset-env": "^7.18.10",
+6
View File
@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-typescript",
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
]
}
+4
View File
@@ -0,0 +1,4 @@
# folders
dist/
# files
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["thirdweb"],
};
+7
View File
@@ -0,0 +1,7 @@
# @thirdweb-dev/pay
## 0.1.0
### Minor Changes
- [#433](https://github.com/thirdweb-dev/js/pull/433) [`7269268`](https://github.com/thirdweb-dev/js/commit/72692689e9adb41e92038765fd01a36c2b7457e1) Thanks [@jnsdls](https://github.com/jnsdls)! - Initialize Pay Package
+16
View File
@@ -0,0 +1,16 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb wallets</h1>
<p align="center">
<!-- <a href="https://www.npmjs.com/package/@thirdweb-dev/sdk"><img src="https://img.shields.io/npm/v/@thirdweb-dev/sdk?color=red&label=npm&logo=npm" alt="npm version"/></a> -->
<!-- <a href="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml/badge.svg"/></a> -->
<!-- <a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a> -->
</p>
<!-- <p align="center"><strong>Best in class Web3 SDK for Browser, Node and Mobile apps</strong></p> -->
<br />
### todo
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-pay-integrations-coinbase.cjs.js",
"module": "dist/thirdweb-dev-pay-integrations-coinbase.esm.js"
}
+53
View File
@@ -0,0 +1,53 @@
{
"name": "@thirdweb-dev/pay",
"version": "0.1.0",
"main": "dist/thirdweb-dev-pay.cjs.js",
"module": "dist/thirdweb-dev-pay.esm.js",
"exports": {
".": {
"module": "./dist/thirdweb-dev-pay.esm.js",
"default": "./dist/thirdweb-dev-pay.cjs.js"
},
"./integrations/coinbase": {
"module": "./integrations/coinbase/dist/thirdweb-dev-pay-integrations-coinbase.esm.js",
"default": "./integrations/coinbase/dist/thirdweb-dev-pay-integrations-coinbase.cjs.js"
},
"./package.json": "./package.json"
},
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/pay",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>",
"files": [
"dist/",
"integrations/"
],
"preconstruct": {
"entrypoints": [
"index.ts",
"integrations/**"
],
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
"exports": true
},
"exports": true
},
"sideEffects": false,
"dependencies": {
"@coinbase/cbpay-js": "^1.6.0"
},
"devDependencies": {
"@preconstruct/cli": "^2.2.1",
"typescript": "^4.7.4"
},
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"build": "tsc && preconstruct build",
"push": "yalc push"
}
}
+1
View File
@@ -0,0 +1 @@
export * from "./integrations/coinbase";
@@ -0,0 +1,62 @@
import { initOnRamp, InitOnRampParams } from "@coinbase/cbpay-js";
interface CoinbasePayOptions extends Pick<InitOnRampParams, "appId"> {}
const CHAIN_ID_MAP = {
[-1]: "solana",
1: "ethereum",
69: "optimism",
137: "polygon",
} as const;
export type FundWalletOptions = {
address: string;
chainId: keyof typeof CHAIN_ID_MAP;
assets?: string[];
};
export class CoinbasePayIntegration {
#appId: string;
constructor(options: CoinbasePayOptions) {
this.#appId = options.appId;
}
async fundWallet(opts: FundWalletOptions): Promise<void> {
const { address, chainId, assets } = opts;
return new Promise((res, rej) => {
initOnRamp(
{
appId: this.#appId,
widgetParameters: {
destinationWallets: [
{
address,
assets,
supportedNetworks: [CHAIN_ID_MAP[chainId]],
},
],
},
experienceLoggedIn: "embedded",
experienceLoggedOut: "popup",
closeOnExit: true,
onSuccess: () => {
res();
},
onExit(error) {
if (error) {
return rej(error);
}
return res();
},
},
(err, instance) => {
if (err || !instance) {
return rej(err);
}
instance.open();
},
);
});
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "@thirdweb-dev/tsconfig/sdk.json",
"include": ["src", "types"],
"exclude": ["dist", "build", "node_modules"]
}
+12
View File
@@ -1,5 +1,17 @@
# @thirdweb-dev/react
## 3.6.5
## 3.6.4
### Patch Changes
- [#425](https://github.com/thirdweb-dev/js/pull/425) [`f545a67`](https://github.com/thirdweb-dev/js/commit/f545a67e9fb597d27bb39ca515d24d306fbb121a) Thanks [@adam-maj](https://github.com/adam-maj)! - Add sameSite none and include credentials
- [#420](https://github.com/thirdweb-dev/js/pull/420) [`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45) Thanks [@jnsdls](https://github.com/jnsdls)! - update deps
## 3.6.3
## 3.6.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.6.2",
"version": "3.6.5",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -14,19 +14,19 @@ export const SupportedNetworkSelect: React.FC<SupportedNetworkSelectProps> = ({
const deprecatedNetworks = useMemo(() => {
return SUPPORTED_CHAIN_IDS.map((supportedChain) => {
return getChainFromChainId(supportedChain);
}).filter((n) => !!n.deprecated);
});
}, []);
const testnets = useMemo(() => {
return SUPPORTED_CHAIN_IDS.map((supportedChain) => {
return getChainFromChainId(supportedChain);
}).filter((n) => n.testnet && !n.deprecated);
});
}, []);
const mainnets = useMemo(() => {
return SUPPORTED_CHAIN_IDS.map((supportedChain) => {
return getChainFromChainId(supportedChain);
}).filter((n) => !n.testnet && !n.deprecated);
});
}, []);
return (
<Select {...selectProps}>
@@ -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";
@@ -103,7 +103,10 @@ export class GnosisSafeConnector extends Connector {
import("@gnosis.pm/safe-ethers-lib"),
]);
const ethAdapter = new safeEthersLib.default({ ethers, signer });
const ethAdapter = new safeEthersLib.default({
ethers,
signerOrProvider: signer,
});
const safe = await safeCoreSdk.default.create({
ethAdapter: ethAdapter as any,
+1 -1
View File
@@ -2,7 +2,7 @@ import { NATIVE_TOKENS, SUPPORTED_CHAIN_ID } from "@thirdweb-dev/sdk";
import { ChainId } from "@thirdweb-dev/sdk";
import type { Chain as WagmiChain } from "wagmi";
export type Chain = WagmiChain & { deprecated?: boolean };
export type Chain = WagmiChain;
const chain: Record<string, Chain> = {
mainnet: {
id: ChainId.Mainnet,
+2 -2
View File
@@ -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 },
);
+3 -1
View File
@@ -24,7 +24,9 @@ export function useUser() {
authConfig,
"Please specify an authConfig in the ThirdwebProvider",
);
const res = await fetch(`${authConfig.authUrl}/user`);
const res = await fetch(`${authConfig.authUrl}/user`, {
credentials: "include",
});
return (await res.json()) as ThirdwebAuthUser;
},
{
@@ -24,7 +24,9 @@ export function useUser() {
authConfig,
"Please specify an authConfig in the ThirdwebProvider",
);
const res = await fetch(`${authConfig.authUrl}/user`);
const res = await fetch(`${authConfig.authUrl}/user`, {
credentials: "include",
});
return (await res.json()) as ThirdwebAuthUser;
},
{
@@ -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
+43
View File
@@ -1,5 +1,48 @@
# @thirdweb-dev/sdk
## 3.6.5
### Patch Changes
- [#440](https://github.com/thirdweb-dev/js/pull/440) [`efc56fa`](https://github.com/thirdweb-dev/js/commit/efc56fa5802490ac8ef50037658d046afd89e9a1) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - providers are now re-used if the constructor options are identical leading to better batching, also introduced an additional max batch size param (250 by default)
## 3.6.4
### Patch Changes
- [#438](https://github.com/thirdweb-dev/js/pull/438) [`f451da6`](https://github.com/thirdweb-dev/js/commit/f451da6395689a5f89800ee63f34b6175b61f703) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix SigMint detection for OSRF NFT Collection contracts
- [#416](https://github.com/thirdweb-dev/js/pull/416) [`af3acc6`](https://github.com/thirdweb-dev/js/commit/af3acc6b10751b840e56aef6400da5eea6040df2) Thanks [@adam-maj](https://github.com/adam-maj)! - Add support for AWS KMS
- [#424](https://github.com/thirdweb-dev/js/pull/424) [`1bfb91d`](https://github.com/thirdweb-dev/js/commit/1bfb91dbf3a39160c987a425813ac5dbb84703ad) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Improve error parsing for other wallets
- [#436](https://github.com/thirdweb-dev/js/pull/436) [`ee47840`](https://github.com/thirdweb-dev/js/commit/ee478407673b0416e0c1cfe2be11bd6963395348) Thanks [@jnsdls](https://github.com/jnsdls)! - fix #429
- [#434](https://github.com/thirdweb-dev/js/pull/434) [`def4251`](https://github.com/thirdweb-dev/js/commit/def42511ff5a20d83f9094164dafb87e412571b5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Remove workaround to fetch NFT owners
- [#429](https://github.com/thirdweb-dev/js/pull/429) [`034a257`](https://github.com/thirdweb-dev/js/commit/034a257442314c67729f7bafcbe3740cff33fa32) Thanks [@jakeloo](https://github.com/jakeloo)! - Extract more minimal proxy impl address
- [#418](https://github.com/thirdweb-dev/js/pull/418) [`eca4776`](https://github.com/thirdweb-dev/js/commit/eca47763cd89cc3b7aa57b542971837987540b55) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Increase gas limit for opening packs
- [#420](https://github.com/thirdweb-dev/js/pull/420) [`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45) Thanks [@jnsdls](https://github.com/jnsdls)! - update deps
- Updated dependencies [[`f451da6`](https://github.com/thirdweb-dev/js/commit/f451da6395689a5f89800ee63f34b6175b61f703)]:
- @thirdweb-dev/contracts-js@1.2.2
## 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
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-sdk-evm-wallets.cjs.js",
"module": "dist/thirdweb-dev-sdk-evm-wallets.esm.js"
}
+22 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.6.2",
"version": "3.6.5",
"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",
@@ -90,13 +97,24 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-tsdoc": "^0.2.16",
"ethers": "^5.7.2",
"ethers-aws-kms-signer": "^1.3.2",
"hardhat": "^2.9.3",
"mocha": "^10.0.0",
"prettier": "^2.7.1",
"typescript": "^4.7.4"
},
"peerDependencies": {
"ethers": "^5"
"@aws-sdk/client-secrets-manager": "^3.215.0",
"ethers": "^5",
"ethers-aws-kms-signer": "^1.3.2"
},
"peerDependenciesMeta": {
"@aws-sdk/client-secrets-manager": {
"optional": true
},
"ethers-aws-kms-signer": {
"optional": true
}
},
"dependencies": {
"@metaplex-foundation/js": "^0.17.6",
@@ -109,7 +127,7 @@
"bn.js": "^5.2.1",
"bs58": "^5.0.0",
"cross-fetch": "^3.1.5",
"eventemitter3": "^4.0.7",
"eventemitter3": "^5.0.0",
"fast-deep-equal": "^3.1.3",
"merkletreejs": "^0.2.24",
"tiny-invariant": "^1.2.0",
@@ -128,4 +146,4 @@
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>"
}
}
+4 -8
View File
@@ -394,20 +394,16 @@ 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);
parseMessageParts(/.*?"message[^a-zA-Z0-9]*([^"\\]*).*?/, raw) ||
parseMessageParts(/.*?"reason[^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);
@@ -266,6 +266,42 @@ function toJSType(
return jsType;
}
/**
* @internal
* @param bytecode
*/
export function extractMinimalProxyImplementationAddress(
bytecode: string,
): string | undefined {
// EIP-1167 clone minimal proxy - https://eips.ethereum.org/EIPS/eip-1167
if (bytecode.startsWith("0x363d3d373d3d3d363d73")) {
const implementationAddress = bytecode.slice(22, 62);
return `0x${implementationAddress}`;
}
// Minimal Proxy with receive() from 0xSplits - https://github.com/0xSplits/splits-contracts/blob/c7b741926ec9746182d0d1e2c4c2046102e5d337/contracts/libraries/Clones.sol
if (bytecode.startsWith("0x36603057343d5230")) {
// +40 = size of addr
const implementationAddress = bytecode.slice(122, 122 + 40);
return `0x${implementationAddress}`;
}
// 0age's minimal proxy - https://medium.com/coinmonks/the-more-minimal-proxy-5756ae08ee48
if (bytecode.startsWith("0x3d3d3d3d363d3d37363d73")) {
// +40 = size of addr
const implementationAddress = bytecode.slice(24, 24 + 40);
return `0x${implementationAddress}`;
}
// vyper's minimal proxy (uniswap v1) - https://etherscan.io/address/0x09cabec1ead1c0ba254b09efb3ee13841712be14#code
if (bytecode.startsWith("0x366000600037611000600036600073")) {
const implementationAddress = bytecode.slice(32, 32 + 40);
return `0x${implementationAddress}`;
}
return undefined;
}
/**
* @internal
* @param address
@@ -282,14 +318,20 @@ export async function resolveContractUriFromAddress(
`Contract at ${address} does not exist on chain '${chain.name}' (chainId: ${chain.chainId})`,
);
}
// EIP-1167 clone proxy - https://eips.ethereum.org/EIPS/eip-1167
if (bytecode.startsWith("0x363d3d373d3d3d363d")) {
const implementationAddress = bytecode.slice(22, 62);
return await resolveContractUriFromAddress(
`0x${implementationAddress}`,
provider,
);
try {
const implementationAddress =
extractMinimalProxyImplementationAddress(bytecode);
if (implementationAddress) {
return await resolveContractUriFromAddress(
implementationAddress,
provider,
);
}
} catch (e) {
// ignore
}
// EIP-1967 proxy storage slots - https://eips.ethereum.org/EIPS/eip-1967
try {
const proxyStorage = await provider.getStorageAt(
@@ -438,8 +480,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 +500,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 +585,8 @@ export function detectFeatures(
}
function extractFeatures(
input: Record<string, FeatureWithEnabled>,
enabledExtensions: FeatureWithEnabled[],
input: Record<string, FeatureWithEnabled>,
enabledExtensions: FeatureWithEnabled[],
) {
if (!input) {
return;
@@ -556,10 +598,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,
};
@@ -12,8 +12,8 @@ import ILazyMintAbi from "@thirdweb-dev/contracts-js/dist/abis/ILazyMint.json";
import IMintableERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/IMintableERC721.json";
import MulticallAbi from "@thirdweb-dev/contracts-js/dist/abis/IMulticall.json";
import SignatureMintERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC721.json";
import SignatureMintERC721_V1Abi from "@thirdweb-dev/contracts-js/dist/abis/ISignatureMintERC721_V1.json";
import TieredDropAbi from "@thirdweb-dev/contracts-js/dist/abis/LazyMintWithTier.json";
import TokenERC721Abi from "@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json";
export const FEATURE_NFT_BURNABLE = {
name: "ERC721Burnable",
@@ -163,7 +163,7 @@ export const FEATURE_NFT_SIGNATURE_MINTABLE_V1 = {
sdk: "sdk.erc721signaturemint",
contracts: "ISignatureMintERC721",
},
abis: [TokenERC721Abi],
abis: [SignatureMintERC721_V1Abi],
features: {},
} as const;
@@ -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;
+14 -1
View File
@@ -123,6 +123,11 @@ export function getProviderForNetwork(network: ChainOrRpc | SignerOrProvider) {
}
}
const READONLY_PROVIDER_MAP: Map<
string,
StaticJsonRpcBatchProvider | providers.JsonRpcBatchProvider
> = new Map();
/**
*
* @param network - the chain name or rpc url
@@ -136,11 +141,19 @@ export function getReadOnlyProvider(network: string, chainId?: number) {
if (match) {
switch (match[1]) {
case "http":
return chainId
const seralizedOpts = `${network}-${chainId || -1}`;
const existingProvider = READONLY_PROVIDER_MAP.get(seralizedOpts);
if (existingProvider) {
return existingProvider;
}
const newProvider = chainId
? // if we know the chainId we should use the StaticJsonRpcBatchProvider
new StaticJsonRpcBatchProvider(network, chainId)
: // otherwise fall back to the built in json rpc batch provider
new providers.JsonRpcBatchProvider(network, chainId);
READONLY_PROVIDER_MAP.set(seralizedOpts, newProvider);
return newProvider;
case "ws":
return new providers.WebSocketProvider(network, chainId);
@@ -536,10 +536,14 @@ export class Pack extends StandardErc1155<PackContract> {
tokenId: BigNumberish,
amount: BigNumberish = 1,
): Promise<PackRewards> {
const receipt = await this.contractWrapper.sendTransaction("openPack", [
tokenId,
amount,
]);
const receipt = await this.contractWrapper.sendTransaction(
"openPack",
[tokenId, amount],
{
// Higher gas limit for opening packs
gasLimit: 500000,
},
);
const event = this.contractWrapper.parseLogs<PackOpenedEvent>(
"PackOpened",
receipt?.logs,
@@ -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 });
}
}
@@ -3,7 +3,7 @@ import { ContractEvent, EventQueryOptions } from "../../types/index";
import { ContractWrapper } from "./contract-wrapper";
import { EventFragment } from "@ethersproject/abi";
import { BaseContract, Event, providers } from "ethers";
import { ListenerFn } from "eventemitter3";
import type { EventEmitter } from "eventemitter3";
/**
* Listen to Contract events in real time
@@ -28,7 +28,7 @@ export class ContractEvents<TContract extends BaseContract> {
* @param listener - the callback function that will be called on every transaction
* @public
*/
public addTransactionListener(listener: ListenerFn) {
public addTransactionListener(listener: EventEmitter.ListenerFn) {
this.contractWrapper.addListener(EventType.Transaction, listener);
}
@@ -44,7 +44,7 @@ export class ContractEvents<TContract extends BaseContract> {
* @param listener - the callback function to remove
* @public
*/
public removeTransactionListener(listener: ListenerFn) {
public removeTransactionListener(listener: EventEmitter.ListenerFn) {
this.contractWrapper.off(EventType.Transaction, listener);
}
@@ -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,
+39 -1
View File
@@ -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
*/
+114 -83
View File
@@ -1,99 +1,130 @@
import { providers, utils } from "ethers";
const DEFAULT_BATCH_TIME_LIMIT_MS = 50;
const DEFAULT_BATCH_SIZE_LIMIT = 250;
const DEFAULT_BATCH_OPTIONS = {
timeLimitMs: DEFAULT_BATCH_TIME_LIMIT_MS,
sizeLimit: DEFAULT_BATCH_SIZE_LIMIT,
};
export type BatchOptions = Partial<typeof DEFAULT_BATCH_OPTIONS>;
// mostly copied from ethers.js directly but make it a StaticJsonRpcProvider
export class StaticJsonRpcBatchProvider extends providers.StaticJsonRpcProvider {
_pendingBatchAggregator: NodeJS.Timer | null;
_pendingBatch: Array<{
request: { method: string, params: Array<any>, id: number, jsonrpc: "2.0" },
resolve: (result: any) => void,
reject: (error: Error) => void
}> | null;
private _timeLimitMs: number;
private _sizeLimit: number;
_pendingBatchAggregator: NodeJS.Timer | null;
_pendingBatch: Array<{
request: { method: string; params: Array<any>; id: number; jsonrpc: "2.0" };
resolve: (result: any) => void;
reject: (error: Error) => void;
}> | null;
constructor(url: string | utils.ConnectionInfo | undefined, network: providers.Networkish | undefined){
super(url, network);
this._pendingBatchAggregator = null;
this._pendingBatch = null;
constructor(
url: string | utils.ConnectionInfo | undefined,
network: providers.Networkish | undefined,
batchOptions: BatchOptions = DEFAULT_BATCH_OPTIONS,
) {
super(url, network);
this._timeLimitMs = batchOptions.timeLimitMs || DEFAULT_BATCH_SIZE_LIMIT;
this._sizeLimit = batchOptions.sizeLimit || DEFAULT_BATCH_TIME_LIMIT_MS;
this._pendingBatchAggregator = null;
this._pendingBatch = null;
}
private sendCurrentBatch(request: any) {
// if we still have a timeout clear that first
if (this._pendingBatchAggregator) {
clearTimeout(this._pendingBatchAggregator);
}
// Get the current batch and clear it, so new requests
// go into the next batch
const batch = this._pendingBatch || [];
this._pendingBatch = null;
this._pendingBatchAggregator = null;
send(method: string, params: Array<any>): Promise<any> {
const request = {
method: method,
params: params,
id: (this._nextId++),
jsonrpc: "2.0"
};
// Get the request as an array of requests
const request_ = batch.map((inflight) => inflight.request);
if (this._pendingBatch === null) {
this._pendingBatch = [ ];
}
this.emit("debug", {
action: "requestBatch",
request: utils.deepCopy(request),
provider: this,
});
const inflightRequest: any = { request, resolve: null, reject: null };
const promise = new Promise((resolve, reject) => {
inflightRequest.resolve = resolve;
inflightRequest.reject = reject;
return utils.fetchJson(this.connection, JSON.stringify(request_)).then(
(result) => {
this.emit("debug", {
action: "response",
request: request_,
response: result,
provider: this,
});
this._pendingBatch.push(inflightRequest);
// For each result, feed it to the correct Promise, depending
// on whether it was a success or error
batch.forEach((inflightRequest_, index) => {
const payload = result[index];
if (payload.error) {
const error = new Error(payload.error.message);
(error as any).code = payload.error.code;
(error as any).data = payload.error.data;
inflightRequest_.reject(error);
} else {
inflightRequest_.resolve(payload.result);
}
});
},
(error) => {
this.emit("debug", {
action: "response",
error: error,
request: request_,
provider: this,
});
if (!this._pendingBatchAggregator) {
// Schedule batch for next event loop + short duration
this._pendingBatchAggregator = setTimeout(() => {
// If there was an error, reject all the requests
batch.forEach((inflightRequest_) => {
inflightRequest_.reject(error);
});
},
);
}
// Get the current batch and clear it, so new requests
// go into the next batch
const batch = this._pendingBatch || [];
this._pendingBatch = null;
this._pendingBatchAggregator = null;
send(method: string, params: Array<any>): Promise<any> {
const request = {
method: method,
params: params,
id: this._nextId++,
jsonrpc: "2.0",
};
// Get the request as an array of requests
const request_ = batch.map((inflight) => inflight.request);
this.emit("debug", {
action: "requestBatch",
request: utils.deepCopy(request),
provider: this
});
return utils.fetchJson(this.connection, JSON.stringify(request_)).then((result) => {
this.emit("debug", {
action: "response",
request: request_,
response: result,
provider: this
});
// For each result, feed it to the correct Promise, depending
// on whether it was a success or error
batch.forEach((inflightRequest_, index) => {
const payload = result[index];
if (payload.error) {
const error = new Error(payload.error.message);
(<any>error).code = payload.error.code;
(<any>error).data = payload.error.data;
inflightRequest_.reject(error);
} else {
inflightRequest_.resolve(payload.result);
}
});
}, (error) => {
this.emit("debug", {
action: "response",
error: error,
request: request,
provider: this
});
batch.forEach((inflightRequest_) => {
inflightRequest_.reject(error);
});
});
}, 10);
}
return promise;
if (this._pendingBatch === null) {
this._pendingBatch = [];
}
}
const inflightRequest: any = { request, resolve: null, reject: null };
const promise = new Promise((resolve, reject) => {
inflightRequest.resolve = resolve;
inflightRequest.reject = reject;
});
// if we would go *over* the size limit of the batch with this request, send the batch now
if (this._pendingBatch.length === this._sizeLimit) {
this.sendCurrentBatch(request);
}
this._pendingBatch.push(inflightRequest);
if (!this._pendingBatchAggregator) {
// Schedule batch for next event loop + short duration
this._pendingBatchAggregator = setTimeout(() => {
this.sendCurrentBatch(request);
}, this._timeLimitMs);
}
return promise;
}
}
@@ -8,3 +8,4 @@ export * from "./token-erc20";
export * from "./token-erc721";
export * from "./token-erc1155";
export * from "./vote";
export * from "./custom";
+1 -1
View File
@@ -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,62 @@
import { AbstractWallet } from "../types/abstract";
import {
TypedDataDomain,
TypedDataField,
} from "@ethersproject/abstract-signer";
import { ethers } from "ethers";
import { AwsKmsSigner, AwsKmsSignerCredentials } from "ethers-aws-kms-signer";
/**
* Create a wallet instance using a private key stored in AWS KMS.
*
* @example
* ```javascript
* import { ThirdwebSDK } from "@thirdweb-dev/sdk"
* import { AwsKmsWallet } from "@thirdweb-dev/sdk/evm/wallets"
*
* const wallet = new AwsKmsallet({
* region: "us-east-1",
* accessKeyId: process.env.AWS_ACCESS_KEY_ID,
* secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
* sessionToken: process.env.AWS_SESSION_TOKEN,
* keyId: process.env.AWS_KEY_ID,
* });
*
* const sdk = await ThirdwebSDK.fromWallet(wallet, "mainnet");
* ```
*/
export class AwsKmsWallet extends AbstractWallet {
private cachedSigner: AwsKmsSigner;
constructor(options: AwsKmsSignerCredentials) {
super();
this.cachedSigner = this.updateSigner(new AwsKmsSigner(options));
}
async getSigner(
provider?: ethers.providers.Provider,
): Promise<ethers.Signer> {
if (provider) {
this.cachedSigner = this.updateSigner(
this.cachedSigner.connect(provider),
);
}
return this.cachedSigner;
}
// Add _signTypedData method onto the signer for now so we don't need to reimpliment
// The entire AWS KMS signer repository and maintain it ourselves.
private updateSigner(signer: AwsKmsSigner): AwsKmsSigner {
(signer as any)._signTypedData = async function (
domain: TypedDataDomain,
types: Record<string, Array<TypedDataField>>,
value: Record<string, any>,
) {
const hash = ethers.utils._TypedDataEncoder.hash(domain, types, value);
return signer._signDigest(hash);
};
return signer;
}
}
@@ -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,2 @@
export * from "./aws-secrets-manager";
export * from "./aws-kms";
+2
View File
@@ -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";
@@ -9,7 +9,6 @@ import {
} from "../../constants/addresses";
import { TransactionResult } from "../../types/common";
import { CreatorOutput } from "../../types/programs";
import { getPublicRpc } from "../../utils/urls";
import { parseCreators } from "./creators-helper";
import {
findMasterEditionV2Pda,
@@ -96,15 +95,10 @@ export class NFTHelper {
async ownerOf(nftAddress: string): Promise<string | undefined> {
try {
// TODO switch back to normal connection when alchemy supports getTokenLargestAccounts
const connection = new Connection(
getPublicRpc(this.metaplex),
"confirmed",
);
const largestAccounts = await connection.getTokenLargestAccounts(
const largestAccounts = await this.connection.getTokenLargestAccounts(
new PublicKey(nftAddress),
);
const largestAccountInfo = await connection.getParsedAccountInfo(
const largestAccountInfo = await this.connection.getParsedAccountInfo(
largestAccounts.value[0].address,
);
const parsedData = largestAccountInfo?.value?.data as ParsedAccountData;
@@ -56,7 +56,16 @@ export type NFTCollectionMetadataInput = z.input<
*/
export const TokenMetadataInputSchema = CommonContractSchema.extend({
decimals: z.number().default(9),
initialSupply: AmountSchema,
initialSupply: AmountSchema.superRefine((val, context) => {
// TODO remove this limitation when metaplex fixes https://github.com/metaplex-foundation/js/issues/421
if (Number(val) > 9999999) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: `Initial supply must less than 10M, additional supply can be minted after deployment.`,
path: ["initialSupply"],
});
}
}),
});
/**
@@ -80,7 +89,7 @@ export type UpdateCreatorInput = {
/**
* @public
*/
export type UpdateRoyaltySettingsInput = {
export type UpdateRoyaltySettingsInput = {
sellerFeeBasisPoints: number;
updateAll: boolean;
};
@@ -0,0 +1,67 @@
import {
extractMinimalProxyImplementationAddress,
ThirdwebSDK,
} from "../../src/evm";
import { signers, sdk } from "./before-setup";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { expect } from "chai";
global.fetch = require("cross-fetch");
describe("Custom Contracts", async () => {
let adminWallet: SignerWithAddress;
let realSDK: ThirdwebSDK;
before(async () => {
[adminWallet] = signers;
realSDK = new ThirdwebSDK(adminWallet);
});
beforeEach(async () => {
sdk.updateSignerOrProvider(adminWallet);
realSDK.updateSignerOrProvider(adminWallet);
});
it("should extract implementation address for eip-1167 minimal proxy", async () => {
// https://eips.ethereum.org/EIPS/eip-1167#specification
const code =
"0x363d3d373d3d3d363d73bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3";
const implementationAddress =
extractMinimalProxyImplementationAddress(code);
expect(implementationAddress).to.equal(
"0xbebebebebebebebebebebebebebebebebebebebe",
);
});
it("should extract implementation address for minimal proxy with receive", async () => {
// https://github.com/0xSplits/splits-contracts/blob/c7b741926ec9746182d0d1e2c4c2046102e5d337/contracts/libraries/Clones.sol#L32
const code =
"0x36603057343d52307f830d2d700a97af574b186c80d40429385d24241565b08a7c559ba283a964d9b160203da23d3df35b3d3d3d3d363d3d37363d73bebebebebebebebebebebebebebebebebebebebe5af43d3d93803e605b57fd5bf3";
const implementationAddress =
extractMinimalProxyImplementationAddress(code);
expect(implementationAddress).to.equal(
"0xbebebebebebebebebebebebebebebebebebebebe",
);
});
it("should extract implementation address for more minimal proxy", async () => {
// https://medium.com/coinmonks/the-more-minimal-proxy-5756ae08ee48
const code =
"0x3d3d3d3d363d3d37363d73bebebebebebebebebebebebebebebebebebebebe5af43d3d93803e602a57fd5bf3";
const implementationAddress =
extractMinimalProxyImplementationAddress(code);
expect(implementationAddress).to.equal(
"0xbebebebebebebebebebebebebebebebebebebebe",
);
});
it("should extract implementation address for older vyper (uniswap v1) proxy", async () => {
const code =
"0x366000600037611000600036600073bebebebebebebebebebebebebebebebebebebebe5af41558576110006000f3";
const implementationAddress =
extractMinimalProxyImplementationAddress(code);
expect(implementationAddress).to.equal(
"0xbebebebebebebebebebebebebebebebebebebebe",
);
});
});
+37 -12
View File
@@ -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, [
+4
View File
@@ -0,0 +1,4 @@
# folders
dist/
# files
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["thirdweb"],
};
+23
View File
@@ -0,0 +1,23 @@
# @thirdweb-dev/unity-js-bridge
## 0.1.1
### Patch Changes
- [#433](https://github.com/thirdweb-dev/js/pull/433) [`7269268`](https://github.com/thirdweb-dev/js/commit/72692689e9adb41e92038765fd01a36c2b7457e1) Thanks [@jnsdls](https://github.com/jnsdls)! - add fundWallet method to bridge
- Updated dependencies [[`efc56fa`](https://github.com/thirdweb-dev/js/commit/efc56fa5802490ac8ef50037658d046afd89e9a1), [`7269268`](https://github.com/thirdweb-dev/js/commit/72692689e9adb41e92038765fd01a36c2b7457e1)]:
- @thirdweb-dev/sdk@3.6.5
- @thirdweb-dev/pay@0.1.0
## 0.1.0
### Minor Changes
- [#420](https://github.com/thirdweb-dev/js/pull/420) [`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45) Thanks [@jnsdls](https://github.com/jnsdls)! - Initial Release
### Patch Changes
- Updated dependencies [[`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45), [`f451da6`](https://github.com/thirdweb-dev/js/commit/f451da6395689a5f89800ee63f34b6175b61f703), [`af3acc6`](https://github.com/thirdweb-dev/js/commit/af3acc6b10751b840e56aef6400da5eea6040df2), [`1bfb91d`](https://github.com/thirdweb-dev/js/commit/1bfb91dbf3a39160c987a425813ac5dbb84703ad), [`ee47840`](https://github.com/thirdweb-dev/js/commit/ee478407673b0416e0c1cfe2be11bd6963395348), [`def4251`](https://github.com/thirdweb-dev/js/commit/def42511ff5a20d83f9094164dafb87e412571b5), [`034a257`](https://github.com/thirdweb-dev/js/commit/034a257442314c67729f7bafcbe3740cff33fa32), [`eca4776`](https://github.com/thirdweb-dev/js/commit/eca47763cd89cc3b7aa57b542971837987540b55), [`639e535`](https://github.com/thirdweb-dev/js/commit/639e535ed55280ad9d081001aab3f5af72bb3e45)]:
- @thirdweb-dev/wallets@0.1.0
- @thirdweb-dev/sdk@3.6.4
+16
View File
@@ -0,0 +1,16 @@
<p align="center">
<br />
<a href="https://thirdweb.com"><img src="https://github.com/thirdweb-dev/js/blob/main/packages/sdk/logo.svg?raw=true" width="200" alt=""/></a>
<br />
</p>
<h1 align="center">thirdweb Unity JS bridge</h1>
<p align="center">
<!-- <a href="https://www.npmjs.com/package/@thirdweb-dev/sdk"><img src="https://img.shields.io/npm/v/@thirdweb-dev/sdk?color=red&label=npm&logo=npm" alt="npm version"/></a> -->
<!-- <a href="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml"><img alt="Build Status" src="https://github.com/thirdweb-dev/js/actions/workflows/CI.yml/badge.svg"/></a> -->
<!-- <a href="https://discord.gg/thirdweb"><img alt="Join our Discord!" src="https://img.shields.io/discord/834227967404146718.svg?color=7289da&label=discord&logo=discord&style=flat"/></a> -->
</p>
<!-- <p align="center"><strong>Best in class Web3 SDK for Browser, Node and Mobile apps</strong></p> -->
<br />
### todo
+34
View File
@@ -0,0 +1,34 @@
{
"name": "@thirdweb-dev/unity-js-bridge",
"version": "0.1.1",
"main": "dist/thirdweb-unity-bridge.js",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
"author": "thirdweb eng <[email protected]>",
"files": [
"dist/"
],
"dependencies": {
"@thirdweb-dev/sdk": "*",
"@thirdweb-dev/storage": "*",
"@thirdweb-dev/wallets": "*",
"@thirdweb-dev/pay": "*",
"ethers": "^5.7.2"
},
"devDependencies": {
"esbuild": "^0.15.16",
"node-stdlib-browser": "^1.2.0",
"typescript": "^4.7.4"
},
"scripts": {
"format": "prettier --write 'src/**/*'",
"lint": "eslint src/",
"fix": "eslint src/ --fix",
"clean": "rm -rf dist/",
"build": "yarn clean && tsc && node scripts/build.js",
"push": "yalc push"
}
}
+28
View File
@@ -0,0 +1,28 @@
const esbuild = require("esbuild");
const plugin = require("node-stdlib-browser/helpers/esbuild/plugin");
const stdLibBrowser = require("node-stdlib-browser");
// const DIST_PATH = path.resolve(process.cwd(), "dist/");
// fs.rmSync(DIST_PATH, { recursive: true, force: true });
esbuild.build({
jsx: "automatic",
entryPoints: ["src/thirdweb-bridge.ts"],
bundle: true,
minify: true,
platform: "browser",
target: "es2020",
outfile: "dist/thirdweb-unity-bridge.js",
splitting: false,
write: true,
sourcemap: false,
inject: [require.resolve("node-stdlib-browser/helpers/esbuild/shim")],
define: {
process: JSON.stringify({
env: "production",
}),
Buffer: "Buffer",
},
plugins: [plugin(stdLibBrowser)],
});
@@ -0,0 +1,234 @@
/// --- Thirdweb Brige ---
import { CoinbasePayIntegration, FundWalletOptions } from "@thirdweb-dev/pay";
import { ChainOrRpc, ThirdwebSDK } from "@thirdweb-dev/sdk/evm";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import {
CoinbaseWallet,
MetaMask,
WalletConnect,
InjectedWallet,
} from "@thirdweb-dev/wallets";
import type { AbstractWallet } from "@thirdweb-dev/wallets/dist/declarations/src/wallets/base";
import { BigNumber } from "ethers";
import type { ContractInterface, Signer } from "ethers";
declare global {
interface Window {
bridge: TWBridge;
}
}
const SEPARATOR = "/";
const SUB_SEPARATOR = "#";
// big number transform
const bigNumberReplacer = (_key: string, value: any) => {
// if we find a BigNumber then make it into a string (since that is safe)
if (
BigNumber.isBigNumber(value) ||
(typeof value === "object" &&
value !== null &&
value.type === "BigNumber" &&
"hex" in value)
) {
return BigNumber.from(value).toString();
}
return value;
};
const WALLETS = [
MetaMask,
InjectedWallet,
WalletConnect,
CoinbaseWallet,
] as const;
type PossibleWallet = typeof WALLETS[number]["id"];
type FundWalletInput = FundWalletOptions & {
appId: string;
};
interface TWBridge {
initialize: (chain: ChainOrRpc, options: string) => void;
connect: (wallet: PossibleWallet, chainId?: number) => Promise<string>;
disconnect: () => Promise<void>;
switchNetwork: (chainId: number) => Promise<void>;
invoke: (route: string, payload: string) => Promise<string | undefined>;
fundWallet: (options: string) => Promise<void>;
}
const w = window;
class ThirdwebBridge implements TWBridge {
private walletMap: Map<string, AbstractWallet> = new Map();
private activeWallet: AbstractWallet | undefined;
private initializedChain: ChainOrRpc | undefined;
private activeSDK: ThirdwebSDK | undefined;
private updateSDKSigner(signer?: Signer) {
if (this.activeSDK) {
if (signer) {
// set signer if we got one
this.activeSDK.updateSignerOrProvider(signer);
} else if (this.initializedChain) {
// reset back to provider only in case signer gets reomved (disconnect case)
this.activeSDK.updateSignerOrProvider(this.initializedChain);
}
}
}
public initialize(chain: ChainOrRpc, options: string) {
this.initializedChain = chain;
console.debug("thirdwebSDK initialization:", chain, options);
const sdkOptions = JSON.parse(options);
const storage =
sdkOptions && sdkOptions.ipfsGatewayUrl
? new ThirdwebStorage({
gatewayUrls: {
"ipfs://": [sdkOptions.ipfsGatewayUrl],
},
})
: new ThirdwebStorage();
this.activeSDK = new ThirdwebSDK(chain, sdkOptions, storage);
for (let wallet of WALLETS) {
const walletInstance = new wallet({
appName: sdkOptions.appName || "thirdweb powered dApp",
});
walletInstance.on("connect", async () =>
this.updateSDKSigner(await walletInstance.getSigner()),
);
walletInstance.on("change", async () =>
this.updateSDKSigner(await walletInstance.getSigner()),
);
walletInstance.on("disconnect", () => this.updateSDKSigner());
this.walletMap.set(wallet.id, walletInstance);
}
}
public async connect(
wallet: PossibleWallet = "injected",
chainId?: number | undefined,
) {
if (!this.activeSDK) {
throw new Error("SDK not initialized");
}
if (chainId === 0) {
chainId = undefined;
}
const walletInstance = this.walletMap.get(wallet);
if (walletInstance) {
await walletInstance.connect(chainId);
this.activeWallet = walletInstance;
this.updateSDKSigner(await walletInstance.getSigner());
return await this.activeSDK.wallet.getAddress();
} else {
throw new Error("Invalid Wallet");
}
}
public async disconnect() {
if (this.activeWallet) {
await this.activeWallet.disconnect();
this.activeWallet = undefined;
this.updateSDKSigner();
}
}
public async switchNetwork(chainId: number) {
if (chainId && this.activeWallet && "switchChain" in this.activeWallet) {
await this.activeWallet.switchChain(chainId);
this.updateSDKSigner(await this.activeWallet.getSigner());
} else {
throw new Error("Error Switching Network");
}
}
public async invoke(route: string, payload: string) {
if (!this.activeSDK) {
throw new Error("SDK not initialized");
}
const routeArgs = route.split(SEPARATOR);
const firstArg = routeArgs[0].split(SUB_SEPARATOR);
const addrOrSDK = firstArg[0];
const fnArgs = JSON.parse(payload).arguments;
const parsedArgs = fnArgs.map((arg: unknown) => {
try {
return typeof arg === "string" &&
(arg.startsWith("{") || arg.startsWith("["))
? JSON.parse(arg)
: arg;
} catch (e) {
return arg;
}
});
console.debug("thirdwebSDK call:", route, parsedArgs);
// wallet call
if (addrOrSDK.startsWith("sdk")) {
let prop = undefined;
if (firstArg.length > 1) {
prop = firstArg[1];
}
if (prop && routeArgs.length === 2) {
// @ts-expect-error need to type-guard this properly
const result = await this.activeSDK[prop][routeArgs[1]](...parsedArgs);
return JSON.stringify({ result: result }, bigNumberReplacer);
} else if (routeArgs.length === 2) {
// @ts-expect-error need to type-guard this properly
const result = await this.activeSDK[routeArgs[1]](...parsedArgs);
return JSON.stringify({ result: result }, bigNumberReplacer);
} else {
throw new Error("Invalid Route");
}
}
// contract call
if (addrOrSDK.startsWith("0x")) {
let typeOrAbi: string | ContractInterface | undefined;
if (firstArg.length > 1) {
try {
typeOrAbi = JSON.parse(firstArg[1]); // try to parse ABI
} catch (e) {
typeOrAbi = firstArg[1];
}
}
const contract = typeOrAbi
? await this.activeSDK.getContract(addrOrSDK, typeOrAbi)
: await this.activeSDK.getContract(addrOrSDK);
if (routeArgs.length === 2) {
// @ts-expect-error need to type-guard this properly
const result = await contract[routeArgs[1]](...parsedArgs);
return JSON.stringify({ result: result }, bigNumberReplacer);
} else if (routeArgs.length === 3) {
// @ts-expect-error need to type-guard this properly
const result = await contract[routeArgs[1]][routeArgs[2]](
...parsedArgs,
);
return JSON.stringify({ result: result }, bigNumberReplacer);
} else if (routeArgs.length === 4) {
// @ts-expect-error need to type-guard this properly
const result = await contract[routeArgs[1]][routeArgs[2]][routeArgs[3]](
...parsedArgs,
);
return JSON.stringify({ result: result }, bigNumberReplacer);
} else {
throw new Error("Invalid Route");
}
}
}
public async fundWallet(options: string) {
if (!this.activeSDK) {
throw new Error("SDK not initialized");
}
const { appId, ...fundOptions } = JSON.parse(options) as FundWalletInput;
const cbPay = new CoinbasePayIntegration({ appId });
return await cbPay.fundWallet(fundOptions);
}
}
// add the bridge to the window object type
w.bridge = new ThirdwebBridge();
/// --- End Thirdweb Brige ---
+5
View File
@@ -0,0 +1,5 @@
{
"extends": "@thirdweb-dev/tsconfig/base.json",
"include": ["src", "types"],
"exclude": ["dist", "build", "node_modules"]
}
+6
View File
@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-typescript",
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
]
}
+4
View File
@@ -0,0 +1,4 @@
# folders
dist/
# files
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["thirdweb"],
};

Some files were not shown because too many files have changed in this diff Show More