Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96d2dfd25d | ||
|
|
5862c558fb | ||
|
|
e913e0daa9 | ||
|
|
f7a74a3b7c | ||
|
|
73883f56d0 | ||
|
|
e47085af92 | ||
|
|
f525e2cc23 | ||
|
|
af85389dc0 | ||
|
|
ccb4ce8d00 | ||
|
|
4cdd0bd634 | ||
|
|
ea95c5f609 | ||
|
|
699a2b16fb | ||
|
|
a8267f912d | ||
|
|
c673e39f23 | ||
|
|
08d04fe4dc | ||
|
|
cbf5508bd5 | ||
|
|
3731459d33 | ||
|
|
64f24f1bb1 | ||
|
|
cac6c30bca | ||
|
|
e7740a59db | ||
|
|
7a37e564fd | ||
|
|
e173636f4b | ||
|
|
8c6cdaa288 | ||
|
|
ad9b3febff | ||
|
|
94f49b1294 | ||
|
|
517e2a75e3 | ||
|
|
72692689e9 | ||
|
|
efc56fa580 |
@@ -13,7 +13,7 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
# timeout-minutes: 15
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-32
|
||||
# To use Remote Caching, uncomment the next lines and follow the steps below.
|
||||
env:
|
||||
|
||||
@@ -12,7 +12,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-32
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -13,8 +13,7 @@ concurrency:
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
# timeout-minutes: 15
|
||||
# try out buildjet to speed up build and test
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
# To use Remote Caching, uncomment the next lines and follow the steps below.
|
||||
env:
|
||||
|
||||
@@ -41,6 +41,7 @@ jobs:
|
||||
|
||||
deploy:
|
||||
runs-on: ubuntu-latest-32
|
||||
timeout-minutes: 30
|
||||
needs: deploy-check
|
||||
if: needs.deploy-check.outputs.triggered == 'true'
|
||||
steps:
|
||||
|
||||
@@ -14,6 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
timeout-minutes: 30
|
||||
runs-on: ubuntu-latest-32
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
|
||||
@@ -5,6 +5,7 @@ on: [pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest-32
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
"@changesets/cli": "^2.24.3",
|
||||
"@manypkg/cli": "^0.19.1",
|
||||
"@manypkg/get-packages": "^1.1.3",
|
||||
"@trivago/prettier-plugin-sort-imports": "^3.3.0",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
"eslint": "^8.21.0",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.39
|
||||
|
||||
## 2.0.38
|
||||
|
||||
## 2.0.37
|
||||
|
||||
## 2.0.36
|
||||
|
||||
## 2.0.35
|
||||
|
||||
## 2.0.34
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.34",
|
||||
"version": "2.0.39",
|
||||
"exports": {
|
||||
"./next": {
|
||||
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
|
||||
|
||||
@@ -1,5 +1,54 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.9.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada), [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358), [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e), [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b)]:
|
||||
- @thirdweb-dev/sdk@3.6.9
|
||||
|
||||
## 0.9.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#461](https://github.com/thirdweb-dev/js/pull/461) [`af85389`](https://github.com/thirdweb-dev/js/commit/af85389dc07fb163b8f9d70093eb1b691e47561a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix deprecated links
|
||||
|
||||
- Updated dependencies [[`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0), [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848), [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39), [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877), [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca)]:
|
||||
- @thirdweb-dev/sdk@3.6.8
|
||||
|
||||
## 0.9.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#441](https://github.com/thirdweb-dev/js/pull/441) [`64f24f1`](https://github.com/thirdweb-dev/js/commit/64f24f1bb167c3d0cb3beca51fd300eaec21cc27) Thanks [@adam-maj](https://github.com/adam-maj)! - CLI autoupdates on run
|
||||
|
||||
- [#447](https://github.com/thirdweb-dev/js/pull/447) [`e7740a5`](https://github.com/thirdweb-dev/js/commit/e7740a59db022a3396194ac66cd195d8ae754d57) Thanks [@adam-maj](https://github.com/adam-maj)! - Detect whether forge is installed
|
||||
|
||||
- Updated dependencies [[`3731459`](https://github.com/thirdweb-dev/js/commit/3731459d33f1ded7ebb69124809449b901b3ad3d), [`cac6c30`](https://github.com/thirdweb-dev/js/commit/cac6c30bca5e17df81d746ef81316af47d5e252e), [`7a37e56`](https://github.com/thirdweb-dev/js/commit/7a37e564fd5d5a9df84c8da44ecaf6c42f67a0e2)]:
|
||||
- @thirdweb-dev/sdk@3.6.7
|
||||
|
||||
## 0.9.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`8c6cdaa`](https://github.com/thirdweb-dev/js/commit/8c6cdaa2887fb2cc40d3ee6991233d195d103805)]:
|
||||
- @thirdweb-dev/sdk@3.6.6
|
||||
|
||||
## 0.9.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#444](https://github.com/thirdweb-dev/js/pull/444) [`94f49b1`](https://github.com/thirdweb-dev/js/commit/94f49b1294bc0208171b16ed413cbb44644af017) Thanks [@adam-maj](https://github.com/adam-maj)! - Add changeset
|
||||
|
||||
## 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
|
||||
|
||||
@@ -62,7 +62,7 @@ Deploying released contracts give deployers access to automatic SDKs to integrat
|
||||
npx thirdweb@latest detect
|
||||
```
|
||||
|
||||
As you're developing your contracts, you may want to implement [Extensions](https://portal.thirdweb.com/extensions) to unlock functionality on the SDKs (ie. nft minting with automatic upload to IPFS) and the dashboard (ie. generated UI to manage permissions). This command will show what extensions were detected on your contract, unlocking the corresponding functionality on the SDKs and dashboard.
|
||||
As you're developing your contracts, you may want to implement [Extensions](https://portal.thirdweb.com/contractkit) to unlock functionality on the SDKs (ie. nft minting with automatic upload to IPFS) and the dashboard (ie. generated UI to manage permissions). This command will show what extensions were detected on your contract, unlocking the corresponding functionality on the SDKs and dashboard.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.9.2",
|
||||
"version": "0.9.8",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -18,12 +18,12 @@
|
||||
"@types/prompts": "^2.0.14",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"@types/tar": "^6.1.2",
|
||||
"@types/update-notifier": "^5.1.0",
|
||||
"@types/update-notifier": "^6.0.1",
|
||||
"@types/validate-npm-package-name": "^4.0.0",
|
||||
"hardhat": "^2.9.3",
|
||||
"jest": "^28",
|
||||
"jest": "^29",
|
||||
"prettier": "^2.7.1",
|
||||
"ts-jest": "^28.0.8",
|
||||
"ts-jest": "^29.0.3",
|
||||
"ts-node": "^10.7.0",
|
||||
"tsup": "^6.2.3",
|
||||
"typescript": "^4.7.4"
|
||||
@@ -32,6 +32,7 @@
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"async-retry": "^1.3.3",
|
||||
"commander": "^9.1.0",
|
||||
"detect-package-manager": "^2.0.1",
|
||||
"enquirer": "^2.3.6",
|
||||
"ethers": "^5.7.2",
|
||||
"got": "11.8.5",
|
||||
|
||||
+148
-20
@@ -2,37 +2,37 @@
|
||||
import { detectExtensions } from "../common/feature-detector";
|
||||
import { processProject } from "../common/processor";
|
||||
import { cliVersion, pkg } from "../constants/urls";
|
||||
import { info, logger } from "../core/helpers/logger";
|
||||
import { info, logger, spinner } from "../core/helpers/logger";
|
||||
import { twCreate } from "../create/command";
|
||||
import { deploy } from "../deploy";
|
||||
import { findPackageInstallation } from "../helpers/detect-local-packages";
|
||||
import { upload } from "../storage/command";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import chalk from "chalk";
|
||||
import { exec, spawn } from "child_process";
|
||||
import { Command } from "commander";
|
||||
import open from "open";
|
||||
import prompts from "prompts";
|
||||
|
||||
const main = async () => {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
const skipIntro = process.env.THIRDWEB_CLI_SKIP_INTRO === "true";
|
||||
|
||||
const program = new Command();
|
||||
|
||||
//yes this has to look like this, eliminates whitespace
|
||||
console.info(`
|
||||
$$\\ $$\\ $$\\ $$\\ $$\\
|
||||
$$ | $$ | \\__| $$ | $$ |
|
||||
$$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\
|
||||
\\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\
|
||||
$$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
|
||||
$$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
|
||||
\\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ |
|
||||
\\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ `);
|
||||
console.info(`\n 💎 thirdweb-cli v${cliVersion} 💎\n`);
|
||||
import("update-notifier").then(({ default: updateNotifier }) => {
|
||||
updateNotifier({
|
||||
pkg,
|
||||
shouldNotifyInNpmScript: true,
|
||||
// check every time while we're still building the CLI
|
||||
updateCheckInterval: 0,
|
||||
}).notify();
|
||||
});
|
||||
// yes this has to look like this, eliminates whitespace
|
||||
if (!skipIntro) {
|
||||
console.info(`
|
||||
$$\\ $$\\ $$\\ $$\\ $$\\
|
||||
$$ | $$ | \\__| $$ | $$ |
|
||||
$$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$$$$\\
|
||||
\\_$$ _| $$ __$$\\ $$ |$$ __$$\\ $$ __$$ |$$ | $$ | $$ |$$ __$$\\ $$ __$$\\
|
||||
$$ | $$ | $$ |$$ |$$ | \\__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ |
|
||||
$$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
|
||||
\\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ |
|
||||
\\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ `);
|
||||
console.info(`\n 💎 thirdweb-cli v${cliVersion} 💎\n`);
|
||||
}
|
||||
|
||||
program
|
||||
.name("thirdweb-cli")
|
||||
@@ -198,6 +198,134 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
await detectExtensions(options);
|
||||
});
|
||||
|
||||
if (!skipIntro) {
|
||||
const versionSpinner = spinner("Checking for updates...");
|
||||
await import("update-notifier").then(
|
||||
async ({ default: updateNotifier }) => {
|
||||
const notifier = updateNotifier({
|
||||
pkg,
|
||||
shouldNotifyInNpmScript: true,
|
||||
// check every time while we're still building the CLI
|
||||
updateCheckInterval: 0,
|
||||
});
|
||||
|
||||
const versionInfo = await notifier.fetchInfo();
|
||||
versionSpinner.stop();
|
||||
|
||||
if (versionInfo.type !== "latest") {
|
||||
const res = await prompts({
|
||||
type: "toggle",
|
||||
name: "upgrade",
|
||||
message: `A new version of the CLI is available. Would you like to upgrade?`,
|
||||
initial: true,
|
||||
active: "yes",
|
||||
inactive: "no",
|
||||
});
|
||||
|
||||
if (res.upgrade) {
|
||||
const updateSpinner = spinner(
|
||||
`Upgrading CLI to version ${versionInfo.latest}...`,
|
||||
);
|
||||
|
||||
const clonedEnvironment = { ...process.env };
|
||||
clonedEnvironment.THIRDWEB_CLI_SKIP_INTRO = "true";
|
||||
|
||||
const installation = await findPackageInstallation();
|
||||
|
||||
// If the package isn't installed anywhere, just defer to npx thirdweb@latest
|
||||
if (!installation) {
|
||||
updateSpinner.succeed(
|
||||
`Now using CLI version ${versionInfo.latest}. Continuing execution...`,
|
||||
);
|
||||
|
||||
await new Promise((done, failed) => {
|
||||
const shell = spawn(
|
||||
`npx --yes thirdweb@latest ${process.argv
|
||||
.slice(2)
|
||||
.join(" ")}`,
|
||||
[],
|
||||
{ stdio: "inherit", shell: true, env: clonedEnvironment },
|
||||
);
|
||||
shell.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
done("");
|
||||
} else {
|
||||
failed();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return process.exit(0);
|
||||
}
|
||||
|
||||
// Otherwise, get the correct command based on package manager and local vs. global
|
||||
let command = "";
|
||||
switch (installation.packageManager) {
|
||||
case "npm":
|
||||
command = installation.isGlobal
|
||||
? `npm install -g thirdweb`
|
||||
: `npm install thirdweb`;
|
||||
break;
|
||||
case "yarn":
|
||||
command = installation.isGlobal
|
||||
? `yarn global add thirdweb`
|
||||
: `yarn add thirdweb`;
|
||||
break;
|
||||
case "pnpm":
|
||||
command = installation.isGlobal
|
||||
? `pnpm add -g thirdweb@latest`
|
||||
: `pnpm add thirdweb@latest`;
|
||||
break;
|
||||
default:
|
||||
console.error(
|
||||
`Could not detect package manager in use, aborting automatic upgrade.\nIf you want to upgrade the CLI, please do it manually with your package manager.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await new Promise((done, failed) => {
|
||||
exec(command, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
failed(err);
|
||||
return;
|
||||
}
|
||||
|
||||
done({ stdout, stderr });
|
||||
});
|
||||
});
|
||||
|
||||
updateSpinner.succeed(
|
||||
`Successfully upgraded CLI to version ${versionInfo.latest}. Continuing execution...`,
|
||||
);
|
||||
|
||||
// If the package is installed globally with yarn or pnpm, then npx won't recognize it
|
||||
// So we need to make sure to run the command directly
|
||||
const executionCommand =
|
||||
!installation.isGlobal || installation.packageManager === "npm"
|
||||
? `npx thirdweb`
|
||||
: `thirdweb`;
|
||||
await new Promise((done, failed) => {
|
||||
const shell = spawn(
|
||||
`${executionCommand} ${process.argv.slice(2).join(" ")}`,
|
||||
[],
|
||||
{ stdio: "inherit", shell: true, env: clonedEnvironment },
|
||||
);
|
||||
shell.on("close", (code) => {
|
||||
if (code === 0) {
|
||||
done("");
|
||||
} else {
|
||||
failed();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await program.parseAsync();
|
||||
};
|
||||
|
||||
|
||||
@@ -72,13 +72,13 @@ export async function detectExtensions(options: any) {
|
||||
const enabledFeatures: Feature[] = features.enabledFeatures.map(
|
||||
(feature) => ({
|
||||
name: feature.name,
|
||||
reference: `https://portal.thirdweb.com/extensions/${feature.name.toLowerCase()}`,
|
||||
reference: `https://portal.thirdweb.com/interfaces/${feature.name.toLowerCase()}`,
|
||||
}),
|
||||
);
|
||||
const suggestedFeatures: Feature[] = features.suggestedFeatures.map(
|
||||
(feature) => ({
|
||||
name: feature.name,
|
||||
reference: `https://portal.thirdweb.com/extensions/${feature.name.toLowerCase()}`,
|
||||
reference: `https://portal.thirdweb.com/interfaces/${feature.name.toLowerCase()}`,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { findFiles } from "../../common/file-helper";
|
||||
import { hasForge } from "../../create/helpers/has-forge";
|
||||
import { execute } from "../helpers/exec";
|
||||
import { logger } from "../helpers/logger";
|
||||
import { CompileOptions } from "../interfaces/Builder";
|
||||
import { ContractPayload } from "../interfaces/ContractPayload";
|
||||
import { BaseBuilder } from "./builder-base";
|
||||
import chalk from "chalk";
|
||||
import { existsSync, readFileSync } from "fs";
|
||||
import { basename, join } from "path";
|
||||
|
||||
@@ -11,6 +13,18 @@ export class FoundryBuilder extends BaseBuilder {
|
||||
public async compile(options: CompileOptions): Promise<{
|
||||
contracts: ContractPayload[];
|
||||
}> {
|
||||
const isInstalled = await hasForge();
|
||||
if (!isInstalled) {
|
||||
console.error(
|
||||
`\n${chalk.redBright(
|
||||
`error`,
|
||||
)} You don't have forge installed on this machine!\n\nYou can install forge by following these instructions:\n${chalk.blueBright(
|
||||
`https://book.getfoundry.sh/getting-started/installation`,
|
||||
)}\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
await execute("forge clean", options.projectPath);
|
||||
await execute("forge build --extra-output metadata", options.projectPath);
|
||||
|
||||
|
||||
@@ -259,16 +259,16 @@ export async function twCreate(
|
||||
framework = res.framework.trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!framework) {
|
||||
console.log("Please specify a framework");
|
||||
process.exit(1);
|
||||
}
|
||||
if (!framework) {
|
||||
console.log("Please specify a framework");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!language) {
|
||||
// Default = JavaScript
|
||||
language = "javascript";
|
||||
if (!language) {
|
||||
// Default = JavaScript
|
||||
language = "javascript";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { hasBaseContract, readBaseContract } from "./base-contracts";
|
||||
import { DownloadError } from "./create-app";
|
||||
import { PackageManager } from "./get-pkg-manager";
|
||||
import { tryGitInit } from "./git";
|
||||
import { hasForge } from "./has-forge";
|
||||
import { install } from "./install";
|
||||
import { isFolderEmpty } from "./is-folder-empty";
|
||||
import { getOnline } from "./is-online";
|
||||
@@ -218,5 +219,19 @@ export async function createContractProject({
|
||||
console.log();
|
||||
console.log(chalk.cyan(" cd"), cdpath);
|
||||
console.log();
|
||||
|
||||
if (framework === "forge") {
|
||||
const isInstalled = await hasForge();
|
||||
|
||||
if (!isInstalled) {
|
||||
console.log(
|
||||
`\n${chalk.yellowBright(
|
||||
`warning`,
|
||||
)} You don't have forge installed on this machine, and will need it to run this project! \n\nYou can install forge by following these instructions:\n${chalk.blueBright(
|
||||
`https://book.getfoundry.sh/getting-started/installation`,
|
||||
)}\n`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { exec } from "child_process";
|
||||
|
||||
export async function hasForge() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return await new Promise((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
exec("forge --version", (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
resolve(false);
|
||||
}
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { exec } from "child_process";
|
||||
|
||||
type PackageManager = "npm" | "yarn" | "pnpm";
|
||||
|
||||
interface Installation {
|
||||
packageManager: PackageManager;
|
||||
isGlobal: boolean;
|
||||
}
|
||||
|
||||
export async function findPackageInstallation(): Promise<
|
||||
Installation | undefined
|
||||
> {
|
||||
const isLocal = await isInstalledLocally();
|
||||
if (isLocal) {
|
||||
const packageManager = await import("detect-package-manager").then(
|
||||
({ detect }) => {
|
||||
return detect();
|
||||
},
|
||||
);
|
||||
|
||||
return { packageManager, isGlobal: false };
|
||||
}
|
||||
|
||||
const isGlobalNpm = await isInstalledGloballyWithNpm();
|
||||
if (isGlobalNpm) {
|
||||
return { packageManager: "npm", isGlobal: true };
|
||||
}
|
||||
|
||||
const isGlobalYarn = await isInstalledGloballyWithYarn();
|
||||
if (isGlobalYarn) {
|
||||
return { packageManager: "yarn", isGlobal: true };
|
||||
}
|
||||
|
||||
const isGlobalPnpm = await isInstalledGloballyWithPnpm();
|
||||
if (isGlobalPnpm) {
|
||||
return { packageManager: "pnpm", isGlobal: true };
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// For LOCAL packages, npm ls picks up things installed by yarn and pnpm too
|
||||
async function isInstalledLocally(): Promise<boolean> {
|
||||
const packages = await detectPackages("npm ls --depth=0");
|
||||
return containsThirdweb(packages || []);
|
||||
}
|
||||
|
||||
// But for GLOBAL packages, we need to use a separate command for each one
|
||||
async function isInstalledGloballyWithNpm(): Promise<boolean> {
|
||||
const packages = await detectPackages("npm ls -g --depth=0");
|
||||
return containsThirdweb(packages || []);
|
||||
}
|
||||
|
||||
async function isInstalledGloballyWithYarn(): Promise<boolean> {
|
||||
const packages = await detectPackages("yarn --cwd `yarn global dir` list");
|
||||
return containsThirdweb(packages || []);
|
||||
}
|
||||
|
||||
async function isInstalledGloballyWithPnpm(): Promise<boolean> {
|
||||
const packages: string[] = await new Promise((resolve) => {
|
||||
exec(`pnpm list -g --depth=0`, (err, stdout) => {
|
||||
var pkgs: string[] = [];
|
||||
pkgs = stdout.split("dependencies:")[1]?.trim().split("\n") || [];
|
||||
pkgs = pkgs.map((pkg) => pkg.replace(" ", "@"));
|
||||
resolve(pkgs);
|
||||
});
|
||||
});
|
||||
return containsThirdweb(packages || []);
|
||||
}
|
||||
|
||||
function containsThirdweb(packages: string[]) {
|
||||
return packages.some((pkg) => pkg.match(/^thirdweb/));
|
||||
}
|
||||
|
||||
async function detectPackages(cmd: string): Promise<string[]> {
|
||||
return new Promise((resolve) => {
|
||||
exec(cmd, (err, stdout) => {
|
||||
var packages: string[] = [];
|
||||
packages = stdout.split("\n");
|
||||
// We're one short on '-' to support both yarn and npm
|
||||
packages = packages.filter(function (item) {
|
||||
if (item.match(/^├─.+/g) !== null) {
|
||||
return true;
|
||||
}
|
||||
if (item.match(/^└─.+/g) !== null) {
|
||||
return true;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
packages = packages
|
||||
.map(function (item) {
|
||||
if (item.match(/^├─.+/g) !== null) {
|
||||
return item.replace(/(^├──\s|^├─\s)/g, "");
|
||||
}
|
||||
if (item.match(/^└─.+/g) !== null) {
|
||||
return item.replace(/(^└──\s|^├─\s)/g, "");
|
||||
}
|
||||
})
|
||||
.filter((item) => !!item) as string[];
|
||||
resolve(packages);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-typescript",
|
||||
["@babel/preset-env", { "targets": "defaults, not ie 11" }]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# folders
|
||||
dist/
|
||||
|
||||
# files
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ["thirdweb"],
|
||||
};
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "@thirdweb-dev/tsconfig/sdk.json",
|
||||
"include": ["src", "types"],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
@@ -1,5 +1,29 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.6.9
|
||||
|
||||
## 3.6.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#422](https://github.com/thirdweb-dev/js/pull/422) [`08d04fe`](https://github.com/thirdweb-dev/js/commit/08d04fe4dc74ba0d12625d7d4004c23f7dc330a2) Thanks [@yehia67](https://github.com/yehia67)! - Add `btnTitle` prop to the `ConnectWallet` component. Now you can set customized string or child component to the connect button.
|
||||
|
||||
## 3.6.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#448](https://github.com/thirdweb-dev/js/pull/448) [`7a37e56`](https://github.com/thirdweb-dev/js/commit/7a37e564fd5d5a9df84c8da44ecaf6c42f67a0e2) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - allow `useContract` to fail faster to enable the import case
|
||||
|
||||
## 3.6.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#443](https://github.com/thirdweb-dev/js/pull/443) [`8c6cdaa`](https://github.com/thirdweb-dev/js/commit/8c6cdaa2887fb2cc40d3ee6991233d195d103805) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix magic link connector not connecting to the right chain
|
||||
|
||||
## 3.6.5
|
||||
|
||||
## 3.6.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.6.4",
|
||||
"version": "3.6.9",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -97,19 +97,19 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@emotion/styled": "^11.10.0",
|
||||
"@gnosis.pm/safe-core-sdk": "^3.1.1",
|
||||
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.11",
|
||||
"@gnosis.pm/safe-ethers-lib": "^1.6.1",
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@safe-global/safe-core-sdk": "^3.2.0",
|
||||
"@safe-global/safe-ethers-adapters": "0.1.0-alpha.13",
|
||||
"@safe-global/safe-ethers-lib": "^1.7.0",
|
||||
"@solana/wallet-adapter-phantom": "^0.9.17",
|
||||
"@tanstack/react-query": "^4.0.10",
|
||||
"@zag-js/menu": "^0.1.16",
|
||||
"@zag-js/react": "^0.1.18",
|
||||
"@zag-js/menu": "^0.3.0",
|
||||
"@zag-js/react": "^0.3.1",
|
||||
"buffer": "^6.0.3",
|
||||
"color": "^4.2.3",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"detect-browser": "^5.3.0",
|
||||
"magic-sdk": "^8.1.1",
|
||||
"magic-sdk": "^10.1.0",
|
||||
"mime": "^3.0.0",
|
||||
"react-cool-dimensions": "^2.0.7",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
|
||||
@@ -63,6 +63,7 @@ interface ConnectWalletProps extends ThemeProviderProps {
|
||||
loginOptional?: boolean;
|
||||
};
|
||||
className?: string;
|
||||
btnTitle?: JSX.Element | string;
|
||||
}
|
||||
|
||||
let connecting = false;
|
||||
@@ -120,6 +121,7 @@ const chainIdToCurrencyMap: Record<
|
||||
export const ConnectWallet: React.FC<ConnectWalletProps> = ({
|
||||
auth,
|
||||
className,
|
||||
btnTitle,
|
||||
...themeProps
|
||||
}) => {
|
||||
const id = useId();
|
||||
@@ -248,7 +250,9 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
|
||||
</span>
|
||||
)
|
||||
) : (
|
||||
<span style={{ whiteSpace: "nowrap" }}>Connect Wallet</span>
|
||||
<span style={{ whiteSpace: "nowrap" }}>
|
||||
{btnTitle || "Connect Wallet"}
|
||||
</span>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -98,9 +98,9 @@ export class GnosisSafeConnector extends Connector {
|
||||
invariant(serverUrl, "Chain not supported");
|
||||
|
||||
const [safeEthersAdapters, safeCoreSdk, safeEthersLib] = await Promise.all([
|
||||
import("@gnosis.pm/safe-ethers-adapters"),
|
||||
import("@gnosis.pm/safe-core-sdk"),
|
||||
import("@gnosis.pm/safe-ethers-lib"),
|
||||
import("@safe-global/safe-ethers-adapters"),
|
||||
import("@safe-global/safe-core-sdk"),
|
||||
import("@safe-global/safe-ethers-lib"),
|
||||
]);
|
||||
|
||||
const ethAdapter = new safeEthersLib.default({
|
||||
|
||||
@@ -62,6 +62,18 @@ export class MagicConnector extends Connector {
|
||||
const { apiKey, ...options } = this.options;
|
||||
const configuration = this.getConfiguration();
|
||||
|
||||
const chains = Object.entries(this.options.rpcUrls);
|
||||
if (chains.length > 1) {
|
||||
console.warn(
|
||||
`Magic only supports connecting to one chain at a time. Using the first chain specified in rpcUrls: ${chains[0]}.`,
|
||||
);
|
||||
}
|
||||
const [chainId, rpcUrl] = chains[0];
|
||||
this.options.network = {
|
||||
chainId: parseInt(chainId),
|
||||
rpcUrl,
|
||||
};
|
||||
|
||||
try {
|
||||
invariant(
|
||||
configuration,
|
||||
|
||||
@@ -238,7 +238,7 @@ export function useContract(
|
||||
await queryClient.fetchQuery(
|
||||
compilerMetadata.cacheKey(contractAddress, activeChainId),
|
||||
() => compilerMetadata.fetchQuery(contractAddress, sdk),
|
||||
{ cacheTime: Infinity, staleTime: Infinity },
|
||||
{ cacheTime: Infinity, staleTime: Infinity, retry: 0 },
|
||||
)
|
||||
)?.abi;
|
||||
}
|
||||
@@ -258,6 +258,8 @@ export function useContract(
|
||||
cacheTime: Infinity,
|
||||
staleTime: Infinity,
|
||||
enabled: !!contractAddress && !!sdk && !!activeChainId,
|
||||
// never retry
|
||||
retry: 0,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,53 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.6.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#465](https://github.com/thirdweb-dev/js/pull/465) [`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada) Thanks [@adam-maj](https://github.com/adam-maj)! - Fix passing options to factory
|
||||
|
||||
- [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358) Thanks [@kumaryash90](https://github.com/kumaryash90)! - update forwarder address
|
||||
|
||||
- [#466](https://github.com/thirdweb-dev/js/pull/466) [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - workaround for signing typed data with magic.link signers
|
||||
|
||||
- [#468](https://github.com/thirdweb-dev/js/pull/468) [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose `sdk.wallet.signTypedData()`
|
||||
|
||||
## 3.6.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#421](https://github.com/thirdweb-dev/js/pull/421) [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848) Thanks [@yehia67](https://github.com/yehia67)! - Add new utilities to parse/format currencies. `toEther` & `toWei` as default 18 decimal and `toUnits` & `toDisplayValue` in case of different numbers of decimals.
|
||||
|
||||
- [#456](https://github.com/thirdweb-dev/js/pull/456) [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Fix claiming drops with custom currencies
|
||||
|
||||
- [#458](https://github.com/thirdweb-dev/js/pull/458) [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for latest wallet package integration
|
||||
|
||||
- [#453](https://github.com/thirdweb-dev/js/pull/453) [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Drop fixes for large batch uploads
|
||||
|
||||
## 3.6.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#451](https://github.com/thirdweb-dev/js/pull/451) [`3731459`](https://github.com/thirdweb-dev/js/commit/3731459d33f1ded7ebb69124809449b901b3ad3d) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Less strict prebuilt checks to support OSRF variants
|
||||
|
||||
- [#450](https://github.com/thirdweb-dev/js/pull/450) [`cac6c30`](https://github.com/thirdweb-dev/js/commit/cac6c30bca5e17df81d746ef81316af47d5e252e) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix setting allowance or approvals gaslessly
|
||||
|
||||
- [#448](https://github.com/thirdweb-dev/js/pull/448) [`7a37e56`](https://github.com/thirdweb-dev/js/commit/7a37e564fd5d5a9df84c8da44ecaf6c42f67a0e2) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - allow `useContract` to fail faster to enable the import case
|
||||
|
||||
## 3.6.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#443](https://github.com/thirdweb-dev/js/pull/443) [`8c6cdaa`](https://github.com/thirdweb-dev/js/commit/8c6cdaa2887fb2cc40d3ee6991233d195d103805) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix signature minting from OpenSea Royalty Filter NFT collection contracts
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.6.4",
|
||||
"version": "3.6.9",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
@@ -85,7 +85,7 @@
|
||||
"@types/deep-equal-in-any-order": "^1.0.1",
|
||||
"@types/mocha": "^10.0.0",
|
||||
"@types/node": "^18.0.0",
|
||||
"@types/uuid": "^8.3.3",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||
"@typescript-eslint/parser": "^5.33.0",
|
||||
"chai": "^4.3.6",
|
||||
@@ -146,4 +146,4 @@
|
||||
"url": "https://github.com/thirdweb-dev/js/issues"
|
||||
},
|
||||
"author": "thirdweb eng <[email protected]>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export const DEFAULT_API_KEY =
|
||||
"c6634ad2d97b74baf15ff556016830c251050e6c36b9da508ce3ec80095d3dc1";
|
||||
|
||||
export function getRpcUrl(network: string) {
|
||||
return `https://${network}.rpc.thirdweb.com/${DEFAULT_API_KEY}`;
|
||||
export function getRpcUrl(network: string, apikey = DEFAULT_API_KEY) {
|
||||
return `https://${network}.rpc.thirdweb.com/${apikey}`;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export async function setErc20Allowance(
|
||||
signer || provider,
|
||||
currencyAddress,
|
||||
ERC20Abi,
|
||||
{},
|
||||
contractToApprove.options,
|
||||
);
|
||||
|
||||
const owner = await contractToApprove.getSignerAddress();
|
||||
@@ -151,7 +151,7 @@ export async function approveErc20Allowance(
|
||||
signer || provider,
|
||||
currencyAddress,
|
||||
ERC20Abi,
|
||||
{},
|
||||
contractToApprove.options,
|
||||
);
|
||||
const owner = await contractToApprove.getSignerAddress();
|
||||
const spender = contractToApprove.readContract.address;
|
||||
@@ -192,3 +192,19 @@ export async function normalizeAmount(
|
||||
const decimals = await contractWrapper.readContract.decimals();
|
||||
return utils.parseUnits(AmountSchema.parse(amount), decimals);
|
||||
}
|
||||
|
||||
export function toEther(amount: BigNumberish): string {
|
||||
return utils.formatEther(amount);
|
||||
}
|
||||
|
||||
export function toWei(amount: Amount): BigNumber {
|
||||
return utils.parseEther(AmountSchema.parse(amount));
|
||||
}
|
||||
|
||||
export function toUnits(amount: Amount, decimals: BigNumberish): BigNumber {
|
||||
return utils.parseUnits(AmountSchema.parse(amount), decimals);
|
||||
}
|
||||
|
||||
export function toDisplayValue(amount: BigNumberish, decimals: BigNumberish): string {
|
||||
return utils.formatUnits(amount, decimals);
|
||||
}
|
||||
|
||||
@@ -442,6 +442,11 @@ export async function fetchContractMetadata(
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<PublishedMetadata> {
|
||||
const metadata = await storage.downloadJSON(compilerMetadataUri);
|
||||
if (!metadata || !metadata.output) {
|
||||
throw new Error(
|
||||
`Could not resolve metadata for contract at ${compilerMetadataUri}`,
|
||||
);
|
||||
}
|
||||
const abi = AbiSchema.parse(metadata.output.abi);
|
||||
const compilationTarget = metadata.settings.compilationTarget;
|
||||
const targets = Object.keys(compilationTarget);
|
||||
|
||||
@@ -3,7 +3,6 @@ import {
|
||||
InterfaceId_IERC1155,
|
||||
InterfaceId_IERC721,
|
||||
} from "../constants/contract";
|
||||
import { SignerOrProvider } from "../core";
|
||||
import { ContractWrapper } from "../core/classes/contract-wrapper";
|
||||
import {
|
||||
NewAuctionListing,
|
||||
@@ -76,33 +75,38 @@ export async function isTokenApprovedForTransfer(
|
||||
/**
|
||||
* Checks if the marketplace is approved to make transfers on the assetContract
|
||||
* If not, it tries to set the approval.
|
||||
* @param signerOrProvider
|
||||
* @param contractWrapper
|
||||
* @param marketplaceAddress
|
||||
* @param assetContract
|
||||
* @param tokenId
|
||||
* @param from
|
||||
*/
|
||||
export async function handleTokenApproval(
|
||||
signerOrProvider: SignerOrProvider,
|
||||
contractWrapper: ContractWrapper<any>,
|
||||
marketplaceAddress: string,
|
||||
assetContract: string,
|
||||
tokenId: BigNumberish,
|
||||
from: string,
|
||||
): Promise<void> {
|
||||
const erc165 = new Contract(
|
||||
const erc165 = new ContractWrapper<IERC165>(
|
||||
contractWrapper.getSignerOrProvider(),
|
||||
assetContract,
|
||||
ERC165Abi,
|
||||
signerOrProvider,
|
||||
) as IERC165;
|
||||
const isERC721 = await erc165.supportsInterface(InterfaceId_IERC721);
|
||||
const isERC1155 = await erc165.supportsInterface(InterfaceId_IERC1155);
|
||||
contractWrapper.options,
|
||||
);
|
||||
const isERC721 = await erc165.readContract.supportsInterface(
|
||||
InterfaceId_IERC721,
|
||||
);
|
||||
const isERC1155 = await erc165.readContract.supportsInterface(
|
||||
InterfaceId_IERC1155,
|
||||
);
|
||||
// check for token approval
|
||||
if (isERC721) {
|
||||
const asset = new ContractWrapper<IERC721>(
|
||||
signerOrProvider,
|
||||
contractWrapper.getSignerOrProvider(),
|
||||
assetContract,
|
||||
ERC721Abi,
|
||||
{},
|
||||
contractWrapper.options,
|
||||
);
|
||||
const approved = await asset.readContract.isApprovedForAll(
|
||||
from,
|
||||
@@ -122,10 +126,10 @@ export async function handleTokenApproval(
|
||||
}
|
||||
} else if (isERC1155) {
|
||||
const asset = new ContractWrapper<IERC1155>(
|
||||
signerOrProvider,
|
||||
contractWrapper.getSignerOrProvider(),
|
||||
assetContract,
|
||||
ERC1155Abi,
|
||||
{},
|
||||
contractWrapper.options,
|
||||
);
|
||||
|
||||
const approved = await asset.readContract.isApprovedForAll(
|
||||
|
||||
@@ -49,6 +49,7 @@ export async function signTypedDataInternal(
|
||||
);
|
||||
|
||||
let signature = "";
|
||||
const signerAddress = (await signer.getAddress()).toLowerCase();
|
||||
|
||||
// an indirect way for accessing walletconnect's underlying provider
|
||||
if ((provider as any)?.provider?.isWalletConnect) {
|
||||
@@ -66,11 +67,19 @@ export async function signTypedDataInternal(
|
||||
} catch (err: any) {
|
||||
if (err?.message?.includes("Method eth_signTypedData_v4 not supported")) {
|
||||
signature = await provider.send("eth_signTypedData", [
|
||||
(await signer.getAddress()).toLowerCase(),
|
||||
signerAddress,
|
||||
JSON.stringify(payload),
|
||||
]);
|
||||
} else {
|
||||
throw err;
|
||||
// magic.link signer only supports this way
|
||||
try {
|
||||
await provider.send("eth_signTypedData_v4", [
|
||||
signerAddress,
|
||||
JSON.stringify(payload),
|
||||
]);
|
||||
} catch (finalErr: any) {
|
||||
throw finalErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ export const CONTRACT_ADDRESSES: Record<
|
||||
twBYOCRegistry: "0x3E6eE864f850F5e5A98bc950B68E181Cf4010F23",
|
||||
},
|
||||
[ChainId.Arbitrum]: {
|
||||
openzeppelinForwarder: OZ_DEFENDER_FORWARDER_ADDRESS,
|
||||
openzeppelinForwarder: "0x8cbc8B5d71702032904750A66AEfE8B603eBC538",
|
||||
openzeppelinForwarderEOA: "0x4f247c69184ad61036EC2Bb3213b69F10FbEDe1F",
|
||||
biconomyForwarder: "0xfe0fa3C06d03bDC7fb49c892BbB39113B534fB57",
|
||||
twFactory: "0xd24b3de085CFd8c54b94feAD08a7962D343E6DE0",
|
||||
@@ -109,7 +109,7 @@ export const CONTRACT_ADDRESSES: Record<
|
||||
twBYOCRegistry: constants.AddressZero,
|
||||
},
|
||||
[ChainId.Optimism]: {
|
||||
openzeppelinForwarder: OZ_DEFENDER_FORWARDER_ADDRESS,
|
||||
openzeppelinForwarder: "0x8cbc8B5d71702032904750A66AEfE8B603eBC538",
|
||||
openzeppelinForwarderEOA: "0x7e80648EB2071E26937F9D42A513ccf4815fc702",
|
||||
biconomyForwarder: "0xefba8a2a82ec1fb1273806174f5e28fbb917cf95",
|
||||
twFactory: "0xd24b3de085CFd8c54b94feAD08a7962D343E6DE0",
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import { getPrebuiltInfo } from "../common/legacy";
|
||||
import { ALL_ROLES } from "../common/role";
|
||||
import { getSignerAndProvider } from "../core/classes/rpc-connection-handler";
|
||||
@@ -56,12 +57,8 @@ export const EditionDropInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
const contractInfo = await assertContractType(
|
||||
address,
|
||||
provider,
|
||||
"edition-drop",
|
||||
);
|
||||
return contractInfo.version > 2
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 2
|
||||
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC1155.json"))
|
||||
.default
|
||||
: (
|
||||
@@ -97,7 +94,6 @@ export const EditionInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "edition");
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC1155.json")
|
||||
).default;
|
||||
@@ -129,7 +125,6 @@ export const MarketplaceInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "marketplace");
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/Marketplace.json")
|
||||
).default;
|
||||
@@ -161,7 +156,6 @@ export const MultiwrapInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "multiwrap");
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/Multiwrap.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -193,7 +187,6 @@ export const NFTCollectionInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "nft-collection");
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC721.json")
|
||||
).default;
|
||||
@@ -225,12 +218,8 @@ export const NFTDropInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
const contractInfo = await assertContractType(
|
||||
address,
|
||||
provider,
|
||||
"nft-drop",
|
||||
);
|
||||
return contractInfo.version > 3
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 3
|
||||
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC721.json"))
|
||||
.default
|
||||
: (
|
||||
@@ -267,7 +256,6 @@ export const PackInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "pack");
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/Pack.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -299,12 +287,8 @@ export const SignatureDropInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
const contractInfo = await assertContractType(
|
||||
address,
|
||||
provider,
|
||||
"signature-drop",
|
||||
);
|
||||
return contractInfo.version > 4
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 4
|
||||
? (
|
||||
await import(
|
||||
"@thirdweb-dev/contracts-js/dist/abis/SignatureDrop.json"
|
||||
@@ -344,7 +328,6 @@ export const SplitInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "split");
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/Split.json"))
|
||||
.default;
|
||||
},
|
||||
@@ -376,12 +359,8 @@ export const TokenDropInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
const contractInfo = await assertContractType(
|
||||
address,
|
||||
provider,
|
||||
"token-drop",
|
||||
);
|
||||
return contractInfo.version > 2
|
||||
const contractInfo = await getContractInfo(address, provider);
|
||||
return !contractInfo || contractInfo.version > 2
|
||||
? (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20.json"))
|
||||
.default
|
||||
: (await import("@thirdweb-dev/contracts-js/dist/abis/DropERC20_V2.json"))
|
||||
@@ -414,7 +393,6 @@ export const TokenInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "token");
|
||||
return (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/TokenERC20.json")
|
||||
).default;
|
||||
@@ -447,22 +425,20 @@ export const VoteInitializer = {
|
||||
);
|
||||
},
|
||||
getAbi: async (address: string, provider: ethers.providers.Provider) => {
|
||||
await assertContractType(address, provider, "vote");
|
||||
return (await import("@thirdweb-dev/contracts-js/dist/abis/VoteERC20.json"))
|
||||
.default;
|
||||
},
|
||||
};
|
||||
|
||||
async function assertContractType(
|
||||
async function getContractInfo(
|
||||
address: string,
|
||||
provider: ethers.providers.Provider,
|
||||
type: PrebuiltContractType,
|
||||
) {
|
||||
const contractInfo = await getPrebuiltInfo(address, provider);
|
||||
if (!contractInfo || contractInfo.type !== getContractName(type)) {
|
||||
throw new Error(`Contract is not a ${type}`);
|
||||
try {
|
||||
return await getPrebuiltInfo(address, provider);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
return contractInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -515,7 +515,7 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
factoryAddress,
|
||||
this.getSignerOrProvider(),
|
||||
this.storage,
|
||||
{},
|
||||
this.options,
|
||||
);
|
||||
return await proxyFactory.deployProxyByImplementation(
|
||||
implementationAddress,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hasFunction } from "../../common";
|
||||
import { detectContractFeature } from "../../common";
|
||||
import { normalizePriceValue, setErc20Allowance } from "../../common/currency";
|
||||
import { uploadOrExtractURIs } from "../../common/nft";
|
||||
import { FEATURE_NFT_SIGNATURE_MINTABLE_V2 } from "../../constants/erc721-features";
|
||||
@@ -22,7 +22,7 @@ import type {
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { TokensMintedWithSignatureEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/SignatureDrop";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BigNumber, ethers } from "ethers";
|
||||
import { BigNumber } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
@@ -397,17 +397,9 @@ export class Erc721WithQuantitySignatureMintable implements DetectableFeature {
|
||||
}
|
||||
|
||||
private async isLegacyNFTContract() {
|
||||
if (hasFunction<TokenERC721>("contractType", this.contractWrapper)) {
|
||||
try {
|
||||
const contractType = ethers.utils.toUtf8String(
|
||||
await this.contractWrapper.readContract.contractType(),
|
||||
);
|
||||
return contractType.includes("TokenERC721");
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return detectContractFeature<ISignatureMintERC721>(
|
||||
this.contractWrapper,
|
||||
"ERC721SignatureMintV1",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ export class MarketplaceAuction {
|
||||
validateNewListingParam(listing);
|
||||
|
||||
await handleTokenApproval(
|
||||
this.contractWrapper.getSignerOrProvider(),
|
||||
this.contractWrapper,
|
||||
this.getAddress(),
|
||||
listing.assetContractAddress,
|
||||
listing.tokenId,
|
||||
|
||||
@@ -162,7 +162,7 @@ export class MarketplaceDirect {
|
||||
validateNewListingParam(listing);
|
||||
|
||||
await handleTokenApproval(
|
||||
this.contractWrapper.getSignerOrProvider(),
|
||||
this.contractWrapper,
|
||||
this.getAddress(),
|
||||
listing.assetContractAddress,
|
||||
listing.tokenId,
|
||||
|
||||
@@ -14,7 +14,7 @@ import EventEmitter from "eventemitter3";
|
||||
export class RPCConnectionHandler extends EventEmitter {
|
||||
private provider: providers.Provider;
|
||||
private signer: Signer | undefined;
|
||||
protected readonly options: SDKOptionsOutput;
|
||||
public readonly options: SDKOptionsOutput;
|
||||
|
||||
constructor(network: NetworkOrSignerOrProvider, options: SDKOptions) {
|
||||
super();
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
isNativeToken,
|
||||
normalizePriceValue,
|
||||
} from "../../common/currency";
|
||||
import { EIP712Domain, signTypedDataInternal } from "../../common/sign";
|
||||
import { NATIVE_TOKEN_ADDRESS } from "../../constants";
|
||||
import { SDKOptions } from "../../schema";
|
||||
import { Amount, CurrencyValue } from "../../types";
|
||||
@@ -11,7 +12,7 @@ import { RPCConnectionHandler } from "../classes/rpc-connection-handler";
|
||||
import { NetworkOrSignerOrProvider, TransactionResult } from "../types";
|
||||
import type { IERC20 } from "@thirdweb-dev/contracts-js";
|
||||
import ERC20Abi from "@thirdweb-dev/contracts-js/dist/abis/IERC20.json";
|
||||
import { ethers, BigNumber, providers, Signer } from "ethers";
|
||||
import { ethers, BigNumber, providers, Signer, TypedDataField } from "ethers";
|
||||
import EventEmitter from "eventemitter3";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -183,6 +184,42 @@ export class UserWallet {
|
||||
return await signer.signMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign a typed data structure (EIP712) with the connected wallet private key
|
||||
* @param domain - the domain as EIP712 standard
|
||||
* @param types - the strcuture and data types as defined by the EIP712 standard
|
||||
* @param message - the data to sign
|
||||
* @returns the payload and its associated signature
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* // This is the message to be signed
|
||||
* // Now we can sign the message with the connected wallet
|
||||
* const { payload, signature } = await sdk.wallet.signTypedData(
|
||||
* {
|
||||
name: "MyEIP721Domain",
|
||||
version: "1",
|
||||
chainId: 1,
|
||||
verifyingContract: "0x...",
|
||||
},
|
||||
{ MyStruct: [ { name: "to", type: "address" }, { name: "quantity", type: "uint256" } ] },
|
||||
{ to: "0x...", quantity: 1 },
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
public async signTypedData(
|
||||
domain: EIP712Domain,
|
||||
types: Record<string, Array<TypedDataField>>,
|
||||
message: Record<string, any>,
|
||||
): Promise<{ payload: any; signature: string }> {
|
||||
return await signTypedDataInternal(
|
||||
this.requireWallet(),
|
||||
domain,
|
||||
types,
|
||||
message,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recover the signing address from a signed message
|
||||
* @param message - the original message that was signed
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export type {
|
||||
} from "./core/schema/nft";
|
||||
|
||||
export type { CurrencyValue, TokenMetadata } from "./core/schema/token";
|
||||
export { getRpcUrl } from "./core/constants/urls";
|
||||
|
||||
export type { QueryAllParams } from "./core/schema/QueryParams";
|
||||
|
||||
|
||||
@@ -64,9 +64,7 @@ export class ClaimConditions {
|
||||
const goLiveDate = candyMachine.goLiveDate
|
||||
? new Date(candyMachine.goLiveDate.toNumber() * 1000)
|
||||
: null;
|
||||
const isWithinGoLiveDate = candyMachine.goLiveDate
|
||||
? candyMachine.goLiveDate.toNumber() * 1000 >= Date.now()
|
||||
: true;
|
||||
const isWithinGoLiveDate = goLiveDate ? goLiveDate <= new Date() : true;
|
||||
const maxClaimable =
|
||||
candyMachine.endSettings && candyMachine.endSettings.endSettingType === 1
|
||||
? candyMachine.endSettings.number.toNumber()
|
||||
@@ -155,7 +153,7 @@ export class ClaimConditions {
|
||||
const parsed = NFTDropUpdateableConditionsInputSchema.parse(metadata);
|
||||
|
||||
// recipients
|
||||
const wallet: PublicKey | undefined = parsed.primarySaleRecipient
|
||||
let wallet: PublicKey | undefined = parsed.primarySaleRecipient
|
||||
? new PublicKey(parsed.primarySaleRecipient)
|
||||
: undefined;
|
||||
const sellerFeeBasisPoints = parsed.sellerFeeBasisPoints;
|
||||
@@ -165,12 +163,19 @@ export class ClaimConditions {
|
||||
? sol(Number(parsed.price))
|
||||
: undefined;
|
||||
let tokenMint: PublicKey | undefined = undefined;
|
||||
if (parsed.currencyAddress && parsed.price) {
|
||||
if (parsed.currencyAddress) {
|
||||
const fetchedToken = await this.metaplex
|
||||
.tokens()
|
||||
.findMintByAddress({ address: new PublicKey(parsed.currencyAddress) });
|
||||
price = token(Number(parsed.price), fetchedToken.decimals);
|
||||
price = token(Number(parsed.price || 0), fetchedToken.decimals);
|
||||
tokenMint = fetchedToken.address;
|
||||
wallet = this.metaplex
|
||||
.tokens()
|
||||
.pdas()
|
||||
.associatedTokenAccount({
|
||||
mint: tokenMint,
|
||||
owner: wallet ? wallet : this.metaplex.identity().publicKey,
|
||||
});
|
||||
}
|
||||
|
||||
// dates
|
||||
|
||||
@@ -440,9 +440,9 @@ export class NFTDrop {
|
||||
* console.log("Claimed NFT at address", claimedAddresses[0]);
|
||||
* ```
|
||||
*/
|
||||
async claim(quantity: Amount): Promise<string[]> {
|
||||
async claim(amount: Amount): Promise<string[]> {
|
||||
const address = this.metaplex.identity().publicKey.toBase58();
|
||||
return this.claimTo(address, quantity);
|
||||
return this.claimTo(address, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,7 +462,7 @@ export class NFTDrop {
|
||||
const candyMachine = await this.getCandyMachine();
|
||||
await this.claimConditions.assertCanClaimable(Number(amount));
|
||||
const builders = await Promise.all(
|
||||
[...Array(amount).keys()].map(async () => {
|
||||
[...Array(Number(amount)).keys()].map(async () => {
|
||||
return await this.metaplex
|
||||
.candyMachinesV2()
|
||||
.builders()
|
||||
|
||||
@@ -29,28 +29,43 @@ export async function sendMultipartTransaction(
|
||||
// make the connected wallet sign all transactions
|
||||
const signedTx = await metaplex.identity().signAllTransactions(txns);
|
||||
|
||||
// send the signed transactions
|
||||
let signatures = [];
|
||||
for (const tx of signedTx) {
|
||||
const signature = await metaplex.connection.sendRawTransaction(
|
||||
tx.serialize(),
|
||||
// send the signed transactions in batches
|
||||
const batchSize = 200; // sends 200 raw tx in parallel
|
||||
const batches = [];
|
||||
for (let i = 0; i < signedTx.length; i += batchSize) {
|
||||
batches.push(signedTx.slice(i, i + batchSize));
|
||||
}
|
||||
let signatures: string[][] = [];
|
||||
for (const txs of batches) {
|
||||
const signature = await Promise.all(
|
||||
txs.map((tx) => metaplex.connection.sendRawTransaction(tx.serialize())),
|
||||
);
|
||||
signatures.push(signature);
|
||||
}
|
||||
|
||||
// wait for confirmations in parallel
|
||||
const confirmations = await Promise.all(
|
||||
signatures.map((sig) => {
|
||||
return metaplex.rpc().confirmTransaction(sig, {
|
||||
blockhash: block.blockhash,
|
||||
lastValidBlockHeight: block.lastValidBlockHeight,
|
||||
});
|
||||
}),
|
||||
);
|
||||
// wait for confirmations in parallel batches
|
||||
let confirmations = [];
|
||||
for (let i = 0; i < signatures.length; i += 1) {
|
||||
await delay(1000); // add delay to avoid rate limiting
|
||||
const sigs = signatures[i];
|
||||
const confirmationBatch = await Promise.all(
|
||||
sigs.map((sig) => {
|
||||
return metaplex.rpc().confirmTransaction(sig, {
|
||||
blockhash: block.blockhash,
|
||||
lastValidBlockHeight: block.lastValidBlockHeight,
|
||||
});
|
||||
}),
|
||||
);
|
||||
confirmations.push(...confirmationBatch);
|
||||
}
|
||||
|
||||
if (confirmations.length === 0) {
|
||||
throw new Error("Transaction failed");
|
||||
}
|
||||
|
||||
return signatures.map((signature) => ({ signature }));
|
||||
return signatures.flatMap((sig) => sig).map((signature) => ({ signature }));
|
||||
}
|
||||
|
||||
async function delay(delayMs: number) {
|
||||
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||
}
|
||||
|
||||
@@ -56,4 +56,25 @@ describe("Wallet", async () => {
|
||||
const address = sdk.wallet.recoverAddress(message, signature);
|
||||
expect(address).to.eq(adminWallet.address);
|
||||
});
|
||||
|
||||
it("should sign typed data and recover address", async () => {
|
||||
sdk.updateSignerOrProvider(adminWallet);
|
||||
const { payload, signature } = await sdk.wallet.signTypedData(
|
||||
{
|
||||
name: "MyEIP721Domain",
|
||||
version: "1",
|
||||
chainId: 1,
|
||||
verifyingContract: adminWallet.address,
|
||||
},
|
||||
{
|
||||
MyStruct: [
|
||||
{ name: "to", type: "address" },
|
||||
{ name: "quantity", type: "uint256" },
|
||||
],
|
||||
},
|
||||
{ to: adminWallet.address, quantity: 1 },
|
||||
);
|
||||
expect(payload !== undefined).to.eq(true);
|
||||
expect(signature !== undefined).to.eq(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,16 +149,13 @@ describe("NFTCollection", async () => {
|
||||
expect(creators[0].share).to.equal(100);
|
||||
|
||||
const newCreator = Keypair.generate().publicKey.toBase58();
|
||||
await collection.updateCreators(
|
||||
[
|
||||
{
|
||||
address: sdk.wallet.getAddress() as string,
|
||||
share: 75,
|
||||
},
|
||||
{ address: newCreator, share: 25 },
|
||||
],
|
||||
true,
|
||||
);
|
||||
await collection.updateCreators([
|
||||
{
|
||||
address: sdk.wallet.getAddress() as string,
|
||||
share: 75,
|
||||
},
|
||||
{ address: newCreator, share: 25 },
|
||||
]);
|
||||
|
||||
creators = await collection.getCreators();
|
||||
expect(creators.length).to.equal(2);
|
||||
@@ -167,16 +164,17 @@ describe("NFTCollection", async () => {
|
||||
expect(creators[1].address).to.equal(newCreator);
|
||||
expect(creators[1].share).to.equal(25);
|
||||
|
||||
const all = await collection.getAll();
|
||||
for (const nft of all) {
|
||||
// @ts-ignore
|
||||
const creatorsOfNft = await collection.nft.creatorsOf(nft.metadata.id);
|
||||
expect(creatorsOfNft.length).to.equal(2);
|
||||
expect(creatorsOfNft[0].address).to.equal(sdk.wallet.getAddress());
|
||||
expect(creatorsOfNft[0].share).to.equal(75);
|
||||
expect(creatorsOfNft[1].address).to.equal(newCreator);
|
||||
expect(creatorsOfNft[1].share).to.equal(25);
|
||||
}
|
||||
// TODO (adam): fix this test
|
||||
// const all = await collection.getAll();
|
||||
// for (const nft of all) {
|
||||
// // @ts-ignore
|
||||
// const creatorsOfNft = await collection.nft.creatorsOf(nft.metadata.id);
|
||||
// expect(creatorsOfNft.length).to.equal(2);
|
||||
// expect(creatorsOfNft[0].address).to.equal(sdk.wallet.getAddress());
|
||||
// expect(creatorsOfNft[0].share).to.equal(75);
|
||||
// expect(creatorsOfNft[1].address).to.equal(newCreator);
|
||||
// expect(creatorsOfNft[1].share).to.equal(25);
|
||||
// }
|
||||
});
|
||||
|
||||
it("Should update royalty", async () => {
|
||||
@@ -189,13 +187,13 @@ describe("NFTCollection", async () => {
|
||||
expect(nft.isMutable).to.equal(true);
|
||||
expect(nft.sellerFeeBasisPoints).to.equal(0);
|
||||
|
||||
await collection.updateRoyalty(100, true);
|
||||
await collection.updateRoyalty(100);
|
||||
|
||||
royalty = await collection.getRoyalty();
|
||||
expect(royalty).to.equal(100);
|
||||
|
||||
// @ts-ignore
|
||||
nft = await collection.nft.getRaw(mintAddress);
|
||||
expect(nft.sellerFeeBasisPoints).to.equal(100);
|
||||
// TODO (adam): fix this test
|
||||
// nft = await collection.nft.getRaw(mintAddress);
|
||||
// expect(nft.sellerFeeBasisPoints).to.equal(100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { NFTDrop, Token } from "../../src/solana";
|
||||
import { sdk } from "./before-setup";
|
||||
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||
import { expect } from "chai";
|
||||
|
||||
describe("NFTDrop ClaimConditions", async () => {
|
||||
let drop: NFTDrop;
|
||||
let token: Token;
|
||||
let primarySaleRecipient: string;
|
||||
|
||||
before(async () => {
|
||||
const address = await sdk.deployer.createNftDrop({
|
||||
name: "NFT Drop #1",
|
||||
totalSupply: 5,
|
||||
});
|
||||
const addressToken = await sdk.deployer.createToken({
|
||||
name: "Token #1",
|
||||
initialSupply: 100,
|
||||
});
|
||||
drop = await sdk.getNFTDrop(address);
|
||||
token = await sdk.getToken(addressToken);
|
||||
primarySaleRecipient = Keypair.generate().publicKey.toBase58();
|
||||
await token.mintTo(primarySaleRecipient, 1); // only needed to create the token account / TODO expose utility for it
|
||||
});
|
||||
|
||||
it("should set claim conditions with custom currency and claim", async () => {
|
||||
const metadatas = [...Array(5)].map((_, i) => ({
|
||||
name: `NFT #${i + 1}`,
|
||||
description: `This is the #${i + 1} NFT`,
|
||||
}));
|
||||
await drop.lazyMint(metadatas);
|
||||
expect((await token.balance()).displayValue).to.eq("100.000000000");
|
||||
expect((await token.balanceOf(primarySaleRecipient)).displayValue).to.eq(
|
||||
"1.000000000",
|
||||
);
|
||||
|
||||
await drop.claimConditions.set({
|
||||
maxClaimable: 3,
|
||||
currencyAddress: token.publicKey.toBase58(),
|
||||
price: 1,
|
||||
primarySaleRecipient,
|
||||
});
|
||||
const addresses = await drop.claim(2);
|
||||
expect(addresses.length).to.eq(2);
|
||||
expect((await token.balance()).displayValue).to.eq("98.000000000");
|
||||
expect((await token.balanceOf(primarySaleRecipient)).displayValue).to.eq(
|
||||
"3.000000000",
|
||||
);
|
||||
expect(await drop.balance(addresses[0])).to.eq(1);
|
||||
expect(await drop.balance(addresses[1])).to.eq(1);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,50 @@
|
||||
# @thirdweb-dev/unity-js-bridge
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e913e0d`](https://github.com/thirdweb-dev/js/commit/e913e0daa9ece9a6274f7ffa4e66bdcbf32e6ada), [`5862c55`](https://github.com/thirdweb-dev/js/commit/5862c558fb48604b5aca4defd1ccc06fc3536358), [`73883f5`](https://github.com/thirdweb-dev/js/commit/73883f56d0ed0a35ace8b98b96caa782443be22e), [`f7a74a3`](https://github.com/thirdweb-dev/js/commit/f7a74a3b7c489ada5b1ec435632a326334a33c9b)]:
|
||||
- @thirdweb-dev/sdk@3.6.9
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#457](https://github.com/thirdweb-dev/js/pull/457) [`ccb4ce8`](https://github.com/thirdweb-dev/js/commit/ccb4ce8d00f50ff34426a0a5e58c7243e409f706) Thanks [@jnsdls](https://github.com/jnsdls)! - Support for new magic link connector
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#458](https://github.com/thirdweb-dev/js/pull/458) [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for latest wallet package integration
|
||||
|
||||
- Updated dependencies [[`ccb4ce8`](https://github.com/thirdweb-dev/js/commit/ccb4ce8d00f50ff34426a0a5e58c7243e409f706), [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0), [`ea95c5f`](https://github.com/thirdweb-dev/js/commit/ea95c5f609e306e333ee0f73f7920503358ca848), [`699a2b1`](https://github.com/thirdweb-dev/js/commit/699a2b16fb991c474ec57db8f178e2601d631f39), [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877), [`a8267f9`](https://github.com/thirdweb-dev/js/commit/a8267f912df84c58d3fe3f47b90bd474f73c84ca)]:
|
||||
- @thirdweb-dev/wallets@0.1.1
|
||||
- @thirdweb-dev/sdk@3.6.8
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3731459`](https://github.com/thirdweb-dev/js/commit/3731459d33f1ded7ebb69124809449b901b3ad3d), [`cac6c30`](https://github.com/thirdweb-dev/js/commit/cac6c30bca5e17df81d746ef81316af47d5e252e), [`7a37e56`](https://github.com/thirdweb-dev/js/commit/7a37e564fd5d5a9df84c8da44ecaf6c42f67a0e2)]:
|
||||
- @thirdweb-dev/sdk@3.6.7
|
||||
|
||||
## 0.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`8c6cdaa`](https://github.com/thirdweb-dev/js/commit/8c6cdaa2887fb2cc40d3ee6991233d195d103805)]:
|
||||
- @thirdweb-dev/sdk@3.6.6
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/unity-js-bridge",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.5",
|
||||
"main": "dist/thirdweb-unity-bridge.js",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
|
||||
"license": "Apache-2.0",
|
||||
@@ -12,13 +12,14 @@
|
||||
"dist/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/pay": "*",
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"@thirdweb-dev/storage": "*",
|
||||
"@thirdweb-dev/wallets": "*",
|
||||
"ethers": "^5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.15.16",
|
||||
"esbuild": "0.15.17",
|
||||
"node-stdlib-browser": "^1.2.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
/// --- Thirdweb Brige ---
|
||||
import { ChainOrRpc, ThirdwebSDK } from "@thirdweb-dev/sdk/evm";
|
||||
import { CoinbasePayIntegration, FundWalletOptions } from "@thirdweb-dev/pay";
|
||||
import { ChainOrRpc, ThirdwebSDK, getRpcUrl } from "@thirdweb-dev/sdk";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
CoinbaseWallet,
|
||||
MetaMask,
|
||||
WalletConnect,
|
||||
InjectedWallet,
|
||||
MagicAuthWallet,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
import type { AbstractWallet } from "@thirdweb-dev/wallets/dist/declarations/src/wallets/base";
|
||||
import { BigNumber } from "ethers";
|
||||
@@ -17,6 +19,8 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
const API_KEY =
|
||||
"339d65590ba0fa79e4c8be0af33d64eda709e13652acb02c6be63f5a1fbef9c3";
|
||||
const SEPARATOR = "/";
|
||||
const SUB_SEPARATOR = "#";
|
||||
|
||||
@@ -40,16 +44,22 @@ const WALLETS = [
|
||||
InjectedWallet,
|
||||
WalletConnect,
|
||||
CoinbaseWallet,
|
||||
MagicAuthWallet,
|
||||
] 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;
|
||||
@@ -77,18 +87,19 @@ class ThirdwebBridge implements TWBridge {
|
||||
console.debug("thirdwebSDK initialization:", chain, options);
|
||||
const sdkOptions = JSON.parse(options);
|
||||
const storage =
|
||||
sdkOptions && sdkOptions.ipfsGatewayUrl
|
||||
sdkOptions?.storage && sdkOptions?.storage?.ipfsGatewayUrl
|
||||
? new ThirdwebStorage({
|
||||
gatewayUrls: {
|
||||
"ipfs://": [sdkOptions.ipfsGatewayUrl],
|
||||
"ipfs://": [sdkOptions.storage.ipfsGatewayUrl],
|
||||
},
|
||||
})
|
||||
: new ThirdwebStorage();
|
||||
|
||||
this.activeSDK = new ThirdwebSDK(chain, sdkOptions, storage);
|
||||
const rpcUrl = chain.startsWith("http") ? chain : getRpcUrl(chain, API_KEY);
|
||||
this.activeSDK = new ThirdwebSDK(rpcUrl, sdkOptions, storage);
|
||||
for (let wallet of WALLETS) {
|
||||
const walletInstance = new wallet({
|
||||
appName: sdkOptions.appName || "thirdweb powered dApp",
|
||||
appName: sdkOptions.wallet?.appName || "thirdweb powered dApp",
|
||||
...sdkOptions.wallet?.extras,
|
||||
});
|
||||
walletInstance.on("connect", async () =>
|
||||
this.updateSDKSigner(await walletInstance.getSigner()),
|
||||
@@ -211,6 +222,15 @@ class ThirdwebBridge implements TWBridge {
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# @thirdweb-dev/wallets
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#457](https://github.com/thirdweb-dev/js/pull/457) [`ccb4ce8`](https://github.com/thirdweb-dev/js/commit/ccb4ce8d00f50ff34426a0a5e58c7243e409f706) Thanks [@jnsdls](https://github.com/jnsdls)! - Support for new magic link connector
|
||||
|
||||
- [#454](https://github.com/thirdweb-dev/js/pull/454) [`c673e39`](https://github.com/thirdweb-dev/js/commit/c673e39f23ef082097d73d62910580e8fad400a0) Thanks [@jnsdls](https://github.com/jnsdls)! - upgraded dependencies
|
||||
|
||||
- [#458](https://github.com/thirdweb-dev/js/pull/458) [`4cdd0bd`](https://github.com/thirdweb-dev/js/commit/4cdd0bd6348494a256d7c6a2bdf8f7b5c20f6877) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fixes for latest wallet package integration
|
||||
|
||||
## 0.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-wallets-connectors-magic.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets-connectors-magic.esm.js"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wallets",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"main": "dist/thirdweb-dev-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets.esm.js",
|
||||
"exports": {
|
||||
@@ -20,6 +20,14 @@
|
||||
"module": "./wallets/metamask/dist/thirdweb-dev-wallets-wallets-metamask.esm.js",
|
||||
"default": "./wallets/metamask/dist/thirdweb-dev-wallets-wallets-metamask.cjs.js"
|
||||
},
|
||||
"./wallets/magic-auth": {
|
||||
"module": "./wallets/magic-auth/dist/thirdweb-dev-wallets-wallets-magic-auth.esm.js",
|
||||
"default": "./wallets/magic-auth/dist/thirdweb-dev-wallets-wallets-magic-auth.cjs.js"
|
||||
},
|
||||
"./connectors/magic": {
|
||||
"module": "./connectors/magic/dist/thirdweb-dev-wallets-connectors-magic.esm.js",
|
||||
"default": "./connectors/magic/dist/thirdweb-dev-wallets-connectors-magic.cjs.js"
|
||||
},
|
||||
"./wallets/wallet-connect": {
|
||||
"module": "./wallets/wallet-connect/dist/thirdweb-dev-wallets-wallets-wallet-connect.esm.js",
|
||||
"default": "./wallets/wallet-connect/dist/thirdweb-dev-wallets-wallets-wallet-connect.cjs.js"
|
||||
@@ -60,7 +68,7 @@
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
"index.ts",
|
||||
"connectors/**",
|
||||
"connectors/*/index.ts",
|
||||
"wallets/**"
|
||||
],
|
||||
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
|
||||
@@ -71,12 +79,17 @@
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@coinbase/wallet-sdk": "^3.6.0",
|
||||
"@wagmi/core": "^0.7.6",
|
||||
"@magic-ext/connect": "^3.1.0",
|
||||
"@magic-ext/oauth": "^4.1.0",
|
||||
"@magic-sdk/provider": "^10.1.0",
|
||||
"@wagmi/chains": "^0.1.0",
|
||||
"@wagmi/core": "^0.8.2",
|
||||
"@walletconnect/ethereum-provider": "^1.7.8",
|
||||
"abitype": "^0.2.5",
|
||||
"ethers": "^5.7.2",
|
||||
"eventemitter3": "^5.0.0",
|
||||
"localforage": "^1.10.0"
|
||||
"localforage": "^1.10.0",
|
||||
"magic-sdk": "^10.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preconstruct/cli": "^2.2.1",
|
||||
|
||||
@@ -158,7 +158,8 @@ export class CoinbaseWalletConnector extends Connector<
|
||||
: chain_.id === walletExtensionChainId,
|
||||
) || this.chains[0];
|
||||
const chainId = this.options.chainId || chain?.id;
|
||||
const jsonRpcUrl = this.options.jsonRpcUrl || chain?.rpcUrls.default;
|
||||
const jsonRpcUrl =
|
||||
this.options.jsonRpcUrl || chain?.rpcUrls.default.http[0];
|
||||
|
||||
this.#provider = this.#client.makeWeb3Provider(jsonRpcUrl, chainId);
|
||||
}
|
||||
@@ -199,7 +200,8 @@ export class CoinbaseWalletConnector extends Connector<
|
||||
id: chainId,
|
||||
name: `Chain ${id}`,
|
||||
network: `${id}`,
|
||||
rpcUrls: { default: "" },
|
||||
nativeCurrency: { name: "Ether", decimals: 18, symbol: "ETH" },
|
||||
rpcUrls: { default: { http: [""] } },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -218,7 +220,10 @@ export class CoinbaseWalletConnector extends Connector<
|
||||
chainId: id,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: [chain.rpcUrls.public ?? chain.rpcUrls.default],
|
||||
rpcUrls: [
|
||||
chain.rpcUrls.public?.http[0] ??
|
||||
chain.rpcUrls.default.http[0],
|
||||
],
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -18,13 +18,16 @@ import type { Address } from "abitype";
|
||||
import { providers } from "ethers";
|
||||
import { getAddress, hexValue } from "ethers/lib/utils.js";
|
||||
|
||||
declare interface Window {
|
||||
ethereum: Ethereum;
|
||||
}
|
||||
|
||||
export type InjectedConnectorOptions = {
|
||||
/** Name of connector */
|
||||
name?: string | ((detectedName: string | string[]) => string);
|
||||
/**
|
||||
* [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider to target
|
||||
*
|
||||
* @default
|
||||
* () => typeof window !== 'undefined' ? window.ethereum : undefined
|
||||
*/
|
||||
getProvider?: () => Ethereum | undefined;
|
||||
/**
|
||||
* MetaMask 10.9.3 emits disconnect event when chain is changed.
|
||||
* This flag prevents the `"disconnect"` event from being emitted upon switching chains. See [GitHub issue](https://github.com/MetaMask/metamask-extension/issues/13375#issuecomment-1027663334) for more info.
|
||||
@@ -38,16 +41,19 @@ export type InjectedConnectorOptions = {
|
||||
shimDisconnect?: boolean;
|
||||
};
|
||||
|
||||
type ConnectorOptions = InjectedConnectorOptions &
|
||||
Required<Pick<InjectedConnectorOptions, "getProvider">>;
|
||||
|
||||
export class InjectedConnector extends Connector<
|
||||
Window["ethereum"],
|
||||
InjectedConnectorOptions | undefined,
|
||||
Ethereum,
|
||||
ConnectorOptions,
|
||||
providers.JsonRpcSigner
|
||||
> {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly ready = typeof window !== "undefined" && !!window.ethereum;
|
||||
readonly ready: boolean;
|
||||
|
||||
#provider?: Window["ethereum"];
|
||||
#provider?: Ethereum;
|
||||
#switchingChains?: boolean;
|
||||
|
||||
protected shimDisconnectKey = "injected.shimDisconnect";
|
||||
@@ -62,28 +68,34 @@ export class InjectedConnector extends Connector<
|
||||
const options = {
|
||||
shimDisconnect: true,
|
||||
shimChainChangedDisconnect: true,
|
||||
getProvider: () =>
|
||||
typeof window !== "undefined"
|
||||
? (window.ethereum as Ethereum)
|
||||
: undefined,
|
||||
...options_,
|
||||
};
|
||||
super({ chains, options });
|
||||
|
||||
let name = "Injected";
|
||||
const overrideName = options.name;
|
||||
if (typeof overrideName === "string") {
|
||||
name = overrideName;
|
||||
} else if (typeof window !== "undefined") {
|
||||
const detectedName = getInjectedName(window.ethereum as Ethereum);
|
||||
if (overrideName) {
|
||||
name = overrideName(detectedName);
|
||||
const provider = options.getProvider();
|
||||
if (typeof options.name === "string") {
|
||||
this.name = options.name;
|
||||
} else if (provider) {
|
||||
const detectedName = getInjectedName(provider as Ethereum);
|
||||
if (options.name) {
|
||||
this.name = options.name(detectedName);
|
||||
} else {
|
||||
name =
|
||||
typeof detectedName === "string"
|
||||
? detectedName
|
||||
: (detectedName[0] as string);
|
||||
if (typeof detectedName === "string") {
|
||||
this.name = detectedName;
|
||||
} else {
|
||||
this.name = detectedName[0] as string;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.name = "Injected";
|
||||
}
|
||||
|
||||
this.id = "injected";
|
||||
this.name = name;
|
||||
this.ready = !!provider;
|
||||
}
|
||||
|
||||
async connect({ chainId }: { chainId?: number } = {}) {
|
||||
@@ -115,8 +127,8 @@ export class InjectedConnector extends Connector<
|
||||
}
|
||||
|
||||
// Add shim to storage signalling wallet is connected
|
||||
if (this.options?.shimDisconnect) {
|
||||
await getConnectorStorage().setItem(this.shimDisconnectKey, true);
|
||||
if (this.options.shimDisconnect) {
|
||||
await getConnectorStorage()?.setItem(this.shimDisconnectKey, true);
|
||||
}
|
||||
|
||||
return { account, chain: { id, unsupported }, provider };
|
||||
@@ -142,8 +154,8 @@ export class InjectedConnector extends Connector<
|
||||
provider.removeListener("disconnect", this.onDisconnect);
|
||||
|
||||
// Remove shim signalling wallet is disconnected
|
||||
if (this.options?.shimDisconnect) {
|
||||
await getConnectorStorage().removeItem(this.shimDisconnectKey);
|
||||
if (this.options.shimDisconnect) {
|
||||
await getConnectorStorage()?.removeItem(this.shimDisconnectKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,8 +180,9 @@ export class InjectedConnector extends Connector<
|
||||
}
|
||||
|
||||
async getProvider() {
|
||||
if (typeof window !== "undefined" && !!window.ethereum) {
|
||||
this.#provider = window.ethereum as Ethereum;
|
||||
const provider = this.options.getProvider();
|
||||
if (provider) {
|
||||
this.#provider = provider;
|
||||
}
|
||||
return this.#provider as Ethereum;
|
||||
}
|
||||
@@ -188,9 +201,9 @@ export class InjectedConnector extends Connector<
|
||||
async isAuthorized() {
|
||||
try {
|
||||
if (
|
||||
this.options?.shimDisconnect &&
|
||||
this.options.shimDisconnect &&
|
||||
// If shim does not exist in storage, wallet is disconnected
|
||||
!(await getConnectorStorage().getItem(this.shimDisconnectKey))
|
||||
!(await getConnectorStorage()?.getItem(this.shimDisconnectKey))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -207,7 +220,7 @@ export class InjectedConnector extends Connector<
|
||||
}
|
||||
|
||||
async switchChain(chainId: number) {
|
||||
if (this.options?.shimChainChangedDisconnect) {
|
||||
if (this.options.shimChainChangedDisconnect) {
|
||||
this.#switchingChains = true;
|
||||
}
|
||||
|
||||
@@ -227,7 +240,8 @@ export class InjectedConnector extends Connector<
|
||||
id: chainId,
|
||||
name: `Chain ${id}`,
|
||||
network: `${id}`,
|
||||
rpcUrls: { default: "" },
|
||||
nativeCurrency: { name: "Ether", decimals: 18, symbol: "ETH" },
|
||||
rpcUrls: { default: { http: [""] } },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -252,7 +266,11 @@ export class InjectedConnector extends Connector<
|
||||
chainId: id,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: [chain.rpcUrls.public ?? chain.rpcUrls.default],
|
||||
rpcUrls: [
|
||||
chain.rpcUrls.public?.http[0] ??
|
||||
chain.rpcUrls.default.http[0] ??
|
||||
"",
|
||||
],
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
@@ -322,14 +340,14 @@ export class InjectedConnector extends Connector<
|
||||
// We need this as MetaMask can emit the "disconnect" event
|
||||
// upon switching chains. This workaround ensures that the
|
||||
// user currently isn't in the process of switching chains.
|
||||
if (this.options?.shimChainChangedDisconnect && this.#switchingChains) {
|
||||
if (this.options.shimChainChangedDisconnect && this.#switchingChains) {
|
||||
this.#switchingChains = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit("disconnect");
|
||||
// Remove shim signalling wallet is disconnected
|
||||
if (this.options?.shimDisconnect) {
|
||||
if (this.options.shimDisconnect) {
|
||||
await getConnectorStorage().removeItem(this.shimDisconnectKey);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
import { MagicConnector, MagicOptions } from "./magicConnector";
|
||||
import { OAuthExtension, OAuthProvider } from "@magic-ext/oauth";
|
||||
import {
|
||||
InstanceWithExtensions,
|
||||
MagicSDKAdditionalConfiguration,
|
||||
SDKBase,
|
||||
} from "@magic-sdk/provider";
|
||||
import {
|
||||
Chain,
|
||||
ChainNotConfiguredError,
|
||||
normalizeChainId,
|
||||
UserRejectedRequestError,
|
||||
} from "@wagmi/core";
|
||||
import { getAddress } from "ethers/lib/utils.js";
|
||||
import { Magic } from "magic-sdk";
|
||||
|
||||
export interface MagicAuthOptions extends MagicOptions {
|
||||
enableEmailLogin?: boolean;
|
||||
enableSMSLogin?: boolean;
|
||||
oauthOptions?: {
|
||||
providers: OAuthProvider[];
|
||||
callbackUrl?: string;
|
||||
};
|
||||
magicSdkConfiguration?: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
OAuthExtension[]
|
||||
>;
|
||||
}
|
||||
|
||||
export class MagicAuthConnector extends MagicConnector {
|
||||
magicSDK?: InstanceWithExtensions<SDKBase, OAuthExtension[]>;
|
||||
|
||||
magicSdkConfiguration: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
OAuthExtension[]
|
||||
>;
|
||||
|
||||
enableSMSLogin: boolean;
|
||||
|
||||
enableEmailLogin: boolean;
|
||||
|
||||
oauthProviders: OAuthProvider[];
|
||||
|
||||
oauthCallbackUrl?: string;
|
||||
|
||||
constructor(config: { chains?: Chain[]; options: MagicAuthOptions }) {
|
||||
super(config);
|
||||
this.magicSdkConfiguration = config.options.magicSdkConfiguration || {};
|
||||
this.oauthProviders = config.options.oauthOptions?.providers || [];
|
||||
this.oauthCallbackUrl = config.options.oauthOptions?.callbackUrl;
|
||||
this.enableSMSLogin = config.options.enableSMSLogin || false;
|
||||
this.enableEmailLogin = config.options.enableEmailLogin || false;
|
||||
}
|
||||
|
||||
async connect({ chainId }: { chainId?: number }) {
|
||||
// for a specific chainId we will overwrite the magicSDKConfiguration
|
||||
if (chainId) {
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (chain) {
|
||||
this.magicSdkConfiguration = {
|
||||
...this.magicSdkConfiguration,
|
||||
network: {
|
||||
chainId: chain.id,
|
||||
rpcUrl: chain.rpcUrls.default.http[0],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
try {
|
||||
const provider = await this.getProvider();
|
||||
|
||||
if (provider.on) {
|
||||
provider.on("accountsChanged", this.onAccountsChanged);
|
||||
provider.on("chainChanged", this.onChainChanged);
|
||||
provider.on("disconnect", this.onDisconnect);
|
||||
}
|
||||
|
||||
// Check if there is a user logged in
|
||||
const isAuthenticated = await this.isAuthorized();
|
||||
|
||||
// Check if we have a chainId, in case of error just assign 0 for legacy
|
||||
let chainId_: number;
|
||||
try {
|
||||
chainId_ = await this.getChainId();
|
||||
} catch (e) {
|
||||
chainId_ = 0;
|
||||
}
|
||||
|
||||
// if there is a user logged in, return the user
|
||||
if (isAuthenticated) {
|
||||
return {
|
||||
provider,
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
account: getAddress(await this.getAccount()),
|
||||
};
|
||||
}
|
||||
|
||||
// open the modal and process the magic login steps
|
||||
if (!this.isModalOpen) {
|
||||
const output = await this.getUserDetailsByForm(
|
||||
this.enableSMSLogin,
|
||||
this.enableEmailLogin,
|
||||
this.oauthProviders,
|
||||
);
|
||||
const magic = this.getMagicSDK();
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH OAUTH PROVIDER
|
||||
if (output.oauthProvider) {
|
||||
await magic.oauth.loginWithRedirect({
|
||||
provider: output.oauthProvider,
|
||||
redirectURI: this.oauthCallbackUrl || window.location.href,
|
||||
});
|
||||
}
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH EMAIL
|
||||
if (output.email) {
|
||||
await magic.auth.loginWithMagicLink({
|
||||
email: output.email,
|
||||
});
|
||||
}
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH PHONE NUMBER
|
||||
if (output.phoneNumber) {
|
||||
await magic.auth.loginWithSMS({
|
||||
phoneNumber: output.phoneNumber,
|
||||
});
|
||||
}
|
||||
|
||||
const signer = await this.getSigner();
|
||||
const account = await signer.getAddress();
|
||||
|
||||
return {
|
||||
account: getAddress(account),
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
provider,
|
||||
};
|
||||
}
|
||||
throw new UserRejectedRequestError("User rejected request");
|
||||
} catch (error) {
|
||||
throw new UserRejectedRequestError("Something went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
async getChainId(): Promise<number> {
|
||||
const networkOptions = this.magicSdkConfiguration?.network;
|
||||
if (typeof networkOptions === "object") {
|
||||
const chainID = networkOptions.chainId;
|
||||
if (chainID) {
|
||||
return normalizeChainId(chainID);
|
||||
}
|
||||
}
|
||||
throw new Error("Chain ID is not defined");
|
||||
}
|
||||
|
||||
getMagicSDK(): InstanceWithExtensions<SDKBase, OAuthExtension[]> {
|
||||
if (!this.magicSDK) {
|
||||
this.magicSDK = new Magic(this.magicOptions.apiKey, {
|
||||
...this.magicSdkConfiguration,
|
||||
extensions: [new OAuthExtension()],
|
||||
});
|
||||
return this.magicSDK;
|
||||
}
|
||||
return this.magicSDK;
|
||||
}
|
||||
|
||||
async switchChain(chainId: number): Promise<Chain> {
|
||||
// check if the chain is supported
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (!chain) {
|
||||
throw new ChainNotConfiguredError({ chainId, connectorId: this.id });
|
||||
}
|
||||
await this.connect({ chainId });
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { MagicConnector, MagicOptions } from "./magicConnector";
|
||||
import { ConnectExtension } from "@magic-ext/connect";
|
||||
import {
|
||||
InstanceWithExtensions,
|
||||
MagicSDKAdditionalConfiguration,
|
||||
SDKBase,
|
||||
} from "@magic-sdk/provider";
|
||||
import {
|
||||
Chain,
|
||||
ChainNotConfiguredError,
|
||||
normalizeChainId,
|
||||
UserRejectedRequestError,
|
||||
} from "@wagmi/core";
|
||||
import { getAddress } from "ethers/lib/utils.js";
|
||||
import { Magic } from "magic-sdk";
|
||||
|
||||
interface MagicConnectOptions extends MagicOptions {
|
||||
magicSdkConfiguration?: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
ConnectExtension[]
|
||||
>;
|
||||
}
|
||||
|
||||
const CONNECT_TIME_KEY = "wagmi-magic-connector.connect.time";
|
||||
const CONNECT_DURATION = 604800000; // 7 days in milliseconds
|
||||
|
||||
export class MagicConnectConnector extends MagicConnector {
|
||||
magicSDK?: InstanceWithExtensions<SDKBase, ConnectExtension[]>;
|
||||
|
||||
magicSdkConfiguration: MagicSDKAdditionalConfiguration<
|
||||
string,
|
||||
ConnectExtension[]
|
||||
>;
|
||||
|
||||
constructor(config: { chains?: Chain[]; options: MagicConnectOptions }) {
|
||||
super(config);
|
||||
this.magicSdkConfiguration = config.options.magicSdkConfiguration || {};
|
||||
}
|
||||
|
||||
async connect({ chainId }: { chainId?: number }) {
|
||||
// for a specific chainId we will overwrite the magicSDKConfiguration
|
||||
if (chainId) {
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (chain) {
|
||||
this.magicSdkConfiguration = {
|
||||
...this.magicSdkConfiguration,
|
||||
network: {
|
||||
chainId: chain.id,
|
||||
rpcUrl: chain.rpcUrls.default.http[0],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
try {
|
||||
const provider = await this.getProvider();
|
||||
|
||||
if (provider.on) {
|
||||
provider.on("accountsChanged", this.onAccountsChanged);
|
||||
provider.on("chainChanged", this.onChainChanged);
|
||||
provider.on("disconnect", this.onDisconnect);
|
||||
}
|
||||
|
||||
// Check if there is a user logged in
|
||||
const isAuthenticated = await this.isAuthorized();
|
||||
|
||||
// Check if we have a chainId, in case of error just assign 0 for legacy
|
||||
let chainId_: number;
|
||||
try {
|
||||
chainId_ = await this.getChainId();
|
||||
} catch (e) {
|
||||
chainId_ = 0;
|
||||
}
|
||||
|
||||
// if there is a user logged in, return the user
|
||||
if (isAuthenticated) {
|
||||
return {
|
||||
provider,
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
account: await this.getAccount(),
|
||||
};
|
||||
}
|
||||
|
||||
// open the modal and process the magic login steps
|
||||
if (!this.isModalOpen) {
|
||||
const output = await this.getUserDetailsByForm(false, true, []);
|
||||
const magic = this.getMagicSDK();
|
||||
|
||||
// LOGIN WITH MAGIC LINK WITH EMAIL
|
||||
if (output.email) {
|
||||
await magic.auth.loginWithEmailOTP({
|
||||
email: output.email,
|
||||
});
|
||||
|
||||
const signer = await this.getSigner();
|
||||
const account = await signer.getAddress();
|
||||
|
||||
// As we have no way to know if a user is connected to Magic Connect we store a connect timestamp
|
||||
// in local storage
|
||||
window.localStorage.setItem(
|
||||
CONNECT_TIME_KEY,
|
||||
String(new Date().getTime()),
|
||||
);
|
||||
|
||||
return {
|
||||
account: getAddress(account),
|
||||
chain: {
|
||||
id: chainId_,
|
||||
unsupported: false,
|
||||
},
|
||||
provider,
|
||||
};
|
||||
}
|
||||
}
|
||||
throw new UserRejectedRequestError("User rejected request");
|
||||
} catch (error) {
|
||||
throw new UserRejectedRequestError("Something went wrong");
|
||||
}
|
||||
}
|
||||
|
||||
async getChainId(): Promise<number> {
|
||||
const networkOptions = this.magicSdkConfiguration?.network;
|
||||
if (typeof networkOptions === "object") {
|
||||
const chainID = networkOptions.chainId;
|
||||
if (chainID) {
|
||||
return normalizeChainId(chainID);
|
||||
}
|
||||
}
|
||||
throw new Error("Chain ID is not defined");
|
||||
}
|
||||
|
||||
getMagicSDK(): InstanceWithExtensions<SDKBase, ConnectExtension[]> {
|
||||
if (!this.magicSDK) {
|
||||
this.magicSDK = new Magic(this.magicOptions.apiKey, {
|
||||
...this.magicSdkConfiguration,
|
||||
extensions: [new ConnectExtension()],
|
||||
});
|
||||
return this.magicSDK;
|
||||
}
|
||||
return this.magicSDK;
|
||||
}
|
||||
|
||||
// Overrides isAuthorized because Connect opens overlay whenever we interact with one of its methods.
|
||||
// Moreover, there is currently no proper way to know if a user is currently logged in to Magic Connect.
|
||||
// So we use a local storage state to handle this information.
|
||||
// TODO Once connect API grows, integrate it
|
||||
async isAuthorized() {
|
||||
if (localStorage.getItem(CONNECT_TIME_KEY) === null) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
parseInt(window.localStorage.getItem(CONNECT_TIME_KEY) || "") +
|
||||
CONNECT_DURATION >
|
||||
new Date().getTime()
|
||||
);
|
||||
}
|
||||
|
||||
// Overrides disconnect because there is currently no proper way to disconnect a user from Magic
|
||||
// Connect.
|
||||
// So we use a local storage state to handle this information.
|
||||
async disconnect(): Promise<void> {
|
||||
window.localStorage.removeItem(CONNECT_TIME_KEY);
|
||||
}
|
||||
|
||||
async switchChain(chainId: number): Promise<Chain> {
|
||||
// check if the chain is supported
|
||||
const chain = this.chains.find((c) => c.id === chainId);
|
||||
if (!chain) {
|
||||
throw new ChainNotConfiguredError({ chainId, connectorId: this.id });
|
||||
}
|
||||
await this.connect({ chainId });
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { createModal } from "../modal/view";
|
||||
import { ConnectExtension } from "@magic-ext/connect";
|
||||
import { OAuthExtension, OAuthProvider } from "@magic-ext/oauth";
|
||||
import { InstanceWithExtensions, SDKBase } from "@magic-sdk/provider";
|
||||
import { RPCProviderModule } from "@magic-sdk/provider/dist/types/modules/rpc-provider";
|
||||
import { Chain, Connector, normalizeChainId } from "@wagmi/core";
|
||||
import { providers, Signer } from "ethers";
|
||||
import { getAddress } from "ethers/lib/utils";
|
||||
import type { AbstractProvider } from "web3-core";
|
||||
|
||||
const IS_SERVER = typeof window === "undefined";
|
||||
|
||||
export interface MagicOptions {
|
||||
apiKey: string;
|
||||
accentColor?: string;
|
||||
isDarkMode?: boolean;
|
||||
customLogo?: string;
|
||||
customHeaderText?: string;
|
||||
}
|
||||
|
||||
interface UserDetails {
|
||||
email: string;
|
||||
phoneNumber: string;
|
||||
oauthProvider: OAuthProvider;
|
||||
}
|
||||
|
||||
export abstract class MagicConnector extends Connector<
|
||||
RPCProviderModule & AbstractProvider
|
||||
> {
|
||||
ready = !IS_SERVER;
|
||||
|
||||
readonly id = "magic";
|
||||
|
||||
readonly name = "Magic";
|
||||
|
||||
provider?: RPCProviderModule & AbstractProvider;
|
||||
|
||||
isModalOpen = false;
|
||||
|
||||
magicOptions: MagicOptions;
|
||||
|
||||
protected constructor(config: { chains?: Chain[]; options: MagicOptions }) {
|
||||
super(config);
|
||||
this.magicOptions = config.options;
|
||||
}
|
||||
|
||||
async getAccount() {
|
||||
const provider = new providers.Web3Provider(
|
||||
(await this.getProvider()) as unknown as providers.ExternalProvider,
|
||||
);
|
||||
const signer = provider.getSigner();
|
||||
const account = await signer.getAddress();
|
||||
return getAddress(account);
|
||||
}
|
||||
|
||||
async getUserDetailsByForm(
|
||||
enableSMSLogin: boolean,
|
||||
enableEmailLogin: boolean,
|
||||
oauthProviders: OAuthProvider[],
|
||||
): Promise<UserDetails> {
|
||||
const output: UserDetails = (await createModal({
|
||||
accentColor: this.magicOptions.accentColor,
|
||||
isDarkMode: this.magicOptions.isDarkMode,
|
||||
customLogo: this.magicOptions.customLogo,
|
||||
customHeaderText: this.magicOptions.customHeaderText,
|
||||
enableSMSLogin: enableSMSLogin,
|
||||
enableEmailLogin: enableEmailLogin || true,
|
||||
oauthProviders,
|
||||
})) as UserDetails;
|
||||
|
||||
this.isModalOpen = false;
|
||||
return output;
|
||||
}
|
||||
|
||||
async getProvider() {
|
||||
if (this.provider) {
|
||||
return this.provider;
|
||||
}
|
||||
const magic = this.getMagicSDK();
|
||||
this.provider = magic.rpcProvider;
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
async getSigner(): Promise<Signer> {
|
||||
const provider = new providers.Web3Provider(
|
||||
(await this.getProvider()) as unknown as providers.ExternalProvider,
|
||||
);
|
||||
return provider.getSigner();
|
||||
}
|
||||
|
||||
async isAuthorized() {
|
||||
const magic = this.getMagicSDK();
|
||||
try {
|
||||
return await magic.user.isLoggedIn();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected onAccountsChanged(accounts: string[]): void {
|
||||
if (accounts.length === 0) {
|
||||
this.emit("disconnect");
|
||||
} else {
|
||||
this.emit("change", { account: getAddress(accounts[0]) });
|
||||
}
|
||||
}
|
||||
|
||||
protected onChainChanged(chainId: string | number): void {
|
||||
const id = normalizeChainId(chainId);
|
||||
const unsupported = this.isChainUnsupported(id);
|
||||
this.emit("change", { chain: { id, unsupported } });
|
||||
}
|
||||
|
||||
protected onDisconnect(): void {
|
||||
this.emit("disconnect");
|
||||
}
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
const magic = this.getMagicSDK();
|
||||
await magic.user.logout();
|
||||
}
|
||||
|
||||
abstract getMagicSDK():
|
||||
| InstanceWithExtensions<SDKBase, OAuthExtension[]>
|
||||
| InstanceWithExtensions<SDKBase, ConnectExtension[]>;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./connectors/magicAuthConnector";
|
||||
export * from "./connectors/magicConnectConnector";
|
||||
@@ -0,0 +1,106 @@
|
||||
export const MagicLogo = `
|
||||
<svg width="65" height="65" viewBox="0 0 65 65" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="65" height="65" rx="14" fill="#6851FF"/>
|
||||
<path d="M32.9375 10C34.9919 12.3852 37.258 14.6002 39.7083 16.6194C38.0756 21.6391 37.1965 26.9723 37.1965 32.5C37.1965 38.0277 38.0756 43.3609 39.7083 48.3806C37.258 50.3997 34.9919 52.6149 32.9375 55C30.883 52.6149 28.617 50.3997 26.1666 48.3806C27.7994 43.3609 28.6784 38.0277 28.6784 32.5C28.6784 26.9723 27.7994 21.6391 26.1666 16.6194C28.617 14.6002 30.883 12.3852 32.9375 10Z" fill="white"/>
|
||||
<path d="M21.4831 44.9102C18.8894 43.1868 16.1367 41.6639 13.25 40.365C14.0507 37.8772 14.4815 35.2363 14.4815 32.5C14.4815 29.7638 14.0507 27.123 13.25 24.635C16.1366 23.3361 18.8894 21.8132 21.4831 20.09C22.4749 24.074 22.9997 28.2288 22.9997 32.5C22.9997 36.7712 22.4749 40.9261 21.4831 44.9102Z" fill="white"/>
|
||||
<path d="M42.8753 32.5C42.8753 36.7712 43.4001 40.9261 44.3919 44.9102C46.9856 43.1868 49.7384 41.6639 52.625 40.365C51.8242 37.8772 51.3935 35.2363 51.3935 32.5C51.3935 29.7638 51.8242 27.123 52.625 24.635C49.7384 23.3361 46.9856 21.8132 44.3919 20.09C43.4001 24.074 42.8753 28.2288 42.8753 32.5Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const googleLogo = `
|
||||
<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M44.5624 18.0934H42.75V18H22.5V27H35.2159C33.3607 32.2391 28.3759 36 22.5 36C15.0446 36 9 29.9554 9 22.5C9 15.0446 15.0446 9 22.5 9C25.9414 9 29.0722 10.2982 31.4561 12.4189L37.8202 6.05475C33.8017 2.30962 28.4265 0 22.5 0C10.0744 0 0 10.0744 0 22.5C0 34.9256 10.0744 45 22.5 45C34.9256 45 45 34.9256 45 22.5C45 20.9914 44.8447 19.5187 44.5624 18.0934Z" fill="#FBC02D"/>
|
||||
<path d="M2.59421 12.0274L9.98658 17.4487C11.9868 12.4965 16.8311 9 22.5 9C25.9413 9 29.0722 10.2982 31.4561 12.4189L37.8202 6.05475C33.8017 2.30962 28.4265 0 22.5 0C13.8577 0 6.36296 4.87912 2.59421 12.0274Z" fill="#E53935"/>
|
||||
<path d="M22.4998 45C28.3116 45 33.5923 42.7759 37.585 39.159L30.6212 33.2663C28.3622 34.9774 25.5542 36 22.4998 36C16.6476 36 11.6785 32.2684 9.80646 27.0608L2.46921 32.7139C6.19296 40.0005 13.7552 45 22.4998 45Z" fill="#4CAF50"/>
|
||||
<path d="M44.5621 18.0934L44.5441 18H42.7498H22.4998V27H35.2156C34.3246 29.5166 32.7058 31.6867 30.6178 33.2674C30.6189 33.2662 30.62 33.2662 30.6211 33.2651L37.5849 39.1579C37.0921 39.6056 44.9998 33.75 44.9998 22.5C44.9998 20.9914 44.8445 19.5187 44.5621 18.0934Z" fill="#1565C0"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const twitchLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5104 45C34.9368 45 45.0104 34.9264 45.0104 22.5C45.0104 10.0736 34.9368 0 22.5104 0C10.084 0 0.010437 10.0736 0.010437 22.5C0.010437 34.9264 10.084 45 22.5104 45Z" fill="#6441A4"/>
|
||||
<path d="M13.2204 12.2168L11.7526 16.1323V31.7813H17.1315V34.7168H20.0671L22.9982 31.7813H27.4016L33.2683 25.9146V12.2168H13.2204ZM15.176 14.1724H31.3171V24.9346L27.8937 28.3579H22.5104L19.5793 31.2891V28.3579H15.176V14.1724ZM20.5549 23.9546H22.5104V18.0879H20.5549V23.9546ZM25.9338 23.9546H27.8893V18.0879H25.9338V23.9546Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const discordLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5208 45C34.9473 45 45.0208 34.9264 45.0208 22.5C45.0208 10.0736 34.9473 0 22.5208 0C10.0944 0 0.0208435 10.0736 0.0208435 22.5C0.0208435 34.9264 10.0944 45 22.5208 45Z" fill="#586CF5"/>
|
||||
<path d="M31.9325 13.8447C30.2018 13.0504 28.3451 12.4653 26.4045 12.1302C26.3693 12.124 26.3341 12.1401 26.3156 12.1722C26.0767 12.5968 25.8126 13.1504 25.6274 13.5861C23.5399 13.2738 21.4629 13.2738 19.4186 13.5861C19.2334 13.1411 18.9593 12.5968 18.7192 12.1722C18.7007 12.1407 18.6655 12.1247 18.6303 12.1302C16.6903 12.4641 14.8343 13.0492 13.1023 13.8447C13.0875 13.8508 13.0745 13.8619 13.0659 13.8761C9.54515 19.1356 8.5804 24.266 9.05382 29.3322C9.05567 29.3569 9.06987 29.381 9.089 29.3958C11.4117 31.1016 13.6622 32.1372 15.8706 32.8235C15.9058 32.834 15.9435 32.8216 15.9657 32.792C16.4879 32.0786 16.9539 31.3269 17.3533 30.5357C17.3767 30.4894 17.3545 30.4345 17.3063 30.4159C16.5675 30.1357 15.8645 29.7945 15.188 29.4063C15.1343 29.3748 15.13 29.2983 15.1793 29.2619C15.3219 29.1551 15.4639 29.044 15.6003 28.9323C15.625 28.9119 15.6589 28.9076 15.6879 28.9206C20.1327 30.9498 24.9447 30.9498 29.337 28.9206C29.366 28.9064 29.4 28.9107 29.4259 28.9311C29.5617 29.0434 29.7043 29.1551 29.8475 29.2619C29.8968 29.2983 29.8938 29.3748 29.8401 29.4063C29.1636 29.8019 28.4599 30.1357 27.7205 30.4147C27.6723 30.4332 27.6507 30.4888 27.6742 30.5357C28.0822 31.3256 28.5476 32.0773 29.0605 32.7908C29.0821 32.821 29.1204 32.834 29.1555 32.8228C31.3745 32.1366 33.625 31.101 35.9477 29.3952C35.968 29.3803 35.981 29.3575 35.9829 29.3328C36.5489 23.4754 35.0342 18.3876 31.9677 13.8768C31.9603 13.8619 31.9473 13.8508 31.9325 13.8447ZM18.0168 26.247C16.6786 26.247 15.5762 25.0189 15.5762 23.51C15.5762 22.001 16.6576 20.7729 18.0168 20.7729C19.3871 20.7729 20.479 22.0121 20.4574 23.51C20.458 25.0183 19.3766 26.247 18.0168 26.247ZM27.0415 26.247C25.7033 26.247 24.6009 25.0189 24.6009 23.51C24.6009 22.001 25.6823 20.7729 27.0415 20.7729C28.4118 20.7729 29.5037 22.0121 29.4821 23.51C29.4821 25.0183 28.4118 26.247 27.0415 26.247Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const bitbucketLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5313 45C34.9577 45 45.0313 34.9264 45.0313 22.5C45.0313 10.0736 34.9577 0 22.5313 0C10.1049 0 0.0312805 10.0736 0.0312805 22.5C0.0312805 34.9264 10.1049 45 22.5313 45Z" fill="#0052CC"/>
|
||||
<path d="M12.0107 12.3882C11.6151 12.3838 11.2856 12.7002 11.2812 13.1001C11.2812 13.144 11.2812 13.1836 11.2899 13.2275L14.3485 31.7988C14.4276 32.2646 14.8319 32.6118 15.3065 32.6162H29.9843C30.3402 32.6206 30.6479 32.3613 30.705 32.0098L33.7724 13.2275C33.8383 12.8364 33.5702 12.4673 33.1791 12.4014C33.1352 12.3926 33.0956 12.3926 33.0561 12.3926L12.0107 12.3882ZM24.8954 25.8091H20.2065L18.9408 19.1865H26.0292L24.8954 25.8091Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const linkedinLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5417 45C34.9681 45 45.0417 34.9264 45.0417 22.5C45.0417 10.0736 34.9681 0 22.5417 0C10.1153 0 0.0417023 10.0736 0.0417023 22.5C0.0417023 34.9264 10.1153 45 22.5417 45Z" fill="#0A66C2"/>
|
||||
<path d="M30.5001 30.4629H27.1602V25.2334C27.1602 23.9854 27.1339 22.3813 25.42 22.3813C23.6798 22.3813 23.4117 23.7393 23.4117 25.1411V30.4629H20.0719V19.7051H23.2799V21.1729H23.3238C23.772 20.3291 24.8619 19.437 26.4879 19.437C29.8717 19.437 30.4957 21.665 30.4957 24.561L30.5001 30.4629ZM16.3057 18.2329C15.2291 18.2329 14.3678 17.3628 14.3678 16.2949C14.3678 15.2271 15.2335 14.3569 16.3057 14.3569C17.378 14.3569 18.2437 15.2271 18.2437 16.2949C18.2437 17.3628 17.3736 18.2329 16.3057 18.2329ZM17.9801 30.4629H14.6314V19.7051H17.9801V30.4629ZM32.17 11.25H12.9571C12.0387 11.25 11.2916 11.9751 11.2916 12.876V32.168C11.2916 33.0645 12.0343 33.7939 12.9571 33.7939H32.1656C33.0841 33.7939 33.8355 33.0688 33.8355 32.168V12.876C33.8355 11.9751 33.0841 11.25 32.17 11.25V11.25Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const appleLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5521 45C34.9785 45 45.0521 34.9264 45.0521 22.5C45.0521 10.0736 34.9785 0 22.5521 0C10.1257 0 0.0521393 10.0736 0.0521393 22.5C0.0521393 34.9264 10.1257 45 22.5521 45Z" fill="#999999"/>
|
||||
<path d="M17.9465 31.7329C17.5026 31.4385 17.1115 31.0737 16.7819 30.6563C16.4216 30.2212 16.092 29.7642 15.7844 29.2939C15.0681 28.2437 14.5056 27.0967 14.1144 25.8882C13.6442 24.4819 13.4201 23.1328 13.4201 21.8145C13.4201 20.3423 13.7365 19.0591 14.3605 17.9824C14.8176 17.1387 15.5031 16.4355 16.3293 15.939C17.1247 15.4424 18.0519 15.1699 18.9924 15.1523C19.322 15.1523 19.6779 15.2007 20.0515 15.293C20.3239 15.3677 20.6491 15.4907 21.0534 15.6401C21.5676 15.8379 21.8488 15.9565 21.9455 15.9873C22.2443 16.1016 22.4992 16.1455 22.697 16.1455C22.8464 16.1455 23.0617 16.0972 23.3034 16.0225C23.4396 15.9741 23.6989 15.8906 24.0637 15.7324C24.424 15.6006 24.714 15.4907 24.9382 15.4028C25.2853 15.3018 25.6193 15.2051 25.9225 15.1611C26.2873 15.104 26.652 15.0864 26.9992 15.1128C27.6628 15.1611 28.2736 15.3018 28.8185 15.5083C29.7722 15.8906 30.5456 16.4927 31.1213 17.3452C30.8796 17.4946 30.6511 17.6704 30.4401 17.8594C29.9831 18.2637 29.5964 18.7383 29.2888 19.27C28.8845 19.9907 28.6779 20.8081 28.6867 21.6299C28.6999 22.645 28.9592 23.5371 29.4733 24.3105C29.8381 24.873 30.3215 25.3564 30.9103 25.7476C31.2004 25.9453 31.4553 26.0815 31.697 26.1694C31.5827 26.521 31.4597 26.8638 31.319 27.2021C30.9938 27.958 30.6071 28.6831 30.1457 29.3687C29.7414 29.9575 29.4206 30.4014 29.1789 30.6914C28.801 31.1397 28.4406 31.478 28.0715 31.7197C27.6672 31.9878 27.197 32.1284 26.7092 32.1284C26.3796 32.1416 26.0544 32.1021 25.7424 32.0098C25.4699 31.9219 25.2018 31.8208 24.9382 31.7065C24.6657 31.5791 24.3801 31.4736 24.09 31.3901C23.7341 31.2979 23.3693 31.2495 23.0002 31.2539C22.6266 31.2539 22.2619 31.3022 21.9147 31.3901C21.6247 31.4736 21.3434 31.5747 21.0666 31.6934C20.6711 31.856 20.4162 31.9658 20.2668 32.0098C19.9636 32.0977 19.6515 32.1548 19.3395 32.1724C18.8517 32.1724 18.3991 32.0317 17.9465 31.7505V31.7329ZM24.3669 14.436C23.7297 14.7524 23.1232 14.8887 22.5168 14.8447C22.4245 14.2383 22.5168 13.6187 22.7717 12.9375C22.9958 12.3574 23.2946 11.8301 23.7077 11.3643C24.1384 10.8765 24.6525 10.4722 25.237 10.1821C25.8566 9.86572 26.4455 9.69434 27.008 9.66797C27.0827 10.3052 27.008 10.9336 26.7751 11.606C26.5598 12.2036 26.2433 12.7573 25.839 13.2539C25.4304 13.7417 24.925 14.146 24.3537 14.436H24.3669Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const twitterLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5626 45C34.989 45 45.0626 34.9264 45.0626 22.5C45.0626 10.0736 34.989 0 22.5626 0C10.1362 0 0.062561 10.0736 0.062561 22.5C0.062561 34.9264 10.1362 45 22.5626 45Z" fill="#1DA1F2"/>
|
||||
<path d="M34.2521 15.5918C33.4259 15.9565 32.5426 16.2026 31.611 16.3169C32.5602 15.7456 33.2897 14.8447 33.6325 13.7725C32.7448 14.291 31.756 14.6689 30.7101 14.8799C29.8708 13.9834 28.6799 13.4209 27.3527 13.4209C24.8127 13.4209 22.7516 15.4819 22.7516 18.0176C22.7516 18.3823 22.7956 18.7339 22.8703 19.0679C19.047 18.8877 15.6545 17.0508 13.3869 14.2734C12.987 14.9502 12.7629 15.7324 12.7629 16.5894C12.7629 18.189 13.5759 19.5952 14.8107 20.4214C14.0549 20.395 13.3473 20.1885 12.7277 19.8457V19.9028C12.7277 22.1353 14.3097 23.9941 16.4191 24.416C16.0324 24.5215 15.6237 24.5742 15.2062 24.5742C14.9118 24.5742 14.6305 24.5479 14.3493 24.4951C14.9382 26.3232 16.6344 27.6548 18.6559 27.6899C17.0871 28.9248 15.092 29.6587 12.9518 29.6587C12.5871 29.6587 12.2223 29.6367 11.8576 29.5972C13.9054 30.9023 16.318 31.6626 18.924 31.6626C27.3835 31.6626 32.0109 24.6533 32.0109 18.5845C32.0109 18.3911 32.0109 18.1934 31.9977 17.9956C32.8942 17.3496 33.6808 16.5366 34.2961 15.6138L34.2521 15.5918Z" fill="white"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const facebookLogo = `
|
||||
<svg width="46" height="45" viewBox="0 0 46 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.573 0C10.1462 0 0.0729866 10.0783 0.0729866 22.5114C0.0729866 33.7986 8.37436 43.1442 19.2025 44.7717C20.3016 44.9372 21.4277 41.646 22.573 41.646C23.6069 41.646 24.625 44.9529 25.6229 44.8179C36.6074 43.3287 45.073 33.91 45.073 22.5114C45.073 10.0783 34.9997 0 22.573 0Z" fill="url(#paint0_linear_4_56)"/>
|
||||
<path opacity="0.05" d="M26.7561 19.3289C26.7561 17.7277 27.0989 15.8196 28.731 15.8196H33.5454L33.5443 9.50388L33.4881 8.54589L32.5371 8.41771C31.8537 8.32551 30.4317 8.13324 27.8093 8.13324C21.5494 8.13324 18.1019 11.8606 18.1019 18.6284V21.4371H12.5401V29.598H18.1019V44.5502C19.552 44.8437 21.0526 45 22.5892 45C24.0145 45 25.4038 44.8527 26.7561 44.5986V29.5969H32.4135L33.6747 21.436H26.7561V19.3289Z" fill="black"/>
|
||||
<path opacity="0.05" d="M26.1865 19.3091C26.1865 17.7867 26.5164 15.2346 28.7275 15.2346H32.9865V9.50924L32.9572 9.01412L32.4697 8.94773C31.7976 8.85659 30.4005 8.66754 27.8055 8.66754C21.8127 8.66754 18.6458 12.1052 18.6458 18.6081V21.9816H13.0741V29.0235H18.6447V44.6354C19.9236 44.8605 21.2318 45 22.576 45C23.8077 45 25.0078 44.874 26.1865 44.6838V29.0224H31.9327L33.0214 21.9805H26.1854V19.3091H26.1865Z" fill="black"/>
|
||||
<path d="M25.6374 28.4741H31.4564L32.3704 22.5626H25.6374V19.3314C25.6374 16.876 26.4401 14.6982 28.7369 14.6982H32.4277V9.54002C31.7791 9.45233 30.4075 9.2612 27.8162 9.2612C22.4042 9.2612 19.2316 12.1191 19.2316 18.631V22.5637H13.6678V28.4752H19.2305V44.7493C20.3322 44.9134 21.4486 45 22.5942 45C23.6296 45 24.6403 44.9303 25.6374 44.7954V28.4741Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_4_56" x1="6.81511" y1="6.74553" x2="41.2823" y2="41.1953" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2AA4F4"/>
|
||||
<stop offset="1" stop-color="#007AD9"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const githubLogo = `
|
||||
<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 22.5C0 10.0736 10.0736 0 22.5 0V0C34.9264 0 45 10.0736 45 22.5V22.5C45 34.9264 34.9264 45 22.5 45V45C10.0736 45 0 34.9264 0 22.5V22.5Z" fill="black"/>
|
||||
<rect x="7" y="4" width="31" height="32" rx="15.5" fill="white"/>
|
||||
<path d="M22.3626 4.00406C13.3264 4.00406 6 11.2809 6 20.2562C6 27.4376 10.6879 33.5281 17.19 35.6774C18.0081 35.8277 18.3088 35.3239 18.3088 34.8953L18.3088 34.8912C18.3087 34.4984 18.2924 33.2227 18.2863 31.8703C13.7334 32.8536 12.7741 29.9526 12.7741 29.9526C12.0296 28.0734 10.9579 27.5737 10.9579 27.5737C9.47296 26.564 11.0704 26.5864 11.0704 26.5864C12.7128 26.7001 13.5779 28.2603 13.5779 28.2603C15.0383 30.7449 17.4068 30.0257 18.3395 29.6113C18.4867 28.561 18.9101 27.8439 19.3785 27.4376C15.7439 27.0272 11.9233 25.6336 11.9233 19.405C11.9233 17.6315 12.5635 16.181 13.6086 15.0433C13.4389 14.6329 12.8784 12.9813 13.7661 10.7426C13.7661 10.7426 15.1406 10.3058 18.2679 12.4084C19.5728 12.0489 20.9718 11.8681 22.3626 11.862C23.7534 11.8681 25.1545 12.0489 26.4614 12.4084C29.5847 10.3058 30.9571 10.7426 30.9571 10.7426C31.8468 12.9813 31.2884 14.635 31.1187 15.0433C32.1679 16.181 32.802 17.6315 32.802 19.405C32.802 25.6478 28.9731 27.0211 25.3304 27.4234C25.9174 27.9272 26.441 28.9165 26.441 30.432C26.441 32.6058 26.4226 34.3569 26.4226 34.8912C26.4226 35.3239 26.7171 35.8298 27.5475 35.6713C34.0455 33.5199 38.7273 27.4295 38.7273 20.2521C38.7273 11.2769 31.4009 4 22.3647 4L22.3626 4.00406Z" fill="black"/>
|
||||
</svg>
|
||||
|
||||
`;
|
||||
|
||||
export const gitlabLogo = `
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.5 48C35.9264 48 46 37.9264 46 25.5C46 13.0736 35.9264 3 23.5 3C11.0736 3 1 13.0736 1 25.5C1 37.9264 11.0736 48 23.5 48Z" fill="#E8E8E8"/>
|
||||
<path d="M24 39L18.2857 22.6579H29.7143L24 39Z" fill="#E53935"/>
|
||||
<path d="M24 39L36.8571 22.6579H29.7143L24 39Z" fill="#FF7043"/>
|
||||
<path d="M33.2857 12L36.8571 22.6579H29.7143L33.2857 12Z" fill="#E53935"/>
|
||||
<path d="M24 39L36.8571 22.6579L39 28.3421L24 39Z" fill="#FFA726"/>
|
||||
<path d="M24 39L11.1429 22.6579H18.2857L24 39Z" fill="#FF7043"/>
|
||||
<path d="M14.7143 12L11.1429 22.6579H18.2857L14.7143 12Z" fill="#E53935"/>
|
||||
<path d="M24 39L11.1429 22.6579L9 28.3421L24 39Z" fill="#FFA726"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
export const microsoftLogo = `
|
||||
<svg width="45" height="45" viewBox="0 0 45 45" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.5 45C34.9264 45 45 34.9264 45 22.5C45 10.0736 34.9264 0 22.5 0C10.0736 0 0 10.0736 0 22.5C0 34.9264 10.0736 45 22.5 45Z" fill="#E8E8E8"/>
|
||||
<path d="M20.7609 21.7391H10.7609L10.7609 11.7391L20.7609 11.7391V21.7391Z" fill="#FF5722"/>
|
||||
<path d="M33.2609 21.7391H23.2609V11.7391L33.2609 11.7391V21.7391Z" fill="#4CAF50"/>
|
||||
<path d="M33.2609 34.2391H23.2609V24.2391H33.2609V34.2391Z" fill="#FFC107"/>
|
||||
<path d="M20.7609 34.2391H10.7609L10.7609 24.2391H20.7609V34.2391Z" fill="#03A9F4"/>
|
||||
</svg>
|
||||
`;
|
||||
@@ -0,0 +1,187 @@
|
||||
export const modalStyles = (accentColor = "#6452f7") => `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap');
|
||||
.Magic__formOverlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(6px);
|
||||
z-index: 2147483647;
|
||||
}
|
||||
.Magic__formOverlay.Magic__dark {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
.Magic__formContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: 'Inter', sans-serif;
|
||||
text-align: center;
|
||||
gap: 30px;
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
transition: all 0.2s ease-in-out;
|
||||
width: min(400px, 90%);
|
||||
z-index: 9999;
|
||||
box-shadow: 0 12px 56px rgb(119 118 122 / 15%);
|
||||
border-radius: 30px;
|
||||
padding: 80px 20px;
|
||||
background-color: #fff;
|
||||
}
|
||||
.Magic__formContainer.Magic__dark {
|
||||
background-color: #333333;
|
||||
box-shadow: 0 12px 56px #00000021;
|
||||
color: white;
|
||||
}
|
||||
.Magic__closeButton {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 15px;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
font-size: 30px;
|
||||
color: #ccc;
|
||||
z-index: 9999;
|
||||
}
|
||||
.Magic__dark .Magic__closeButton {
|
||||
color: #555;
|
||||
}
|
||||
.Magic__formHeader{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
.Magic__customLogo{
|
||||
height: 80px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.Magic__logoText{
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.Magic__dark .Magic__logoText{
|
||||
color: white;
|
||||
}
|
||||
.Magic__formBody{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.Magic__formLabel{
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.Magic__formInput {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
max-width: 300px;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: #D6D6D6;
|
||||
color: #333;
|
||||
font-size: 17px;
|
||||
font-weight: 400;
|
||||
border-radius: 10px;
|
||||
accent-color: ${accentColor};
|
||||
background-color: transparent;
|
||||
}
|
||||
.Magic__dark .Magic__formInput {
|
||||
border-color: #444;
|
||||
color: white;
|
||||
}
|
||||
.Magic__formInput::placeholder {
|
||||
color: #D6D6D6;
|
||||
opacity: 1;
|
||||
}
|
||||
.Magic__dark .Magic__formInput::placeholder {
|
||||
color: #555;
|
||||
}
|
||||
.Magic__divider {
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: #D6D6D6;
|
||||
font-size: 14px;
|
||||
}
|
||||
.Magic__dark .Magic__divider {
|
||||
color: #444;
|
||||
}
|
||||
.Magic__submitButton {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
padding: 15px 30px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
margin-bottom: 10px;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
border-radius: 100px;
|
||||
background-color: ${accentColor};
|
||||
}
|
||||
.Magic__submitButton:hover {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.4) 0 0)
|
||||
}
|
||||
.Magic__oauthButtonsContainer{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
width: 90%;
|
||||
}
|
||||
.Magic__oauthButton{
|
||||
display: block;
|
||||
padding: 5px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 100px;
|
||||
}
|
||||
.Magic__aloneOauthContainer > .Magic__oauthButton{
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
border: 1px solid #D6D6D6;
|
||||
color: #333;
|
||||
}
|
||||
.Magic__dark .Magic__aloneOauthContainer > .Magic__oauthButton{
|
||||
border-color: #444;
|
||||
color: white;
|
||||
}
|
||||
.Magic__aloneOauthContainer .Magic__oauthButtonIcon > svg{
|
||||
width: 30px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
.Magic__aloneOauthContainer .Magic__oauthButtonName{
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 16px;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,214 @@
|
||||
import {
|
||||
appleLogo,
|
||||
bitbucketLogo,
|
||||
discordLogo,
|
||||
facebookLogo,
|
||||
githubLogo,
|
||||
gitlabLogo,
|
||||
googleLogo,
|
||||
linkedinLogo,
|
||||
MagicLogo,
|
||||
microsoftLogo,
|
||||
twitchLogo,
|
||||
twitterLogo,
|
||||
} from "./logos";
|
||||
import { modalStyles } from "./styles";
|
||||
import { OAuthProvider } from "@magic-ext/oauth";
|
||||
|
||||
export const createModal = async (props: {
|
||||
accentColor?: string;
|
||||
isDarkMode?: boolean;
|
||||
customLogo?: string;
|
||||
customHeaderText?: string;
|
||||
enableSMSLogin?: boolean;
|
||||
enableEmailLogin?: boolean;
|
||||
oauthProviders?: OAuthProvider[];
|
||||
}) => {
|
||||
// INJECT FORM STYLES
|
||||
const style = document.createElement("style");
|
||||
style.innerHTML = modalStyles(props.accentColor);
|
||||
document.head.appendChild(style);
|
||||
|
||||
// PROVIDERS FOR OAUTH BUTTONS
|
||||
const allProviders = [
|
||||
{ name: "google", icon: googleLogo },
|
||||
{ name: "facebook", icon: facebookLogo },
|
||||
{ name: "apple", icon: appleLogo },
|
||||
{ name: "github", icon: githubLogo },
|
||||
{ name: "bitbucket", icon: bitbucketLogo },
|
||||
{ name: "gitlab", icon: gitlabLogo },
|
||||
{ name: "linkedin", icon: linkedinLogo },
|
||||
{ name: "twitter", icon: twitterLogo },
|
||||
{ name: "discord", icon: discordLogo },
|
||||
{ name: "twitch", icon: twitchLogo },
|
||||
{ name: "microsoft", icon: microsoftLogo },
|
||||
];
|
||||
|
||||
// make providers included in oauthProviders
|
||||
const providers = props.oauthProviders
|
||||
?.map((provider) => {
|
||||
return allProviders.find((p) => p.name === provider);
|
||||
})
|
||||
.filter((p) => p !== undefined);
|
||||
|
||||
const phoneNumberRegex = `(\\+|00)[0-9]{1,3}[0-9]{4,14}(?:x.+)?$`;
|
||||
const emailRegex = `^([a-zA-Z0-9_.-])+(\\+[a-zA-Z0-9-]+)?@(([a-zA-Z0-9-])+.)+([a-zA-Z0-9]{2,4})`;
|
||||
|
||||
// MODAL HTML
|
||||
const modal = `
|
||||
<div class="Magic__formContainer ${
|
||||
props.isDarkMode ? `Magic__dark` : ``
|
||||
}" id="MagicModalBody">
|
||||
<button class="Magic__closeButton" id="MagicCloseBtn">×</button>
|
||||
<div class="Magic__formHeader">
|
||||
${
|
||||
props.customLogo
|
||||
? `<img class="Magic__customLogo" src="${props.customLogo}" />`
|
||||
: `<div class="Magic__logo">${MagicLogo}</div>`
|
||||
}
|
||||
<h1 class='Magic__logoText'> ${props.customHeaderText || "Magic"} </h1>
|
||||
|
||||
<form class="Magic__formBody" id="MagicForm">
|
||||
${
|
||||
props.enableSMSLogin && props.enableEmailLogin
|
||||
? `
|
||||
<label class="Magic__FormLabel">Sign-in with Phone or Email</label>
|
||||
<input class="Magic__formInput" id="MagicFormInput" required pattern = "${emailRegex}|${phoneNumberRegex}" placeholder="Phone or Email" />
|
||||
`
|
||||
: props.enableEmailLogin
|
||||
? `
|
||||
<label class="Magic__FormLabel">Sign-in with Email</label>
|
||||
<input class="Magic__formInput" id="MagicFormInput" required type="email" placeholder="[email protected]" />
|
||||
`
|
||||
: props.enableSMSLogin
|
||||
? `
|
||||
<label class="Magic__FormLabel">Sign-in with Phone</label>
|
||||
<input class="Magic__formInput" id="MagicFormInput" required type="tel" pattern="${phoneNumberRegex}" placeholder="+11234567890" />
|
||||
`
|
||||
: ``
|
||||
}
|
||||
${
|
||||
props.enableSMSLogin || props.enableEmailLogin
|
||||
? ` <button class="Magic__submitButton" type="submit">
|
||||
Send login link
|
||||
</button>`
|
||||
: ``
|
||||
}
|
||||
</form>
|
||||
${
|
||||
providers &&
|
||||
providers.length > 0 &&
|
||||
(props.enableSMSLogin || props.enableEmailLogin)
|
||||
? `<div class="Magic__divider">
|
||||
⎯⎯⎯ OR ⎯⎯⎯
|
||||
</div>`
|
||||
: ``
|
||||
}
|
||||
<div class="${
|
||||
!props.enableEmailLogin && !props.enableSMSLogin
|
||||
? `Magic__oauthButtonsContainer Magic__aloneOauthContainer`
|
||||
: `Magic__oauthButtonsContainer`
|
||||
}">
|
||||
${providers
|
||||
?.map((provider) => {
|
||||
return `
|
||||
<button class="Magic__oauthButton" id="MagicOauth${
|
||||
provider?.name
|
||||
}" data-provider="${provider?.name}" >
|
||||
<span class="Magic__oauthButtonIcon">${provider?.icon}</span>
|
||||
${
|
||||
!props.enableSMSLogin && !props.enableEmailLogin
|
||||
? `<span class="Magic__oauthButtonName">${provider?.name}</span>`
|
||||
: ``
|
||||
}
|
||||
</button>
|
||||
`;
|
||||
})
|
||||
.join("")}
|
||||
</div>
|
||||
${
|
||||
!props.enableEmailLogin &&
|
||||
!props.enableEmailLogin &&
|
||||
providers?.length === 0
|
||||
? `<div>No Login Methods Configured 😥</div>`
|
||||
: ``
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// ADD FORM TO BODY
|
||||
const overlay = document.createElement("div");
|
||||
overlay.classList.add("Magic__formOverlay");
|
||||
if (props.isDarkMode) {
|
||||
overlay.classList.add("Magic__dark");
|
||||
}
|
||||
document.body.appendChild(overlay);
|
||||
overlay.innerHTML = modal;
|
||||
const formBody = document.getElementById("MagicModalBody");
|
||||
setTimeout(() => {
|
||||
if (formBody) {
|
||||
formBody.style.transform = "translate(-50%, -50%) scale(1)";
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// FORM SUBMIT HANDLER
|
||||
const removeForm = () => {
|
||||
if (formBody) {
|
||||
formBody.style.transform = "translate(-50%, -50%) scale(0)";
|
||||
}
|
||||
setTimeout(() => {
|
||||
overlay.remove();
|
||||
}, 200);
|
||||
};
|
||||
|
||||
return new Promise((resolve) => {
|
||||
// FORM CLOSE BUTTON
|
||||
document.getElementById("MagicCloseBtn")?.addEventListener("click", () => {
|
||||
removeForm();
|
||||
resolve({
|
||||
email: "",
|
||||
phoneNumber: "",
|
||||
isGoogle: false,
|
||||
isDiscord: false,
|
||||
});
|
||||
});
|
||||
|
||||
// EMAIL FORM SUBMIT HANDLER
|
||||
document.getElementById("MagicForm")?.addEventListener("submit", (e) => {
|
||||
e.preventDefault();
|
||||
const formInputField = document.getElementById(
|
||||
"MagicFormInput",
|
||||
) as HTMLInputElement;
|
||||
const isFormValid = formInputField?.checkValidity();
|
||||
if (isFormValid) {
|
||||
let output;
|
||||
if (RegExp(phoneNumberRegex).test(formInputField.value)) {
|
||||
output = {
|
||||
phoneNumber: formInputField?.value,
|
||||
};
|
||||
} else {
|
||||
output = {
|
||||
email: formInputField?.value,
|
||||
};
|
||||
}
|
||||
removeForm();
|
||||
resolve(output);
|
||||
}
|
||||
});
|
||||
|
||||
// OAUTH BUTTONS
|
||||
providers?.forEach((provider) => {
|
||||
const oauthButton = document.getElementById(
|
||||
`MagicOauth${provider?.name}`,
|
||||
);
|
||||
oauthButton?.addEventListener("click", () => {
|
||||
const output = {
|
||||
oauthProvider: provider?.name as OAuthProvider,
|
||||
};
|
||||
removeForm();
|
||||
resolve(output);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
import type { Address } from "abitype";
|
||||
import { getAddress } from "ethers/lib/utils.js";
|
||||
|
||||
declare interface Window {
|
||||
ethereum: Ethereum;
|
||||
function isWindoWethereum(w: Window): w is Window & { ethereum: Ethereum } {
|
||||
return "ethereum" in window;
|
||||
}
|
||||
|
||||
export type MetaMaskConnectorOptions = Pick<
|
||||
@@ -28,11 +28,7 @@ export type MetaMaskConnectorOptions = Pick<
|
||||
|
||||
export class MetaMaskConnector extends InjectedConnector {
|
||||
readonly id = "metaMask";
|
||||
readonly ready =
|
||||
typeof window !== "undefined" &&
|
||||
!!this.#findProvider(window.ethereum as Ethereum);
|
||||
|
||||
#provider?: Window["ethereum"];
|
||||
#UNSTABLE_shimOnConnectSelectAccount: MetaMaskConnectorOptions["UNSTABLE_shimOnConnectSelectAccount"];
|
||||
|
||||
constructor({
|
||||
@@ -46,6 +42,46 @@ export class MetaMaskConnector extends InjectedConnector {
|
||||
name: "MetaMask",
|
||||
shimDisconnect: true,
|
||||
shimChainChangedDisconnect: true,
|
||||
getProvider() {
|
||||
function getReady(ethereum?: Ethereum) {
|
||||
const isMetaMask = !!ethereum?.isMetaMask;
|
||||
if (!isMetaMask) {
|
||||
return;
|
||||
}
|
||||
// Brave tries to make itself look like MetaMask
|
||||
// Could also try RPC `web3_clientVersion` if following is unreliable
|
||||
if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isAvalanche) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isKuCoinWallet) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isPortal) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isTokenPocket) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isTokenary) {
|
||||
return;
|
||||
}
|
||||
return ethereum;
|
||||
}
|
||||
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
if (isWindoWethereum(window)) {
|
||||
if (window.ethereum?.providers) {
|
||||
return window.ethereum.providers.find(getReady);
|
||||
}
|
||||
|
||||
return getReady(window.ethereum);
|
||||
}
|
||||
},
|
||||
...options_,
|
||||
};
|
||||
super({ chains, options });
|
||||
@@ -75,18 +111,24 @@ export class MetaMaskConnector extends InjectedConnector {
|
||||
if (
|
||||
this.#UNSTABLE_shimOnConnectSelectAccount &&
|
||||
this.options?.shimDisconnect &&
|
||||
(await getConnectorStorage().getItem(this.shimDisconnectKey))
|
||||
!(await getConnectorStorage().getItem(this.shimDisconnectKey))
|
||||
) {
|
||||
account = await this.getAccount().catch(() => null);
|
||||
const isConnected = !!account;
|
||||
if (isConnected) {
|
||||
// Attempt to show another prompt for selecting wallet if already connected
|
||||
await provider
|
||||
.request({
|
||||
try {
|
||||
await provider.request({
|
||||
method: "wallet_requestPermissions",
|
||||
params: [{ eth_accounts: {} }],
|
||||
})
|
||||
.catch(() => null);
|
||||
});
|
||||
} catch (error) {
|
||||
// Not all MetaMask injected providers support `wallet_requestPermissions` (e.g. MetaMask iOS).
|
||||
// Only bubble up error if user rejects request
|
||||
if (this.isUserRejectedRequestError(error)) {
|
||||
throw new UserRejectedRequestError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,48 +163,4 @@ export class MetaMaskConnector extends InjectedConnector {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getProvider() {
|
||||
if (typeof window !== "undefined") {
|
||||
// TODO: Fallback to `ethereum#initialized` event for async injection
|
||||
// https://github.com/MetaMask/detect-provider#synchronous-and-asynchronous-injection=
|
||||
this.#provider = this.#findProvider(window.ethereum as Ethereum);
|
||||
}
|
||||
return this.#provider as Ethereum;
|
||||
}
|
||||
|
||||
#getReady(ethereum?: Ethereum) {
|
||||
const isMetaMask = !!ethereum?.isMetaMask;
|
||||
if (!isMetaMask) {
|
||||
return;
|
||||
}
|
||||
// Brave tries to make itself look like MetaMask
|
||||
// Could also try RPC `web3_clientVersion` if following is unreliable
|
||||
if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isAvalanche) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isKuCoinWallet) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isPortal) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isTokenPocket) {
|
||||
return;
|
||||
}
|
||||
if (ethereum.isTokenary) {
|
||||
return;
|
||||
}
|
||||
return ethereum;
|
||||
}
|
||||
|
||||
#findProvider(ethereum?: Ethereum) {
|
||||
if (ethereum?.providers) {
|
||||
return ethereum.providers.find(this.#getReady);
|
||||
}
|
||||
return this.#getReady(ethereum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,10 @@ export class WalletConnectConnector extends Connector<
|
||||
if (!this.#provider || chainId || create) {
|
||||
const rpc = !this.options?.infuraId
|
||||
? this.chains.reduce(
|
||||
(rpc_, chain) => ({ ...rpc_, [chain.id]: chain.rpcUrls.default }),
|
||||
(rpc_, chain) => ({
|
||||
...rpc_,
|
||||
[chain.id]: chain.rpcUrls.default.http[0],
|
||||
}),
|
||||
{},
|
||||
)
|
||||
: {};
|
||||
@@ -181,7 +184,8 @@ export class WalletConnectConnector extends Connector<
|
||||
id: chainId,
|
||||
name: `Chain ${id}`,
|
||||
network: `${id}`,
|
||||
rpcUrls: { default: "" },
|
||||
nativeCurrency: { name: "Ether", decimals: 18, symbol: "ETH" },
|
||||
rpcUrls: { default: { http: [""] } },
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import {
|
||||
Chain,
|
||||
etherscanBlockExplorers,
|
||||
alchemyRpcUrls,
|
||||
infuraRpcUrls,
|
||||
} from "@wagmi/core";
|
||||
arbitrum,
|
||||
arbitrumGoerli,
|
||||
avalanche,
|
||||
avalancheFuji,
|
||||
bsc,
|
||||
bscTestnet,
|
||||
fantom,
|
||||
fantomTestnet,
|
||||
goerli,
|
||||
mainnet,
|
||||
optimism,
|
||||
optimismGoerli,
|
||||
polygon,
|
||||
polygonMumbai,
|
||||
} from "@wagmi/chains";
|
||||
import type { Chain } from "@wagmi/core";
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -64,345 +75,89 @@ export const SUPPORTED_CHAIN_IDS: SUPPORTED_CHAIN_ID[] = [
|
||||
ChainId.BinanceSmartChainTestnet,
|
||||
];
|
||||
|
||||
type NativeCurrency = {
|
||||
name: string;
|
||||
/** 2-6 characters long */
|
||||
symbol: string;
|
||||
decimals: 18;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const NATIVE_TOKEN_ADDRESS =
|
||||
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const NATIVE_TOKENS: Record<SUPPORTED_CHAIN_ID, NativeCurrency> = {
|
||||
[ChainId.Mainnet]: {
|
||||
name: "Ether",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Goerli]: {
|
||||
name: "Görli Ether",
|
||||
symbol: "GOR",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Polygon]: {
|
||||
name: "Matic",
|
||||
symbol: "MATIC",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Mumbai]: {
|
||||
name: "Matic",
|
||||
symbol: "MATIC",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Avalanche]: {
|
||||
name: "Avalanche",
|
||||
symbol: "AVAX",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.AvalancheFujiTestnet]: {
|
||||
name: "Avalanche",
|
||||
symbol: "AVAX",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Fantom]: {
|
||||
name: "Fantom",
|
||||
symbol: "FTM",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.FantomTestnet]: {
|
||||
name: "Fantom",
|
||||
symbol: "FTM",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Arbitrum]: {
|
||||
name: "Ether",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
},
|
||||
|
||||
[ChainId.ArbitrumGoerli]: {
|
||||
name: "Arbitrum Goerli Ether",
|
||||
symbol: "AGOR",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.Optimism]: {
|
||||
name: "Ether",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
},
|
||||
|
||||
[ChainId.OptimismGoerli]: {
|
||||
name: "Goerli Ether",
|
||||
symbol: "ETH",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.BinanceSmartChainMainnet]: {
|
||||
name: "Binance Chain Native Token",
|
||||
symbol: "BNB",
|
||||
decimals: 18,
|
||||
},
|
||||
[ChainId.BinanceSmartChainTestnet]: {
|
||||
name: "Binance Chain Native Token",
|
||||
symbol: "TBNB",
|
||||
decimals: 18,
|
||||
},
|
||||
};
|
||||
|
||||
export const DEFAULT_API_KEY =
|
||||
"c6634ad2d97b74baf15ff556016830c251050e6c36b9da508ce3ec80095d3dc1";
|
||||
|
||||
export function getRpcUrl(network: string) {
|
||||
return `https://${network}.rpc.thirdweb.com/${DEFAULT_API_KEY}`;
|
||||
function getRpcNameFromChainId(chainId: SUPPORTED_CHAIN_ID): string {
|
||||
switch (chainId) {
|
||||
case ChainId.Mainnet:
|
||||
return "mainnet";
|
||||
case ChainId.Goerli:
|
||||
return "goerli";
|
||||
case ChainId.Polygon:
|
||||
return "polygon";
|
||||
case ChainId.Mumbai:
|
||||
return "mumbai";
|
||||
case ChainId.Avalanche:
|
||||
return "avalanche";
|
||||
case ChainId.AvalancheFujiTestnet:
|
||||
return "avalanche-fuji";
|
||||
case ChainId.Fantom:
|
||||
return "fantom";
|
||||
case ChainId.FantomTestnet:
|
||||
return "fantom-testnet";
|
||||
case ChainId.Arbitrum:
|
||||
return "arbitrum";
|
||||
case ChainId.ArbitrumGoerli:
|
||||
return "arbitrum-goerli";
|
||||
case ChainId.Optimism:
|
||||
return "optimism";
|
||||
case ChainId.OptimismGoerli:
|
||||
return "optimism-goerli";
|
||||
case ChainId.BinanceSmartChainMainnet:
|
||||
return "bsc";
|
||||
case ChainId.BinanceSmartChainTestnet:
|
||||
return "bsc-testnet";
|
||||
default:
|
||||
throw new Error("Unsupported chain id");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the native token for a given chain
|
||||
* @param chainId - the chain id
|
||||
* @public
|
||||
*/
|
||||
export function getNativeTokenByChainId(chainId: ChainId): NativeCurrency {
|
||||
return NATIVE_TOKENS[chainId as SUPPORTED_CHAIN_ID];
|
||||
export function getRpcUrl(chainId: SUPPORTED_CHAIN_ID) {
|
||||
return `https://${getRpcNameFromChainId(
|
||||
chainId,
|
||||
)}.rpc.thirdweb.com/${DEFAULT_API_KEY}`;
|
||||
}
|
||||
|
||||
function enhanceChain<TChain extends Chain>(chain: TChain) {
|
||||
const twRPC = getRpcUrl(chain.id);
|
||||
return {
|
||||
...chain,
|
||||
rpcUrls: {
|
||||
...chain.rpcUrls,
|
||||
default: {
|
||||
...chain.rpcUrls.default,
|
||||
http: [twRPC, ...chain.rpcUrls.default.http],
|
||||
},
|
||||
public: {
|
||||
...chain.rpcUrls.public,
|
||||
http: [twRPC, ...(chain.rpcUrls.public?.http || [])],
|
||||
},
|
||||
},
|
||||
} as TChain;
|
||||
}
|
||||
|
||||
const supportedChains: Record<SUPPORTED_CHAIN_ID, Chain> = {
|
||||
[ChainId.Mainnet]: {
|
||||
id: ChainId.Mainnet,
|
||||
name: "Ethereum",
|
||||
network: "homestead",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Mainnet),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.mainnet,
|
||||
default: getRpcUrl("ethereum"),
|
||||
infura: infuraRpcUrls.mainnet,
|
||||
public: getRpcUrl("ethereum"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.mainnet,
|
||||
default: etherscanBlockExplorers.mainnet,
|
||||
},
|
||||
},
|
||||
[ChainId.Goerli]: {
|
||||
id: ChainId.Goerli,
|
||||
name: "Goerli",
|
||||
network: "goerli",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Goerli),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.goerli,
|
||||
default: getRpcUrl("goerli"),
|
||||
infura: infuraRpcUrls.goerli,
|
||||
public: getRpcUrl("goerli"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.goerli,
|
||||
default: etherscanBlockExplorers.goerli,
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.Polygon]: {
|
||||
id: ChainId.Polygon,
|
||||
name: "Polygon",
|
||||
network: "matic",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Polygon),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.polygon,
|
||||
default: getRpcUrl("polygon"),
|
||||
infura: infuraRpcUrls.polygon,
|
||||
public: getRpcUrl("polygon"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.polygon,
|
||||
default: etherscanBlockExplorers.polygon,
|
||||
},
|
||||
},
|
||||
[ChainId.Mumbai]: {
|
||||
id: ChainId.Mumbai,
|
||||
name: "Mumbai",
|
||||
network: "maticmum",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Mumbai),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.polygonMumbai,
|
||||
default: getRpcUrl("mumbai"),
|
||||
infura: infuraRpcUrls.polygonMumbai,
|
||||
public: getRpcUrl("mumbai"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.polygonMumbai,
|
||||
default: etherscanBlockExplorers.polygonMumbai,
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.Avalanche]: {
|
||||
id: ChainId.Avalanche,
|
||||
name: "Avalanche",
|
||||
network: "avalanche",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Avalanche),
|
||||
rpcUrls: {
|
||||
default: getRpcUrl("avalanche"),
|
||||
public: getRpcUrl("avalanche"),
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "SnowTrace",
|
||||
url: "https://snowtrace.io/",
|
||||
},
|
||||
},
|
||||
},
|
||||
[ChainId.AvalancheFujiTestnet]: {
|
||||
id: ChainId.AvalancheFujiTestnet,
|
||||
name: "Avalanche Fuji Testnet",
|
||||
network: "avalanche-fuji",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.AvalancheFujiTestnet),
|
||||
rpcUrls: {
|
||||
default: getRpcUrl("avalanche-fuji"),
|
||||
public: getRpcUrl("avalanche-fuji"),
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "SnowTrace",
|
||||
url: "https://testnet.snowtrace.io/",
|
||||
},
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.Fantom]: {
|
||||
id: ChainId.Fantom,
|
||||
name: "Fantom Opera",
|
||||
network: "fantom",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Fantom),
|
||||
rpcUrls: {
|
||||
default: getRpcUrl("fantom"),
|
||||
public: getRpcUrl("fantom"),
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "Fantom Explorer",
|
||||
url: "https://ftmscan.com/",
|
||||
},
|
||||
},
|
||||
},
|
||||
[ChainId.FantomTestnet]: {
|
||||
id: ChainId.FantomTestnet,
|
||||
name: "Fantom Opera Testnet",
|
||||
network: "fantom-testnet",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.FantomTestnet),
|
||||
rpcUrls: {
|
||||
default: getRpcUrl("fantom-testnet"),
|
||||
public: getRpcUrl("fantom-testnet"),
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "Fantom Explorer",
|
||||
url: "https://testnet.ftmscan.com/",
|
||||
},
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.Optimism]: {
|
||||
id: ChainId.Optimism,
|
||||
name: "Optimism",
|
||||
network: "optimism",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Optimism),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.optimism,
|
||||
default: getRpcUrl("optimism"),
|
||||
infura: infuraRpcUrls.optimism,
|
||||
public: getRpcUrl("optimism"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.optimism,
|
||||
default: etherscanBlockExplorers.optimism,
|
||||
},
|
||||
},
|
||||
[ChainId.OptimismGoerli]: {
|
||||
id: ChainId.OptimismGoerli,
|
||||
name: "Optimism Goerli",
|
||||
network: "optimism-goerli",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.OptimismGoerli),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.optimismGoerli,
|
||||
default: getRpcUrl("optimism-goerli"),
|
||||
infura: infuraRpcUrls.optimismGoerli,
|
||||
public: getRpcUrl("optimism-goerli"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.optimismGoerli,
|
||||
default: etherscanBlockExplorers.optimismGoerli,
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.Arbitrum]: {
|
||||
id: ChainId.Arbitrum,
|
||||
name: "Arbitrum One",
|
||||
network: "arbitrum",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.Arbitrum),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.arbitrum,
|
||||
default: getRpcUrl("arbitrum"),
|
||||
infura: infuraRpcUrls.arbitrum,
|
||||
public: getRpcUrl("arbitrum"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.arbitrum,
|
||||
default: etherscanBlockExplorers.arbitrum,
|
||||
},
|
||||
},
|
||||
[ChainId.ArbitrumGoerli]: {
|
||||
id: ChainId.ArbitrumGoerli,
|
||||
name: "Arbitrum Goerli",
|
||||
network: "arbitrum-goerli",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.ArbitrumGoerli),
|
||||
rpcUrls: {
|
||||
alchemy: alchemyRpcUrls.arbitrumGoerli,
|
||||
default: getRpcUrl("arbitrum-goerli"),
|
||||
infura: infuraRpcUrls.arbitrumGoerli,
|
||||
public: getRpcUrl("arbitrum-goerli"),
|
||||
},
|
||||
blockExplorers: {
|
||||
etherscan: etherscanBlockExplorers.arbitrumGoerli,
|
||||
default: etherscanBlockExplorers.arbitrumGoerli,
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.BinanceSmartChainMainnet]: {
|
||||
id: ChainId.BinanceSmartChainMainnet,
|
||||
name: "Binance Smart Chain",
|
||||
network: "bsc",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.BinanceSmartChainMainnet),
|
||||
rpcUrls: {
|
||||
default: getRpcUrl("binance"),
|
||||
public: getRpcUrl("binance"),
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "BscScan",
|
||||
url: "https://bscscan.com/",
|
||||
},
|
||||
},
|
||||
},
|
||||
[ChainId.BinanceSmartChainTestnet]: {
|
||||
id: ChainId.BinanceSmartChainTestnet,
|
||||
name: "Binance Smart Chain Testnet",
|
||||
network: "bsc-testnet",
|
||||
nativeCurrency: getNativeTokenByChainId(ChainId.BinanceSmartChainTestnet),
|
||||
rpcUrls: {
|
||||
default: getRpcUrl("binance-testnet"),
|
||||
public: getRpcUrl("binance-testnet"),
|
||||
},
|
||||
blockExplorers: {
|
||||
default: {
|
||||
name: "BscScan",
|
||||
url: "https://testnet.bscscan.com/",
|
||||
},
|
||||
},
|
||||
testnet: true,
|
||||
},
|
||||
[ChainId.Mainnet]: enhanceChain(mainnet),
|
||||
[ChainId.Goerli]: enhanceChain(goerli),
|
||||
[ChainId.Polygon]: enhanceChain(polygon),
|
||||
[ChainId.Mumbai]: enhanceChain(polygonMumbai),
|
||||
[ChainId.Avalanche]: enhanceChain(avalanche),
|
||||
[ChainId.AvalancheFujiTestnet]: enhanceChain(avalancheFuji),
|
||||
[ChainId.Fantom]: enhanceChain(fantom),
|
||||
[ChainId.FantomTestnet]: enhanceChain(fantomTestnet),
|
||||
[ChainId.Arbitrum]: enhanceChain(arbitrum),
|
||||
[ChainId.ArbitrumGoerli]: enhanceChain(arbitrumGoerli),
|
||||
[ChainId.Optimism]: enhanceChain(optimism),
|
||||
[ChainId.OptimismGoerli]: enhanceChain(optimismGoerli),
|
||||
[ChainId.BinanceSmartChainMainnet]: enhanceChain(bsc),
|
||||
[ChainId.BinanceSmartChainTestnet]: enhanceChain(bscTestnet),
|
||||
};
|
||||
|
||||
export const thirdwebChains: Chain[] = Object.values(supportedChains);
|
||||
|
||||
@@ -2,6 +2,7 @@ export * from "./wallets/coinbase-wallet";
|
||||
export * from "./wallets/metamask";
|
||||
export * from "./wallets/wallet-connect";
|
||||
export * from "./wallets/injected";
|
||||
export * from "./wallets/magic-auth";
|
||||
// just the types
|
||||
export type {
|
||||
AbstractWallet,
|
||||
|
||||
@@ -16,21 +16,23 @@ export interface WalletEvents {
|
||||
error(error: Error): void;
|
||||
}
|
||||
|
||||
export type WalletOptions = {
|
||||
export type WalletOptions<TOpts extends Record<string, any> = {}> = {
|
||||
chains?: Chain[];
|
||||
// default: true
|
||||
shouldAutoConnect?: boolean;
|
||||
appName: string;
|
||||
};
|
||||
} & TOpts;
|
||||
|
||||
export abstract class AbstractWallet extends EventEmitter<WalletEvents> {
|
||||
export abstract class AbstractWallet<
|
||||
TAdditionalOpts extends Record<string, any> = {},
|
||||
> extends EventEmitter<WalletEvents> {
|
||||
#wallletId;
|
||||
protected coordinatorStorage;
|
||||
protected walletStorage;
|
||||
protected chains;
|
||||
protected options: WalletOptions;
|
||||
protected options: WalletOptions<TAdditionalOpts>;
|
||||
|
||||
constructor(walletId: string, options: WalletOptions) {
|
||||
constructor(walletId: string, options: WalletOptions<TAdditionalOpts>) {
|
||||
super();
|
||||
this.#wallletId = walletId;
|
||||
this.options = options;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { MagicAuthConnector, MagicAuthOptions } from "../connectors/magic";
|
||||
import { AbstractWallet, WalletOptions } from "./base";
|
||||
|
||||
export class MagicAuthWallet extends AbstractWallet<MagicAuthOptions> {
|
||||
#connector?: MagicAuthConnector;
|
||||
|
||||
static id = "magicAuth" as const;
|
||||
|
||||
public get walletName() {
|
||||
return this.#connector?.name || ("MagicAuth" as const);
|
||||
}
|
||||
|
||||
constructor(options: WalletOptions<MagicAuthOptions>) {
|
||||
super(MagicAuthWallet.id, options);
|
||||
}
|
||||
|
||||
protected async getConnector(): Promise<MagicAuthConnector> {
|
||||
if (!this.#connector) {
|
||||
// import the connector dynamically
|
||||
const { MagicAuthConnector } = await import("../connectors/magic");
|
||||
this.#connector = new MagicAuthConnector({
|
||||
chains: this.chains,
|
||||
options: this.options,
|
||||
});
|
||||
}
|
||||
return this.#connector;
|
||||
}
|
||||
|
||||
switchChain(chainId: number) {
|
||||
if (!this.#connector?.switchChain) {
|
||||
throw new Error("switchChain is not implemented for this connector");
|
||||
}
|
||||
return this.#connector.switchChain(chainId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-wallets-wallets-magic-auth.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets-wallets-magic-auth.esm.js"
|
||||
}
|
||||
@@ -9,6 +9,10 @@
|
||||
"outputs": ["dist/**", "wallets/**", "connectors/**"],
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"@thirdweb-dev/pay#build": {
|
||||
"outputs": ["dist/**", "integrations/**"],
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"@thirdweb-dev/sdk#build": {
|
||||
"outputs": ["dist/**", "evm/**", "solana/**"],
|
||||
"dependsOn": ["^build"]
|
||||
|
||||
Reference in New Issue
Block a user