Compare commits
126
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4e005be7f | ||
|
|
ccb7db4873 | ||
|
|
9151aa7dc8 | ||
|
|
5a01c537e3 | ||
|
|
f09c153ff7 | ||
|
|
15431621fd | ||
|
|
887af24450 | ||
|
|
15514f190a | ||
|
|
72589ec234 | ||
|
|
f8bceec10c | ||
|
|
7c0744a006 | ||
|
|
846022e2cf | ||
|
|
96bd92e724 | ||
|
|
c2b6d977ed | ||
|
|
7cb8e59be4 | ||
|
|
04a47e8971 | ||
|
|
8a6a97a3e9 | ||
|
|
612bcdd16f | ||
|
|
0ed77d2c9e | ||
|
|
7129aef6e1 | ||
|
|
f50b74819d | ||
|
|
3eba69afd7 | ||
|
|
98c2253e8f | ||
|
|
32d91ef709 | ||
|
|
b79dc18f7a | ||
|
|
4af785c37c | ||
|
|
6f818f393e | ||
|
|
97ed4779a9 | ||
|
|
2d3fca5237 | ||
|
|
7016019f2a | ||
|
|
4e7b293c54 | ||
|
|
c9c0e4ff39 | ||
|
|
06c24c153a | ||
|
|
aa6d9ed75d | ||
|
|
42a606588d | ||
|
|
2820761003 | ||
|
|
5200d5d7e7 | ||
|
|
e731c1a7c3 | ||
|
|
2e8d3fd6e7 | ||
|
|
cc4613b5fd | ||
|
|
78566babaa | ||
|
|
65fc7ba6ff | ||
|
|
1c24c3c3c4 | ||
|
|
408c5a81fa | ||
|
|
352291791e | ||
|
|
aabbb149e8 | ||
|
|
d59450f814 | ||
|
|
7d9a4c6abc | ||
|
|
99806e595d | ||
|
|
21c21c1adf | ||
|
|
4b10719047 | ||
|
|
989cf7347c | ||
|
|
72227b2e16 | ||
|
|
ed6c54f20d | ||
|
|
26116a6f63 | ||
|
|
f11a2719ff | ||
|
|
ef27aad0aa | ||
|
|
b03a902145 | ||
|
|
340605b507 | ||
|
|
15f8006e1f | ||
|
|
811c5a1166 | ||
|
|
c41a80d86d | ||
|
|
ad851f7b4d | ||
|
|
6218c88584 | ||
|
|
c9b564feaf | ||
|
|
87fd6ab14e | ||
|
|
df74340fa5 | ||
|
|
a57b4f01f3 | ||
|
|
126752d7d0 | ||
|
|
89ff921a7d | ||
|
|
9124b3180c | ||
|
|
f41a443c57 | ||
|
|
b7cd744cd0 | ||
|
|
bb1b4e6ac3 | ||
|
|
2f8ec89c4c | ||
|
|
6ae52664e5 | ||
|
|
4f867e15ed | ||
|
|
cc8f983615 | ||
|
|
f45ef62128 | ||
|
|
9727502eb3 | ||
|
|
b6eec6146c | ||
|
|
88daa3c5cb | ||
|
|
53c6507669 | ||
|
|
782b58d3f6 | ||
|
|
e17e41f4d1 | ||
|
|
712b11a43e | ||
|
|
306debb029 | ||
|
|
86f0ceff46 | ||
|
|
7f0d1ec947 | ||
|
|
b1218cbd7d | ||
|
|
97ef35ce0f | ||
|
|
1f46f270cd | ||
|
|
e8c25ed0f2 | ||
|
|
78d6d3c159 | ||
|
|
c72d92956a | ||
|
|
cf88795376 | ||
|
|
47287fb189 | ||
|
|
3763326c25 | ||
|
|
894eb27dfa | ||
|
|
8b3b97c8f7 | ||
|
|
89d87a5eb3 | ||
|
|
492a818079 | ||
|
|
86f608390a | ||
|
|
8096c78b46 | ||
|
|
521a49c6ec | ||
|
|
25a4773bba | ||
|
|
18381fac73 | ||
|
|
89b5c6fe07 | ||
|
|
4dccccc6fa | ||
|
|
de15e4212f | ||
|
|
1f41adbd2c | ||
|
|
1918495f2e | ||
|
|
24514a6e9f | ||
|
|
ad06c5b289 | ||
|
|
9de3f8c8e9 | ||
|
|
b1fa17158d | ||
|
|
2bdf1984cb | ||
|
|
df048af605 | ||
|
|
41507d32a7 | ||
|
|
b2cadf164c | ||
|
|
57b9c671aa | ||
|
|
3b7d10ed93 | ||
|
|
d6bb61b7fa | ||
|
|
8e591383fc | ||
|
|
6f95a520e6 | ||
|
|
f669d3ef4a |
@@ -7,7 +7,7 @@
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": [],
|
||||
"ignore": ["e2e-cra-5", "e2e-next-12", "e2e-vite-3"],
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
"updateInternalDependents": "always",
|
||||
"onlyUpdatePeerDependentsWhenOutOfRange": true
|
||||
|
||||
@@ -9,10 +9,10 @@ runs:
|
||||
with:
|
||||
node-version: 16
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
# cache: "yarn"
|
||||
cache: "yarn"
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: yarn install --frozen-lockfile
|
||||
run: yarn install
|
||||
env:
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
|
||||
|
||||
@@ -14,8 +14,7 @@ jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
# timeout-minutes: 15
|
||||
# try out buildjet to speed up build and test
|
||||
runs-on: buildjet-32vcpu-ubuntu-2204
|
||||
runs-on: ubuntu-latest-32
|
||||
# To use Remote Caching, uncomment the next lines and follow the steps below.
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
@@ -13,8 +13,7 @@ concurrency:
|
||||
jobs:
|
||||
integration-tests:
|
||||
timeout-minutes: 60
|
||||
# try out buildjet to speed up build and test
|
||||
runs-on: buildjet-32vcpu-ubuntu-2204
|
||||
runs-on: ubuntu-latest-32
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -28,9 +27,3 @@ jobs:
|
||||
run: yarn e2e
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: packages/react/playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
@@ -40,8 +40,7 @@ jobs:
|
||||
comment_body: ${{ github.event.comment.body }}
|
||||
|
||||
deploy:
|
||||
# try out buildjet to speed up build and test
|
||||
runs-on: buildjet-32vcpu-ubuntu-2204
|
||||
runs-on: ubuntu-latest-32
|
||||
needs: deploy-check
|
||||
if: needs.deploy-check.outputs.triggered == 'true'
|
||||
steps:
|
||||
|
||||
@@ -14,8 +14,7 @@ concurrency:
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
# try out buildjet to speed up build and test
|
||||
runs-on: buildjet-32vcpu-ubuntu-2204
|
||||
runs-on: ubuntu-latest-32
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -4,8 +4,7 @@ on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# try out buildjet to speed up build and test
|
||||
runs-on: buildjet-32vcpu-ubuntu-2204
|
||||
runs-on: ubuntu-latest-32
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -16,3 +15,5 @@ jobs:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
strip-hash: "\\-(\\w{8})(?:\\.esm|\\.cjs)"
|
||||
minimum-change-threshold: 100
|
||||
# exclude source maps, node modules and any test related files
|
||||
exclude: "{**/*.map,**/node_modules/**,**/test/**}"
|
||||
|
||||
+6
-1
@@ -12,4 +12,9 @@ server/dist
|
||||
public/dist
|
||||
.turbo
|
||||
./packages/contracts-js/abis
|
||||
.idea/
|
||||
.idea/
|
||||
.yalc/
|
||||
yalc.lock
|
||||
build/
|
||||
playwright-report/
|
||||
.vscode/
|
||||
@@ -1,4 +1,3 @@
|
||||
lib
|
||||
node_modules
|
||||
dist
|
||||
contracts-js
|
||||
|
||||
+5
-9
@@ -8,14 +8,15 @@
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
"packages/*",
|
||||
"test/**/*"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"test": "turbo run test test:evm test:solana",
|
||||
"e2e": "turbo run e2e",
|
||||
"build": "turbo run build",
|
||||
"build:release": "turbo run build --force",
|
||||
"build": "turbo run build --filter=./packages/*",
|
||||
"build:release": "turbo run build --force --filter=./packages/*",
|
||||
"dev": "turbo run dev --no-cache --parallel --continue",
|
||||
"fix:repo": "manypkg fix",
|
||||
"lint": "turbo run lint",
|
||||
@@ -27,12 +28,7 @@
|
||||
"release": "node ./scripts/pre-release.mjs && changeset publish",
|
||||
"version-packages:nightly": "changeset version --snapshot nightly",
|
||||
"release:nightly": "node ./scripts/pre-release.mjs && changeset publish --tag nightly",
|
||||
"push": "turbo run push"
|
||||
},
|
||||
"preconstruct": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
]
|
||||
"push": "turbo run push --filter=./packages/*"
|
||||
},
|
||||
"packageManager": "[email protected]",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,5 +1,57 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 2.0.32
|
||||
|
||||
## 2.0.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#397](https://github.com/thirdweb-dev/js/pull/397) [`72589ec`](https://github.com/thirdweb-dev/js/commit/72589ec234d86939b67b0ca8b3b789f2e9c54cc2) Thanks [@adam-maj](https://github.com/adam-maj)! - Call user callback in getUser
|
||||
|
||||
## 2.0.30
|
||||
|
||||
## 2.0.29
|
||||
|
||||
## 2.0.28
|
||||
|
||||
## 2.0.27
|
||||
|
||||
## 2.0.26
|
||||
|
||||
## 2.0.25
|
||||
|
||||
## 2.0.24
|
||||
|
||||
## 2.0.23
|
||||
|
||||
## 2.0.22
|
||||
|
||||
## 2.0.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - enable e2e testing
|
||||
|
||||
## 2.0.20
|
||||
|
||||
## 2.0.19
|
||||
|
||||
## 2.0.18
|
||||
|
||||
## 2.0.17
|
||||
|
||||
## 2.0.16
|
||||
|
||||
## 2.0.15
|
||||
|
||||
## 2.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#274](https://github.com/thirdweb-dev/js/pull/274) [`ad06c5b`](https://github.com/thirdweb-dev/js/commit/ad06c5b28902422f9b416d65255c64c937a5e046) Thanks [@adam-maj](https://github.com/adam-maj)! - Add solana auth support and plugins for react and auth package
|
||||
|
||||
## 2.0.13
|
||||
|
||||
## 2.0.12
|
||||
|
||||
## 2.0.11
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-auth-next-evm.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth-next-evm.esm.js"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-auth-next-solana.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth-next-solana.esm.js"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "2.0.12",
|
||||
"version": "2.0.32",
|
||||
"exports": {
|
||||
"./next": {
|
||||
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
|
||||
@@ -10,10 +10,18 @@
|
||||
"module": "./express/dist/thirdweb-dev-auth-express.esm.js",
|
||||
"default": "./express/dist/thirdweb-dev-auth-express.cjs.js"
|
||||
},
|
||||
"./next/evm": {
|
||||
"module": "./next/evm/dist/thirdweb-dev-auth-next-evm.esm.js",
|
||||
"default": "./next/evm/dist/thirdweb-dev-auth-next-evm.cjs.js"
|
||||
},
|
||||
"./next-auth": {
|
||||
"module": "./next-auth/dist/thirdweb-dev-auth-next-auth.esm.js",
|
||||
"default": "./next-auth/dist/thirdweb-dev-auth-next-auth.cjs.js"
|
||||
},
|
||||
"./next/solana": {
|
||||
"module": "./next/solana/dist/thirdweb-dev-auth-next-solana.esm.js",
|
||||
"default": "./next/solana/dist/thirdweb-dev-auth-next-solana.cjs.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/auth",
|
||||
@@ -35,6 +43,8 @@
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
"next/index.ts",
|
||||
"next/evm/index.ts",
|
||||
"next/solana/index.ts",
|
||||
"express/index.ts",
|
||||
"next-auth/index.ts"
|
||||
],
|
||||
@@ -63,7 +73,7 @@
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"ethers": "^5.7.1",
|
||||
"ethers": "^5.7.2",
|
||||
"express": "^4.18.1",
|
||||
"next": "^12.2.0",
|
||||
"next-auth": "^4.10.3",
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import loginHandler from "./routes/login";
|
||||
import logoutHandler from "./routes/logout";
|
||||
import userHandler from "./routes/user";
|
||||
import {
|
||||
ThirdwebAuthConfig,
|
||||
ThirdwebAuthRoute,
|
||||
ThirdwebAuthUser,
|
||||
} from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { Express, NextFunction, Request, Response } from "express";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
export function getUser(req: Request): ThirdwebAuthUser | null {
|
||||
return req.user;
|
||||
}
|
||||
|
||||
export function ThirdwebAuth(app: Express, cfg: ThirdwebAuthConfig) {
|
||||
const ctx = {
|
||||
...cfg,
|
||||
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
|
||||
};
|
||||
|
||||
const authUrl = cfg.authUrl?.replace(/\/$/, "") || "/auth";
|
||||
|
||||
app.use(cookieParser());
|
||||
|
||||
app.use(async (req: Request, _: Response, next: NextFunction) => {
|
||||
const { sdk, domain } = ctx;
|
||||
let user = null;
|
||||
const token = req.cookies.thirdweb_auth_token;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(domain, token);
|
||||
|
||||
if (ctx.callbacks?.user) {
|
||||
user = await ctx.callbacks.user(address);
|
||||
}
|
||||
|
||||
user = { ...user, address };
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
req.user = user as ThirdwebAuthUser | null;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get(`${authUrl}/:route`, (req: Request, res: Response) => {
|
||||
const action = req.params.route as ThirdwebAuthRoute;
|
||||
|
||||
switch (action) {
|
||||
case "login":
|
||||
return loginHandler(req, res, ctx);
|
||||
case "user":
|
||||
return userHandler(req, res);
|
||||
case "logout":
|
||||
return logoutHandler(req, res);
|
||||
default:
|
||||
return res.status(400).json({
|
||||
message: "Invalid route for authentication.",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,68 +1 @@
|
||||
import loginHandler from "./routes/login";
|
||||
import logoutHandler from "./routes/logout";
|
||||
import userHandler from "./routes/user";
|
||||
import {
|
||||
ThirdwebAuthConfig,
|
||||
ThirdwebAuthRoute,
|
||||
ThirdwebAuthUser,
|
||||
} from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import cookieParser from "cookie-parser";
|
||||
import { Express, NextFunction, Request, Response } from "express";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
export function getUser(req: Request): ThirdwebAuthUser | null {
|
||||
return req.user;
|
||||
}
|
||||
|
||||
export function ThirdwebAuth(app: Express, cfg: ThirdwebAuthConfig) {
|
||||
const ctx = {
|
||||
...cfg,
|
||||
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
|
||||
};
|
||||
|
||||
const authUrl = cfg.authUrl?.replace(/\/$/, "") || "/auth";
|
||||
|
||||
app.use(cookieParser());
|
||||
|
||||
app.use(async (req: Request, _: Response, next: NextFunction) => {
|
||||
const { sdk, domain } = ctx;
|
||||
let user = null;
|
||||
const token = req.cookies.thirdweb_auth_token;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(domain, token);
|
||||
|
||||
if (ctx.callbacks?.user) {
|
||||
user = await ctx.callbacks.user(address);
|
||||
}
|
||||
|
||||
user = { ...user, address };
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
req.user = user as ThirdwebAuthUser | null;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get(`${authUrl}/:route`, (req: Request, res: Response) => {
|
||||
const action = req.params.route as ThirdwebAuthRoute;
|
||||
|
||||
switch (action) {
|
||||
case "login":
|
||||
return loginHandler(req, res, ctx);
|
||||
case "user":
|
||||
return userHandler(req, res);
|
||||
case "logout":
|
||||
return logoutHandler(req, res);
|
||||
default:
|
||||
return res.status(400).json({
|
||||
message: "Invalid route for authentication.",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
export * from "./evm";
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
import { ThirdwebNextAuthConfig } from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import { serialize } from "cookie";
|
||||
import {
|
||||
GetServerSidePropsContext,
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
} from "next";
|
||||
import NextAuth, {
|
||||
NextAuthOptions,
|
||||
Session,
|
||||
unstable_getServerSession,
|
||||
} from "next-auth";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
|
||||
export function ThirdwebNextAuth(cfg: ThirdwebNextAuthConfig) {
|
||||
const sdk = ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet");
|
||||
|
||||
function ThirdwebProvider(res: GetServerSidePropsContext["res"]) {
|
||||
return CredentialsProvider({
|
||||
name: "ThirdwebAuth",
|
||||
credentials: {
|
||||
payload: {
|
||||
label: "Payload",
|
||||
type: "text",
|
||||
placeholder: "",
|
||||
},
|
||||
},
|
||||
async authorize({ payload }: any) {
|
||||
try {
|
||||
const parsed = JSON.parse(payload);
|
||||
const token = await sdk.auth.generateAuthToken(cfg.domain, parsed);
|
||||
const address = await sdk.auth.authenticate(cfg.domain, token);
|
||||
|
||||
// Securely set httpOnly cookie on request to prevent XSS on frontend
|
||||
// And set path to / to enable thirdweb_auth_token usage on all endpoints
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
serialize("thirdweb_auth_token", token, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "strict",
|
||||
}),
|
||||
);
|
||||
|
||||
return { address };
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function nextOptions(
|
||||
req: GetServerSidePropsContext["req"],
|
||||
res: GetServerSidePropsContext["res"],
|
||||
): NextAuthOptions {
|
||||
async function session({
|
||||
session: _session,
|
||||
}: {
|
||||
session: Session;
|
||||
}): Promise<Session> {
|
||||
const token = req.cookies.thirdweb_auth_token || "";
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(cfg.domain, token);
|
||||
_session.user = { ..._session.user, address } as Session["user"];
|
||||
return _session;
|
||||
} catch {
|
||||
return _session;
|
||||
}
|
||||
}
|
||||
|
||||
function signOut() {
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
serialize("thirdweb_auth_token", "", {
|
||||
path: "/",
|
||||
expires: new Date(Date.now() + 5 * 1000),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const providers: NextAuthOptions["providers"] = [
|
||||
...cfg.nextOptions.providers,
|
||||
ThirdwebProvider(res),
|
||||
];
|
||||
|
||||
const configSession = cfg.nextOptions.callbacks?.session;
|
||||
const callbacks: NextAuthOptions["callbacks"] = {
|
||||
...cfg.nextOptions.callbacks,
|
||||
session: configSession
|
||||
? async (params) => {
|
||||
params.session = await session(params);
|
||||
return configSession(params);
|
||||
}
|
||||
: session,
|
||||
};
|
||||
|
||||
const configSignOut = cfg.nextOptions.events?.signOut;
|
||||
const events: NextAuthOptions["events"] = {
|
||||
...cfg.nextOptions.events,
|
||||
signOut: configSignOut
|
||||
? async (params) => {
|
||||
signOut();
|
||||
return configSignOut(params);
|
||||
}
|
||||
: signOut,
|
||||
};
|
||||
|
||||
return {
|
||||
...cfg.nextOptions,
|
||||
providers,
|
||||
callbacks,
|
||||
events,
|
||||
};
|
||||
}
|
||||
|
||||
async function getUser(
|
||||
...args:
|
||||
| [NextApiRequest, NextApiResponse]
|
||||
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
|
||||
) {
|
||||
return unstable_getServerSession(
|
||||
args[0],
|
||||
args[1],
|
||||
nextOptions(args[0], args[1]),
|
||||
);
|
||||
}
|
||||
|
||||
function NextAuthHandler(...args: [] | [NextApiRequest, NextApiResponse]) {
|
||||
if (args.length === 0) {
|
||||
return (req: NextApiRequest, res: NextApiResponse) => {
|
||||
return NextAuth(req, res, nextOptions(req, res));
|
||||
};
|
||||
}
|
||||
|
||||
return NextAuth(args[0], args[1], nextOptions(args[0], args[1]));
|
||||
}
|
||||
|
||||
return {
|
||||
NextAuthHandler,
|
||||
getUser,
|
||||
};
|
||||
}
|
||||
@@ -1,145 +1 @@
|
||||
import { ThirdwebNextAuthConfig } from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import { serialize } from "cookie";
|
||||
import {
|
||||
GetServerSidePropsContext,
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
} from "next";
|
||||
import NextAuth, {
|
||||
NextAuthOptions,
|
||||
Session,
|
||||
unstable_getServerSession,
|
||||
} from "next-auth";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
|
||||
export function ThirdwebNextAuth(cfg: ThirdwebNextAuthConfig) {
|
||||
const sdk = ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet");
|
||||
|
||||
function ThirdwebProvider(res: GetServerSidePropsContext["res"]) {
|
||||
return CredentialsProvider({
|
||||
name: "ThirdwebAuth",
|
||||
credentials: {
|
||||
payload: {
|
||||
label: "Payload",
|
||||
type: "text",
|
||||
placeholder: "",
|
||||
},
|
||||
},
|
||||
async authorize({ payload }: any) {
|
||||
try {
|
||||
const parsed = JSON.parse(payload);
|
||||
const token = await sdk.auth.generateAuthToken(cfg.domain, parsed);
|
||||
const address = await sdk.auth.authenticate(cfg.domain, token);
|
||||
|
||||
// Securely set httpOnly cookie on request to prevent XSS on frontend
|
||||
// And set path to / to enable thirdweb_auth_token usage on all endpoints
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
serialize("thirdweb_auth_token", token, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "strict",
|
||||
}),
|
||||
);
|
||||
|
||||
return { address };
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function nextOptions(
|
||||
req: GetServerSidePropsContext["req"],
|
||||
res: GetServerSidePropsContext["res"],
|
||||
): NextAuthOptions {
|
||||
async function session({
|
||||
session: _session,
|
||||
}: {
|
||||
session: Session;
|
||||
}): Promise<Session> {
|
||||
const token = req.cookies.thirdweb_auth_token || "";
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(cfg.domain, token);
|
||||
_session.user = { ..._session.user, address } as Session["user"];
|
||||
return _session;
|
||||
} catch {
|
||||
return _session;
|
||||
}
|
||||
}
|
||||
|
||||
function signOut() {
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
serialize("thirdweb_auth_token", "", {
|
||||
path: "/",
|
||||
expires: new Date(Date.now() + 5 * 1000),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const providers: NextAuthOptions["providers"] = [
|
||||
...cfg.nextOptions.providers,
|
||||
ThirdwebProvider(res),
|
||||
];
|
||||
|
||||
const configSession = cfg.nextOptions.callbacks?.session;
|
||||
const callbacks: NextAuthOptions["callbacks"] = {
|
||||
...cfg.nextOptions.callbacks,
|
||||
session: configSession
|
||||
? async (params) => {
|
||||
params.session = await session(params);
|
||||
return configSession(params);
|
||||
}
|
||||
: session,
|
||||
};
|
||||
|
||||
const configSignOut = cfg.nextOptions.events?.signOut;
|
||||
const events: NextAuthOptions["events"] = {
|
||||
...cfg.nextOptions.events,
|
||||
signOut: configSignOut
|
||||
? async (params) => {
|
||||
signOut();
|
||||
return configSignOut(params);
|
||||
}
|
||||
: signOut,
|
||||
};
|
||||
|
||||
return {
|
||||
...cfg.nextOptions,
|
||||
providers,
|
||||
callbacks,
|
||||
events,
|
||||
};
|
||||
}
|
||||
|
||||
async function getUser(
|
||||
...args:
|
||||
| [NextApiRequest, NextApiResponse]
|
||||
| [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]]
|
||||
) {
|
||||
return unstable_getServerSession(
|
||||
args[0],
|
||||
args[1],
|
||||
nextOptions(args[0], args[1]),
|
||||
);
|
||||
}
|
||||
|
||||
function NextAuthHandler(...args: [] | [NextApiRequest, NextApiResponse]) {
|
||||
if (args.length === 0) {
|
||||
return (req: NextApiRequest, res: NextApiResponse) => {
|
||||
return NextAuth(req, res, nextOptions(req, res));
|
||||
};
|
||||
}
|
||||
|
||||
return NextAuth(args[0], args[1], nextOptions(args[0], args[1]));
|
||||
}
|
||||
|
||||
return {
|
||||
NextAuthHandler,
|
||||
getUser,
|
||||
};
|
||||
}
|
||||
export * from "./evm";
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import loginHandler from "./routes/login";
|
||||
import logoutHandler from "./routes/logout";
|
||||
import userHandler from "./routes/user";
|
||||
import {
|
||||
ThirdwebAuthConfig,
|
||||
ThirdwebAuthContext,
|
||||
ThirdwebAuthRoute,
|
||||
ThirdwebAuthUser,
|
||||
} from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
GetServerSidePropsContext,
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
} from "next/types";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
async function ThirdwebAuthRouter(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
ctx: ThirdwebAuthContext,
|
||||
) {
|
||||
// Catch-all route must be named with [...thirdweb]
|
||||
const { thirdweb } = req.query;
|
||||
const action = thirdweb?.[0] as ThirdwebAuthRoute;
|
||||
|
||||
switch (action) {
|
||||
case "login":
|
||||
return await loginHandler(req, res, ctx);
|
||||
case "user":
|
||||
return await userHandler(req, res, ctx);
|
||||
case "logout":
|
||||
return await logoutHandler(req, res);
|
||||
default:
|
||||
return res.status(400).json({
|
||||
message: "Invalid route for authentication.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
|
||||
const ctx = {
|
||||
...cfg,
|
||||
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
|
||||
};
|
||||
|
||||
function ThirdwebAuthHandler(
|
||||
...args: [] | [NextApiRequest, NextApiResponse]
|
||||
) {
|
||||
if (args.length === 0) {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) =>
|
||||
await ThirdwebAuthRouter(req, res, ctx);
|
||||
}
|
||||
|
||||
return ThirdwebAuthRouter(args[0], args[1], ctx);
|
||||
}
|
||||
|
||||
async function getUser(
|
||||
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
|
||||
) {
|
||||
const { sdk, domain } = ctx;
|
||||
let user: ThirdwebAuthUser | null = null;
|
||||
const token =
|
||||
typeof req.cookies.get === "function"
|
||||
? (req.cookies as any).get("thirdweb_auth_token")
|
||||
: (req.cookies as any).thirdweb_auth_token;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(domain, token);
|
||||
|
||||
let data = {};
|
||||
if (ctx.callbacks?.user) {
|
||||
data = await ctx.callbacks.user(address);
|
||||
}
|
||||
|
||||
user = { ...data, address };
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
return { ThirdwebAuthHandler, getUser };
|
||||
}
|
||||
@@ -1,83 +1 @@
|
||||
import loginHandler from "./routes/login";
|
||||
import logoutHandler from "./routes/logout";
|
||||
import userHandler from "./routes/user";
|
||||
import {
|
||||
ThirdwebAuthConfig,
|
||||
ThirdwebAuthContext,
|
||||
ThirdwebAuthRoute,
|
||||
ThirdwebAuthUser,
|
||||
} from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
GetServerSidePropsContext,
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
} from "next/types";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
async function ThirdwebAuthRouter(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
ctx: ThirdwebAuthContext,
|
||||
) {
|
||||
// Catch-all route must be named with [...thirdweb]
|
||||
const { thirdweb } = req.query;
|
||||
const action = thirdweb?.[0] as ThirdwebAuthRoute;
|
||||
|
||||
switch (action) {
|
||||
case "login":
|
||||
return await loginHandler(req, res, ctx);
|
||||
case "user":
|
||||
return await userHandler(req, res, ctx);
|
||||
case "logout":
|
||||
return await logoutHandler(req, res);
|
||||
default:
|
||||
return res.status(400).json({
|
||||
message: "Invalid route for authentication.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
|
||||
const ctx = {
|
||||
...cfg,
|
||||
sdk: ThirdwebSDK.fromPrivateKey(cfg.privateKey, "mainnet"),
|
||||
};
|
||||
|
||||
function ThirdwebAuthHandler(
|
||||
...args: [] | [NextApiRequest, NextApiResponse]
|
||||
) {
|
||||
if (args.length === 0) {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) =>
|
||||
await ThirdwebAuthRouter(req, res, ctx);
|
||||
}
|
||||
|
||||
return ThirdwebAuthRouter(args[0], args[1], ctx);
|
||||
}
|
||||
|
||||
async function getUser(
|
||||
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
|
||||
) {
|
||||
const { sdk, domain } = ctx;
|
||||
let user: ThirdwebAuthUser | null = null;
|
||||
const token =
|
||||
typeof req.cookies.get === "function"
|
||||
? (req.cookies as any).get("thirdweb_auth_token")
|
||||
: (req.cookies as any).thirdweb_auth_token;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(domain, token);
|
||||
user = { address };
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
return { ThirdwebAuthHandler, getUser };
|
||||
}
|
||||
export * from "./evm";
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import loginHandler from "./routes/login";
|
||||
import logoutHandler from "./routes/logout";
|
||||
import userHandler from "./routes/user";
|
||||
import {
|
||||
ThirdwebAuthConfig,
|
||||
ThirdwebAuthContext,
|
||||
ThirdwebAuthRoute,
|
||||
ThirdwebAuthUser,
|
||||
} from "./types";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
|
||||
import { NextRequest } from "next/server";
|
||||
import {
|
||||
GetServerSidePropsContext,
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
} from "next/types";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
async function ThirdwebAuthRouter(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
ctx: ThirdwebAuthContext,
|
||||
) {
|
||||
// Catch-all route must be named with [...thirdweb]
|
||||
const { thirdweb } = req.query;
|
||||
const action = thirdweb?.[0] as ThirdwebAuthRoute;
|
||||
|
||||
switch (action) {
|
||||
case "login":
|
||||
return await loginHandler(req, res, ctx);
|
||||
case "user":
|
||||
return await userHandler(req, res, ctx);
|
||||
case "logout":
|
||||
return await logoutHandler(req, res);
|
||||
default:
|
||||
return res.status(400).json({
|
||||
message: "Invalid route for authentication.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function ThirdwebAuth(cfg: ThirdwebAuthConfig) {
|
||||
const ctx = {
|
||||
...cfg,
|
||||
sdk: ThirdwebSDK.fromPrivateKey("mainnet-beta", cfg.privateKey),
|
||||
};
|
||||
|
||||
function ThirdwebAuthHandler(
|
||||
...args: [] | [NextApiRequest, NextApiResponse]
|
||||
) {
|
||||
if (args.length === 0) {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) =>
|
||||
await ThirdwebAuthRouter(req, res, ctx);
|
||||
}
|
||||
|
||||
return ThirdwebAuthRouter(args[0], args[1], ctx);
|
||||
}
|
||||
|
||||
async function getUser(
|
||||
req: GetServerSidePropsContext["req"] | NextRequest | NextApiRequest,
|
||||
) {
|
||||
const { sdk, domain } = ctx;
|
||||
let user: ThirdwebAuthUser | null = null;
|
||||
const token =
|
||||
typeof req.cookies.get === "function"
|
||||
? (req.cookies as any).get("thirdweb_auth_token")
|
||||
: (req.cookies as any).thirdweb_auth_token;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(domain, token);
|
||||
|
||||
let data = {};
|
||||
if (ctx.callbacks?.user) {
|
||||
data = await ctx.callbacks.user(address);
|
||||
}
|
||||
|
||||
user = { ...data, address };
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
return { ThirdwebAuthHandler, getUser };
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { ThirdwebAuthContext } from "../types";
|
||||
import { LoginPayload } from "@thirdweb-dev/sdk";
|
||||
import { serialize } from "cookie";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
function redirectWithError(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
error: string,
|
||||
) {
|
||||
const encodedError = encodeURIComponent(error);
|
||||
const url = new URL(req.headers.referer as string);
|
||||
url.searchParams.set("error", encodedError);
|
||||
return res.redirect(url.toString());
|
||||
}
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
ctx: ThirdwebAuthContext,
|
||||
) {
|
||||
if (req.method !== "GET") {
|
||||
return redirectWithError(req, res, "INVALID_METHOD");
|
||||
}
|
||||
|
||||
const { sdk, domain } = ctx;
|
||||
|
||||
// Get signed login payload from the frontend
|
||||
const payload = JSON.parse(atob(req.query.payload as string)) as LoginPayload;
|
||||
if (!payload) {
|
||||
redirectWithError(req, res, "MISSING_LOGIN_PAYLOAD");
|
||||
}
|
||||
|
||||
let token;
|
||||
try {
|
||||
// Generate an access token with the SDK using the signed payload
|
||||
token = await sdk.auth.generateAuthToken(domain, payload);
|
||||
} catch {
|
||||
return redirectWithError(req, res, "INVALID_LOGIN_PAYLOAD");
|
||||
}
|
||||
|
||||
// Securely set httpOnly cookie on request to prevent XSS on frontend
|
||||
// And set path to / to enable thirdweb_auth_token usage on all endpoints
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
serialize("thirdweb_auth_token", token, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: "strict",
|
||||
}),
|
||||
);
|
||||
|
||||
if (ctx.callbacks?.login) {
|
||||
const address = sdk.auth.verify(domain, payload);
|
||||
await ctx.callbacks.login(address);
|
||||
}
|
||||
|
||||
return res.status(301).redirect(req.query.redirect as string);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { serialize } from "cookie";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
) {
|
||||
if (req.method !== "GET") {
|
||||
return res.status(400).json({
|
||||
error: "Invalid method. Only GET supported.",
|
||||
});
|
||||
}
|
||||
|
||||
// Set the access token to 'none' and expire in 5 seconds
|
||||
res.setHeader(
|
||||
"Set-Cookie",
|
||||
serialize("thirdweb_auth_token", "", {
|
||||
path: "/",
|
||||
expires: new Date(Date.now() + 5 * 1000),
|
||||
}),
|
||||
);
|
||||
|
||||
return res.status(301).redirect(req.headers.referer as string);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ThirdwebAuthContext, ThirdwebAuthUser } from "../types";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
ctx: ThirdwebAuthContext,
|
||||
) {
|
||||
if (req.method !== "GET") {
|
||||
return res.status(400).json({
|
||||
error: "Invalid method. Only GET supported.",
|
||||
});
|
||||
}
|
||||
|
||||
const { sdk, domain } = ctx;
|
||||
let user = null;
|
||||
const token = req.cookies.thirdweb_auth_token;
|
||||
|
||||
if (token) {
|
||||
try {
|
||||
const address = await sdk.auth.authenticate(domain, token);
|
||||
|
||||
if (ctx.callbacks?.user) {
|
||||
user = await ctx.callbacks.user(address);
|
||||
}
|
||||
|
||||
user = { ...user, address };
|
||||
} catch {
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).json(user as ThirdwebAuthUser | null);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
export type ThirdwebAuthRoute = "login" | "logout" | "user";
|
||||
|
||||
export type ThirdwebAuthConfig = {
|
||||
privateKey: string;
|
||||
domain: string;
|
||||
callbacks?: {
|
||||
login?: (address: string) => Promise<void> | void;
|
||||
user?: (
|
||||
address: string,
|
||||
) =>
|
||||
| Promise<Omit<ThirdwebAuthUser, "address">>
|
||||
| Omit<ThirdwebAuthUser, "address">;
|
||||
};
|
||||
};
|
||||
|
||||
export type ThirdwebAuthContext = {
|
||||
sdk: ThirdwebSDK;
|
||||
domain: string;
|
||||
callbacks?: {
|
||||
login?: (address: string) => Promise<void> | void;
|
||||
user?: (
|
||||
address: string,
|
||||
) =>
|
||||
| Promise<Omit<ThirdwebAuthUser, "address">>
|
||||
| Omit<ThirdwebAuthUser, "address">;
|
||||
};
|
||||
};
|
||||
|
||||
export type ThirdwebAuthUser = {
|
||||
address: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
@@ -1,5 +1,161 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.8.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ccb7db4`](https://github.com/thirdweb-dev/js/commit/ccb7db48739b8dddcb2c032b3b6e3e5200485715), [`9151aa7`](https://github.com/thirdweb-dev/js/commit/9151aa7dc80754a7aa4fd3f6ce0207bccbb6451f), [`5a01c53`](https://github.com/thirdweb-dev/js/commit/5a01c537e30bd7ef562aad1012413990de6faedf)]:
|
||||
- @thirdweb-dev/sdk@3.6.2
|
||||
|
||||
## 0.8.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`15514f1`](https://github.com/thirdweb-dev/js/commit/15514f190a844d809ae5b1edb580aab46a682485), [`7c0744a`](https://github.com/thirdweb-dev/js/commit/7c0744a006e78987ad8b271b2c64f4bac7759510), [`f8bceec`](https://github.com/thirdweb-dev/js/commit/f8bceec10c60b30dc3d6f45926e2a9e018202578), [`887af24`](https://github.com/thirdweb-dev/js/commit/887af24450e316c2ea390cd49c0563cebe793c0f), [`96bd92e`](https://github.com/thirdweb-dev/js/commit/96bd92e724ec7d880d23348071b26ab8c21dd868)]:
|
||||
- @thirdweb-dev/sdk@3.6.1
|
||||
|
||||
## 0.8.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7cb8e59`](https://github.com/thirdweb-dev/js/commit/7cb8e59be48dc923316eb36ab43a1bef7364f6b1), [`0ed77d2`](https://github.com/thirdweb-dev/js/commit/0ed77d2c9e7526444ef660aab82a950865234aa6), [`04a47e8`](https://github.com/thirdweb-dev/js/commit/04a47e8971d28da2532ebd9db824eeb459c17bd4)]:
|
||||
- @thirdweb-dev/sdk@3.6.0
|
||||
|
||||
## 0.8.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3eba69a`](https://github.com/thirdweb-dev/js/commit/3eba69afd78b7aa472a9c00c9d4220bb69ad1d3e), [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855), [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd), [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0)]:
|
||||
- @thirdweb-dev/sdk@3.5.2
|
||||
|
||||
## 0.8.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`4e7b293`](https://github.com/thirdweb-dev/js/commit/4e7b293c543244de79c150ca8c37d941221fc9a3), [`c9c0e4f`](https://github.com/thirdweb-dev/js/commit/c9c0e4ff39b054af746f3aec75fcbd0a3ef7239d)]:
|
||||
- @thirdweb-dev/sdk@3.5.1
|
||||
|
||||
## 0.8.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5200d5d`](https://github.com/thirdweb-dev/js/commit/5200d5d7e730da58bd4d3e7c222e3c661265f913), [`42a6065`](https://github.com/thirdweb-dev/js/commit/42a606588dfed03c67c57357dd0ee1ad19fad6ea)]:
|
||||
- @thirdweb-dev/sdk@3.5.0
|
||||
|
||||
## 0.8.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`e731c1a`](https://github.com/thirdweb-dev/js/commit/e731c1a7c341e6f2b9cc42c88d5c1b036dcf4a7c)]:
|
||||
- @thirdweb-dev/sdk@3.4.5
|
||||
|
||||
## 0.8.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/sdk@3.4.4
|
||||
|
||||
## 0.8.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5), [`65fc7ba`](https://github.com/thirdweb-dev/js/commit/65fc7ba6fff093a971b53cb0979c39768e090ba1)]:
|
||||
- @thirdweb-dev/sdk@3.4.3
|
||||
|
||||
## 0.8.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`21c21c1`](https://github.com/thirdweb-dev/js/commit/21c21c1adfd09b60ad563bfd4c308597140de33c), [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60)]:
|
||||
- @thirdweb-dev/sdk@3.4.2
|
||||
|
||||
## 0.8.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2)]:
|
||||
- @thirdweb-dev/sdk@3.4.1
|
||||
|
||||
## 0.8.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - enable e2e testing
|
||||
|
||||
- Updated dependencies [[`340605b`](https://github.com/thirdweb-dev/js/commit/340605b507f384fbd2999b9c16542af3c53e84a9), [`26116a6`](https://github.com/thirdweb-dev/js/commit/26116a6f637ee845f7bd17f20ffe17caf184658e), [`15f8006`](https://github.com/thirdweb-dev/js/commit/15f8006e1fb22333b7ee239b45e7b1b12d6dccc8), [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5)]:
|
||||
- @thirdweb-dev/sdk@3.4.0
|
||||
|
||||
## 0.8.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ad851f7`](https://github.com/thirdweb-dev/js/commit/ad851f7b4d6abb5ab9364ef3dec229f913e18ce5), [`6218c88`](https://github.com/thirdweb-dev/js/commit/6218c885842e6b4e44ec38ec92a9211e56c49bf4)]:
|
||||
- @thirdweb-dev/sdk@3.3.1
|
||||
|
||||
## 0.8.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#326](https://github.com/thirdweb-dev/js/pull/326) [`126752d`](https://github.com/thirdweb-dev/js/commit/126752d7d02c9a808a63fb9e67a9df6658b5682b) Thanks [@jnsdls](https://github.com/jnsdls)! - remove tons of unused dependencies, use SDK function for extracting ipfs has from bytecode
|
||||
|
||||
- Updated dependencies [[`126752d`](https://github.com/thirdweb-dev/js/commit/126752d7d02c9a808a63fb9e67a9df6658b5682b), [`df74340`](https://github.com/thirdweb-dev/js/commit/df74340fa51323304c32419761d6f18628b060fa), [`89ff921`](https://github.com/thirdweb-dev/js/commit/89ff921a7d9f0e42b4e6707c8b56d0aab95c1aa0), [`a57b4f0`](https://github.com/thirdweb-dev/js/commit/a57b4f01f3a49590b897101f2730582e4124a554)]:
|
||||
- @thirdweb-dev/sdk@3.3.0
|
||||
|
||||
## 0.8.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`4f867e1`](https://github.com/thirdweb-dev/js/commit/4f867e15edbe5757afbf54cab207458711a8986c), [`b6eec61`](https://github.com/thirdweb-dev/js/commit/b6eec6146c23b66a3681bbb48b173b9019254214), [`9727502`](https://github.com/thirdweb-dev/js/commit/9727502eb30ac139382b7c7c8e8fc0967cbfbcf1), [`782b58d`](https://github.com/thirdweb-dev/js/commit/782b58d3f6a3b49ff9b3d837551db5161f96171a), [`f41a443`](https://github.com/thirdweb-dev/js/commit/f41a443c5785240197607b5bf93ff18a1c8e8307), [`f45ef62`](https://github.com/thirdweb-dev/js/commit/f45ef62128cfefe06275a38440c90d45e9f3cef7), [`2f8ec89`](https://github.com/thirdweb-dev/js/commit/2f8ec89c4c83ae577092c840c198e6fcfb114e69), [`b7cd744`](https://github.com/thirdweb-dev/js/commit/b7cd744cd0482a6c3fe5ae0c858f20cd380d4114), [`53c6507`](https://github.com/thirdweb-dev/js/commit/53c65076696a43bd9a7e58a7493d4e65c172900f)]:
|
||||
- @thirdweb-dev/sdk@3.2.6
|
||||
|
||||
## 0.8.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`712b11a`](https://github.com/thirdweb-dev/js/commit/712b11a43e389ae10b7f09648779a69c05b2c913)]:
|
||||
- @thirdweb-dev/sdk@3.2.5
|
||||
|
||||
## 0.8.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/sdk@3.2.4
|
||||
|
||||
## 0.8.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/sdk@3.2.3
|
||||
|
||||
## 0.8.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#276](https://github.com/thirdweb-dev/js/pull/276) [`24514a6`](https://github.com/thirdweb-dev/js/commit/24514a6e9fc1316622c1494e5be225e53f88d26e) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - build is now default compile command, reorganized the help list
|
||||
|
||||
- [#279](https://github.com/thirdweb-dev/js/pull/279) [`1918495`](https://github.com/thirdweb-dev/js/commit/1918495f2ee7f50d94083d9577dab0bf1c828c94) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - Updating command list order, helper text and changed detect -> build
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#277](https://github.com/thirdweb-dev/js/pull/277) [`9de3f8c`](https://github.com/thirdweb-dev/js/commit/9de3f8c8e954733bfc3eb6e8e0e786ff5b296f0c) Thanks [@adam-maj](https://github.com/adam-maj)! - Add recursive folder upload support
|
||||
|
||||
- [#268](https://github.com/thirdweb-dev/js/pull/268) [`57b9c67`](https://github.com/thirdweb-dev/js/commit/57b9c671aaf7fce0076dd332c171ba6944752e7a) Thanks [@adam-maj](https://github.com/adam-maj)! - Update storage upload cLI
|
||||
|
||||
- Updated dependencies [[`521a49c`](https://github.com/thirdweb-dev/js/commit/521a49c6ec6a73068adcfbc1d94d2f3f17afae86), [`8b3b97c`](https://github.com/thirdweb-dev/js/commit/8b3b97c8f7b3710ce62ed86cfeb4f6de3c23d559), [`25a4773`](https://github.com/thirdweb-dev/js/commit/25a4773bba406f4e270ac86e68c5266d24c08330), [`b2cadf1`](https://github.com/thirdweb-dev/js/commit/b2cadf164cfe9fb27081df7530356baf70ec2b3a), [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c), [`78d6d3c`](https://github.com/thirdweb-dev/js/commit/78d6d3c15968a449924b3f24354e151a90e4b20d), [`89d87a5`](https://github.com/thirdweb-dev/js/commit/89d87a5eb31e78e68e097718e9ae6e4c4d101868), [`41507d3`](https://github.com/thirdweb-dev/js/commit/41507d32a78c556f50c88de81c4d6109b1783c78), [`8096c78`](https://github.com/thirdweb-dev/js/commit/8096c78b466a7f5f9c2aac6e2c56a06f9d3a25d6), [`b1fa171`](https://github.com/thirdweb-dev/js/commit/b1fa17158dcac00265bc8f4d64f1f9708482cdb5), [`18381fa`](https://github.com/thirdweb-dev/js/commit/18381fac73741826934212ac6441842ed42a64a0), [`df048af`](https://github.com/thirdweb-dev/js/commit/df048af605f449279b01d7352b604e7856ac223c), [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c), [`cf88795`](https://github.com/thirdweb-dev/js/commit/cf88795376d3110f9d3aa839928d22276904b15a), [`89b5c6f`](https://github.com/thirdweb-dev/js/commit/89b5c6fe079e4df2cf12cb557aa9784f88c5d159)]:
|
||||
- @thirdweb-dev/sdk@3.2.2
|
||||
|
||||
## 0.7.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#259](https://github.com/thirdweb-dev/js/pull/259) [`6f95a52`](https://github.com/thirdweb-dev/js/commit/6f95a520e6cc69e08f734705e8adc71431b091cc) Thanks [@adam-maj](https://github.com/adam-maj)! - Add npx thirdweb upload command
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/sdk@3.2.1
|
||||
|
||||
## 0.7.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.7.23",
|
||||
"version": "0.8.18",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gmrchk/cli-testing-library": "^0.1.2",
|
||||
"@types/async-retry": "1.3.0",
|
||||
"@types/async-retry": "^1.3.0",
|
||||
"@types/cross-spawn": "^6.0.2",
|
||||
"@types/inquirer": "^8.2.1",
|
||||
"@types/jest": "^29.0.0",
|
||||
@@ -31,24 +31,17 @@
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"async-retry": "^1.3.3",
|
||||
"cbor": "^8.1.0",
|
||||
"commander": "^9.1.0",
|
||||
"cpy": "^9.0.1",
|
||||
"enquirer": "^2.3.6",
|
||||
"ethers": "^5.7.0",
|
||||
"form-data": "^4.0.0",
|
||||
"ethers": "^5.7.2",
|
||||
"got": "11.8.5",
|
||||
"inquirer": "^8.2.3",
|
||||
"ipfs-unixfs": "^6.0.7",
|
||||
"ipld-dag-pb": "^0.22.3",
|
||||
"multihashes": "^4.0.3",
|
||||
"open": "^8.4.0",
|
||||
"prompts": "^2.4.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"solc": "0.8.13",
|
||||
"solc": "0.8.17",
|
||||
"tar": "^6.1.11",
|
||||
"tslog": "^3.3.3",
|
||||
"update-check": "^1.5.4",
|
||||
"update-notifier": "^6.0.2",
|
||||
"validate-npm-package-name": "^4.0.0",
|
||||
"yaml": "^2.1.1"
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#!/usr/bin/env node
|
||||
import { installGithubAction } from "../common/ci-installer";
|
||||
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 { twCreate } from "../create/command";
|
||||
import generateDashboardUrl from "../helpers/generate-dashboard-url";
|
||||
import { upload } from "../storage/command";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import chalk from "chalk";
|
||||
import { Command } from "commander";
|
||||
import open from "open";
|
||||
@@ -39,12 +40,12 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
.version(cliVersion, "-v, --version");
|
||||
|
||||
program
|
||||
.command("create")
|
||||
.command("create [projectPath]")
|
||||
.description(
|
||||
"Create a thirdweb app from any of our official templates. Checkout some examples you can use here: https://github.com/thirdweb-example/",
|
||||
"Create a web3 app from any of our official templates: https://github.com/thirdweb-example/",
|
||||
)
|
||||
.option("--app", `Create a thirdweb app.`)
|
||||
.option("--contract", `Create a thirdweb contracts project.`)
|
||||
.option("--app", `Create a web3 app.`)
|
||||
.option("--contract", `Create a web3 contract project`)
|
||||
.option("--ts, --typescript", `Initialize as a TypeScript project.`)
|
||||
.option("--js, --javascript", `Initialize as a JavaScript project.`)
|
||||
.option("--forge", `Initialize as a Forge project.`)
|
||||
@@ -65,15 +66,23 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
"-t, --template [name]",
|
||||
`A template to start your project from. You can use an template repository name from the official thirdweb-example org.`,
|
||||
)
|
||||
.action(async (path, options) => {
|
||||
await twCreate(path, options);
|
||||
});
|
||||
|
||||
program
|
||||
.command("build")
|
||||
.description("Compile contract and detect thirdweb contract extensions")
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
.option("-a, --all", "run detection on all contracts")
|
||||
.action(async (options) => {
|
||||
await twCreate(options);
|
||||
await detectExtensions(options);
|
||||
});
|
||||
|
||||
program
|
||||
.command("deploy")
|
||||
.description(
|
||||
"Deploy contracts through your thirdweb dashboard, without dealing with private keys.",
|
||||
)
|
||||
.description("Deploy your (or team) contracts securely to blockchains")
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("--dry-run", "dry run (skip actually publishing)")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
@@ -116,7 +125,7 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
program
|
||||
.command("release")
|
||||
.description(
|
||||
"Release contracts, making them available for others to deploy and unlocking SDKs, Dashboards and Analytics.",
|
||||
"Release your protocol so other devs can deploy them and unlock SDKs, Dashboards and Analytics",
|
||||
)
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("--dry-run", "dry run (skip actually publishing)")
|
||||
@@ -129,10 +138,25 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
open(url.toString());
|
||||
});
|
||||
|
||||
program
|
||||
.command("upload")
|
||||
.description("Upload any file or directory to decentralized storage (IPFS)")
|
||||
.argument("[upload]", "path to file or directory to upload")
|
||||
.action(async (path) => {
|
||||
const storage = new ThirdwebStorage();
|
||||
const uri = await upload(storage, path);
|
||||
info(`Files stored at the following IPFS URI:`);
|
||||
logger.info(chalk.blueBright(uri.toString()));
|
||||
|
||||
const url = storage.resolveScheme(uri);
|
||||
info(`Open this link to view your upload:`);
|
||||
logger.info(chalk.blueBright(url.toString()));
|
||||
});
|
||||
|
||||
program
|
||||
.command("detect")
|
||||
.description(
|
||||
"Compile contracts and detect implemented thirdweb contract extensions",
|
||||
"(deprecated) Compile contracts and detect implemented thirdweb contract extensions",
|
||||
)
|
||||
.option("-p, --path <project-path>", "path to project", ".")
|
||||
.option("-d, --debug", "show debug logs")
|
||||
@@ -141,15 +165,6 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
await detectExtensions(options);
|
||||
});
|
||||
|
||||
program
|
||||
.command("install-ci")
|
||||
.description(
|
||||
"(alpha) Set up continuous integration for your contracts. This adds a github action to deploy the project on pull requests and pushes to branches. Publishes on push the the main branch.",
|
||||
)
|
||||
.action(async (options) => {
|
||||
await installGithubAction(options);
|
||||
});
|
||||
|
||||
await program.parseAsync();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { extractIPFSHashFromBytecode } from "../helpers/ipfs";
|
||||
import { logger } from "../helpers/logger";
|
||||
import { CompileOptions, IBuilder } from "../interfaces/Builder";
|
||||
import { ContractPayload } from "../interfaces/ContractPayload";
|
||||
import { extractIPFSHashFromBytecode } from "@thirdweb-dev/sdk/evm";
|
||||
|
||||
export abstract class BaseBuilder implements IBuilder {
|
||||
abstract compile(
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import { decodeFirstSync } from "cbor";
|
||||
import { UnixFS } from "ipfs-unixfs";
|
||||
import { DAGNode } from "ipld-dag-pb";
|
||||
import { toB58String } from "multihashes";
|
||||
|
||||
/**
|
||||
* Derives IPFS hash of string
|
||||
* @param {String} str
|
||||
* @return {String} IPFS hash (ex: "Qm")
|
||||
*/
|
||||
export async function getIPFSHash(str: string) {
|
||||
const file = new UnixFS({
|
||||
type: "file",
|
||||
data: Buffer.from(str),
|
||||
});
|
||||
const node = new DAGNode(file.marshal());
|
||||
const metadataLink = await node.toDAGLink();
|
||||
return toB58String(metadataLink.Hash.multihash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the IPFS hash from the given bytecode
|
||||
* @param bytecode
|
||||
* @returns
|
||||
*/
|
||||
export function extractIPFSHashFromBytecode(
|
||||
bytecode: string,
|
||||
): string | undefined {
|
||||
const numericBytecode = hexToBytes(bytecode);
|
||||
const cborLength: number =
|
||||
numericBytecode[numericBytecode.length - 2] * 0x100 +
|
||||
numericBytecode[numericBytecode.length - 1];
|
||||
const bytecodeBuffer = Buffer.from(
|
||||
numericBytecode.slice(numericBytecode.length - 2 - cborLength, -2),
|
||||
);
|
||||
const cborData = decodeFirstSync(bytecodeBuffer);
|
||||
if (cborData["ipfs"]) {
|
||||
const uri = toB58String(cborData["ipfs"]);
|
||||
return uri;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function hexToBytes(hex: string | number) {
|
||||
hex = hex.toString(16);
|
||||
if (!hex.startsWith("0x")) {
|
||||
hex = `0x${hex}`;
|
||||
}
|
||||
if (!isHexStrict(hex)) {
|
||||
throw new Error('Given value "' + hex + '" is not a valid hex string.');
|
||||
}
|
||||
|
||||
hex = hex.replace(/^0x/i, "");
|
||||
|
||||
for (var bytes = [], c = 0; c < hex.length; c += 2) {
|
||||
bytes.push(parseInt(hex.slice(c, c + 2), 16));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function isHexStrict(hex: string | number) {
|
||||
return (
|
||||
(typeof hex === "string" || typeof hex === "number") &&
|
||||
/^(-)?0x[0-9a-f]*$/i.test(hex.toString())
|
||||
);
|
||||
}
|
||||
@@ -17,9 +17,9 @@ let language: string = "";
|
||||
let baseContract: string = "";
|
||||
/* let createType: string = "app"; */
|
||||
|
||||
export async function twCreate(options: any) {
|
||||
if (typeof projectPath === "string") {
|
||||
projectPath = projectPath.trim();
|
||||
export async function twCreate(pPath: string = "", options: any) {
|
||||
if (typeof pPath === "string") {
|
||||
projectPath = pPath;
|
||||
}
|
||||
|
||||
if (options.app) {
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import { logger } from "../core/helpers/logger";
|
||||
import { spinner } from "../core/helpers/logger";
|
||||
import { BufferOrStringWithName, ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import chalk from "chalk";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
function recurseFiles(
|
||||
uploadPath: string,
|
||||
basePath: string,
|
||||
): BufferOrStringWithName[] {
|
||||
const files = [];
|
||||
|
||||
const fileType = fs.lstatSync(uploadPath);
|
||||
if (fileType.isFile()) {
|
||||
const fileName = path.relative(basePath, uploadPath);
|
||||
const fileData = fs.readFileSync(uploadPath);
|
||||
files.push({ name: fileName, data: fileData });
|
||||
} else if (fileType.isDirectory()) {
|
||||
const paths = fs.readdirSync(uploadPath);
|
||||
paths.forEach((subPath) => {
|
||||
const fullSubPath = path.join(uploadPath, subPath);
|
||||
const subFiles = recurseFiles(fullSubPath, basePath);
|
||||
subFiles.forEach((file) => files.push(file));
|
||||
});
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
export async function upload(
|
||||
storage: ThirdwebStorage,
|
||||
uploadPath: string,
|
||||
): Promise<string> {
|
||||
if (!uploadPath) {
|
||||
logger.error(
|
||||
`Please pass a path to a file or directory to upload with the following format:\n ${chalk.blueBright(
|
||||
`npx thirdweb@latest upload path/to/file.jpg`,
|
||||
)}\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pathExists = fs.existsSync(uploadPath);
|
||||
if (!pathExists) {
|
||||
logger.error(
|
||||
`Invalid path ${chalk.blueBright(
|
||||
uploadPath,
|
||||
)} provided. Please provide a valid path to a file or directory to upload."`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let uri = "";
|
||||
const fileType = fs.lstatSync(uploadPath);
|
||||
if (fileType.isDirectory()) {
|
||||
const files = recurseFiles(uploadPath, uploadPath);
|
||||
|
||||
if (files.length === 0) {
|
||||
logger.error(
|
||||
`No files detected in specified directory ${chalk.blueBright(
|
||||
uploadPath,
|
||||
)} to upload.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const spin = spinner(
|
||||
"Uploading directory to IPFS. This may take a while depending on file sizes.",
|
||||
);
|
||||
const uris = await storage.uploadBatch(files);
|
||||
spin.succeed("Successfully uploaded directory to IPFS");
|
||||
uri = uris[0].substring(0, uris[0].lastIndexOf("/"));
|
||||
} else if (fileType.isFile()) {
|
||||
const fileName = path.parse(uploadPath).base;
|
||||
const fileData = fs.readFileSync(uploadPath);
|
||||
const file = { name: fileName, data: fileData };
|
||||
|
||||
const spin = spinner(
|
||||
"Uploading file to IPFS. This may take a while depending on file sizes.",
|
||||
);
|
||||
uri = await storage.upload(file, { uploadWithoutDirectory: true });
|
||||
spin.succeed("Succesfully uploaded file to IPFS.");
|
||||
} else {
|
||||
logger.error(
|
||||
`Path ${chalk.blueBright(
|
||||
uploadPath,
|
||||
)} does not point to a valid file or directory. Please provide a valid path to a file or directory to upload`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/contracts-js
|
||||
|
||||
## 1.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#376](https://github.com/thirdweb-dev/js/pull/376) [`4af785c`](https://github.com/thirdweb-dev/js/commit/4af785c37c831ecd7087c1c9eb9095922cac0855) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop with tokens claimed
|
||||
|
||||
- [#373](https://github.com/thirdweb-dev/js/pull/373) [`6f818f3`](https://github.com/thirdweb-dev/js/commit/6f818f393eb9023b53a3358d62d60ec23a9246bd) Thanks [@adam-maj](https://github.com/adam-maj)! - Update tiered-drop contract version
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#333](https://github.com/thirdweb-dev/js/pull/333) [`aa6d9ed`](https://github.com/thirdweb-dev/js/commit/aa6d9ed75d126d7a0cca9fb8fc389ff94e9d1e14) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update to contracts package v3.2.0
|
||||
|
||||
## 1.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js-factories-TWMultichainRegistry__factory.esm.js"
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/contracts-js",
|
||||
"version": "1.1.9",
|
||||
"version": "1.2.1",
|
||||
"main": "dist/thirdweb-dev-contracts-js.cjs.js",
|
||||
"module": "dist/thirdweb-dev-contracts-js.esm.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/contracts": "3.1.8-0"
|
||||
"@thirdweb-dev/contracts": "^3.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@preconstruct/cli": "^2.2.1",
|
||||
"@typechain/ethers-v5": "10.0.0",
|
||||
"ethers": "^5.7.0",
|
||||
"ethers": "^5.7.2",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
|
||||
@@ -11,7 +11,8 @@ module.exports = {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-parameter-properties": "error",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
// making this an error going forward
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
|
||||
// import rules
|
||||
"import/first": "error",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.33.0",
|
||||
"@typescript-eslint/parser": "^5.33.0",
|
||||
"eslint-config-next": "^12.0.8",
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-turbo": "latest",
|
||||
"eslint-plugin-react": "^7.28.0"
|
||||
|
||||
@@ -1,5 +1,179 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#405](https://github.com/thirdweb-dev/js/pull/405) [`ccb7db4`](https://github.com/thirdweb-dev/js/commit/ccb7db48739b8dddcb2c032b3b6e3e5200485715) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add updateAll param to creators and royalty update hooks for Solana
|
||||
|
||||
## 3.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#392](https://github.com/thirdweb-dev/js/pull/392) [`846022e`](https://github.com/thirdweb-dev/js/commit/846022e2cfb12b846aae029b85899fb72fd2c9ad) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix unlimited price for useActiveClaimConditionForWallet
|
||||
|
||||
- [#388](https://github.com/thirdweb-dev/js/pull/388) [`7c0744a`](https://github.com/thirdweb-dev/js/commit/7c0744a006e78987ad8b271b2c64f4bac7759510) Thanks [@adam-maj](https://github.com/adam-maj)! - Add support for total supply and pagination on get all
|
||||
|
||||
## 3.6.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#386](https://github.com/thirdweb-dev/js/pull/386) [`612bcdd`](https://github.com/thirdweb-dev/js/commit/612bcdd16fc242d8b04bc70d609e44b0df47ed3f) Thanks [@jnsdls](https://github.com/jnsdls)! - [Gnosis Connector] - switch to safe.global hostname & add support for bsc and optimism chains
|
||||
|
||||
- [#364](https://github.com/thirdweb-dev/js/pull/364) [`7cb8e59`](https://github.com/thirdweb-dev/js/commit/7cb8e59be48dc923316eb36ab43a1bef7364f6b1) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add experimental `useActiveClaimConditionForWallet()` hook
|
||||
|
||||
## 3.5.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#369](https://github.com/thirdweb-dev/js/pull/369) [`2d3fca5`](https://github.com/thirdweb-dev/js/commit/2d3fca52373046c88392139922e9c9da79f9a2d4) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useClaimerProofs hook
|
||||
|
||||
- [#378](https://github.com/thirdweb-dev/js/pull/378) [`32d91ef`](https://github.com/thirdweb-dev/js/commit/32d91ef709c8fb92a903e52c65b27538a80ea4eb) Thanks [@mykcryptodev](https://github.com/mykcryptodev)! - round borders for buttons and menus on safari
|
||||
|
||||
- [#377](https://github.com/thirdweb-dev/js/pull/377) [`b79dc18`](https://github.com/thirdweb-dev/js/commit/b79dc18f7ab5155bbf4af02dc1a953546160bad0) Thanks [@adam-maj](https://github.com/adam-maj)! - Add event filters
|
||||
|
||||
## 3.5.1
|
||||
|
||||
## 3.5.0
|
||||
|
||||
## 3.4.5
|
||||
|
||||
## 3.4.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#356](https://github.com/thirdweb-dev/js/pull/356) [`cc4613b`](https://github.com/thirdweb-dev/js/commit/cc4613b5fd69840f4f9cfd0ac1c4e6743e62fe52) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add Pack and Multiwrap as NFTContract types
|
||||
|
||||
## 3.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#353](https://github.com/thirdweb-dev/js/pull/353) [`1c24c3c`](https://github.com/thirdweb-dev/js/commit/1c24c3c3c48476a824f817e09e7bf0fbe67c1db5) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useUpdateCreators hook, fix CreatorInput schema
|
||||
|
||||
## 3.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#348](https://github.com/thirdweb-dev/js/pull/348) [`7d9a4c6`](https://github.com/thirdweb-dev/js/commit/7d9a4c6abcad1b4f43e431ce7b0b38db9016ea9a) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add `useUpdateRoyaltySettings()` hook
|
||||
[SOL] - change `useRoyalty()` hook -> `useRoyaltySettings()` for consistency
|
||||
|
||||
- [#352](https://github.com/thirdweb-dev/js/pull/352) [`3522917`](https://github.com/thirdweb-dev/js/commit/352291791ee900e1500f84f095290497934d2f60) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - expose useExecuteAuctionSale and useAcceptDirectListingOffer
|
||||
|
||||
- [#351](https://github.com/thirdweb-dev/js/pull/351) [`aabbb14`](https://github.com/thirdweb-dev/js/commit/aabbb149e81d6824015a4797b2b6c929ca359545) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - fix wallet connection during dev (hot reload)
|
||||
|
||||
## 3.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#343](https://github.com/thirdweb-dev/js/pull/343) [`72227b2`](https://github.com/thirdweb-dev/js/commit/72227b2e166a3a68bbb41cf2b389322f5b7547a2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose new useOffers and useMakeOffer hooks
|
||||
|
||||
## 3.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- [#327](https://github.com/thirdweb-dev/js/pull/327) [`ef27aad`](https://github.com/thirdweb-dev/js/commit/ef27aad0aafc4577e85f44dc77dfbe880bd239b5) Thanks [@jnsdls](https://github.com/jnsdls)! - Gnosis Safe and Magic Link connectors are no longer included in the default export. They are now available as named exports instead.
|
||||
|
||||
## Gnosis Safe
|
||||
|
||||
### Connector
|
||||
|
||||
```diff
|
||||
- import { GnosisSafeConnector } from "@thirdweb-dev/react";
|
||||
+ import { GnosisSafeConnector } from "@thirdweb-dev/react/evm/connectors/gnosis-safe";
|
||||
```
|
||||
|
||||
### Hook
|
||||
|
||||
```diff
|
||||
- import { useGnosis } from "@thirdweb-dev/react";
|
||||
+ import { useGnosis } from "@thirdweb-dev/react/evm/connectors/gnosis-safe";
|
||||
```
|
||||
|
||||
## Magic Link
|
||||
|
||||
### Connector
|
||||
|
||||
```diff
|
||||
- import { MagicLink } from "@thirdweb-dev/react";
|
||||
+ import { MagicConnector } from "@thirdweb-dev/react/evm/connectors/magic";
|
||||
```
|
||||
|
||||
### Hook
|
||||
|
||||
```diff
|
||||
- import { useMagic } from "@thirdweb-dev/react";
|
||||
+ import { useMagic } from "@thirdweb-dev/react/evm/connectors/magic";
|
||||
```
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#339](https://github.com/thirdweb-dev/js/pull/339) [`b03a902`](https://github.com/thirdweb-dev/js/commit/b03a9021451b79f802f682f66e5ae8e9355d7e6f) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Fix docs for some functions
|
||||
|
||||
## 3.3.1
|
||||
|
||||
## 3.3.0
|
||||
|
||||
## 3.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#316](https://github.com/thirdweb-dev/js/pull/316) [`6ae5266`](https://github.com/thirdweb-dev/js/commit/6ae52664e5865564cd4fc2e00e3120675b9e0ca3) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix SIWE when using an external signer
|
||||
|
||||
- [#310](https://github.com/thirdweb-dev/js/pull/310) [`9727502`](https://github.com/thirdweb-dev/js/commit/9727502eb30ac139382b7c7c8e8fc0967cbfbcf1) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add useMintNFTSupply hook in react and accomodate inputs for it in SDK
|
||||
|
||||
- [#319](https://github.com/thirdweb-dev/js/pull/319) [`2f8ec89`](https://github.com/thirdweb-dev/js/commit/2f8ec89c4c83ae577092c840c198e6fcfb114e69) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose total claimed and unclaimed supply for erc721 drop contracts
|
||||
|
||||
- [#318](https://github.com/thirdweb-dev/js/pull/318) [`bb1b4e6`](https://github.com/thirdweb-dev/js/commit/bb1b4e6ac39d98cc68b8e31e659cd68be3a5b967) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useMinimumNextBid hook and change invariant for other marketplace hooks
|
||||
|
||||
## 3.2.5
|
||||
|
||||
## 3.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#301](https://github.com/thirdweb-dev/js/pull/301) [`86f0cef`](https://github.com/thirdweb-dev/js/commit/86f0ceff46f72df8ebd81f843e3c66a245f23992) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Remove useAddress hook for now
|
||||
|
||||
## 3.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#299](https://github.com/thirdweb-dev/js/pull/299) [`b1218cb`](https://github.com/thirdweb-dev/js/commit/b1218cbd7d97ca7949d94a7e4ab93fef4ffbacd5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Recreate SDK on wallet changes for hook propagation
|
||||
|
||||
## 3.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#289](https://github.com/thirdweb-dev/js/pull/289) [`521a49c`](https://github.com/thirdweb-dev/js/commit/521a49c6ec6a73068adcfbc1d94d2f3f17afae86) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Standarize useClaimNFT to evm
|
||||
|
||||
- [#274](https://github.com/thirdweb-dev/js/pull/274) [`ad06c5b`](https://github.com/thirdweb-dev/js/commit/ad06c5b28902422f9b416d65255c64c937a5e046) Thanks [@adam-maj](https://github.com/adam-maj)! - Add solana auth support and plugins for react and auth package
|
||||
|
||||
- [#269](https://github.com/thirdweb-dev/js/pull/269) [`b2cadf1`](https://github.com/thirdweb-dev/js/commit/b2cadf164cfe9fb27081df7530356baf70ec2b3a) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - remove deprecated chains from support
|
||||
|
||||
- [#272](https://github.com/thirdweb-dev/js/pull/272) [`2bdf198`](https://github.com/thirdweb-dev/js/commit/2bdf1984cb97121c447cffd27aa6a5f4f92679b3) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add claim conditions hook for Solana
|
||||
|
||||
- [#291](https://github.com/thirdweb-dev/js/pull/291) [`86f6083`](https://github.com/thirdweb-dev/js/commit/86f608390a7848cda82c0f7e8913ca60f7d3901f) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - expose `.program` on `useProgram()` return type to mirror `.contract` on EVM
|
||||
|
||||
- [#292](https://github.com/thirdweb-dev/js/pull/292) [`492a818`](https://github.com/thirdweb-dev/js/commit/492a818079aff67c7f88bc43666c87c3714cc957) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] expose `useAddress()` hook that returns the connected wallet address in base58
|
||||
|
||||
- [#290](https://github.com/thirdweb-dev/js/pull/290) [`8096c78`](https://github.com/thirdweb-dev/js/commit/8096c78b466a7f5f9c2aac6e2c56a06f9d3a25d6) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - `useClaimNFT()` allow claiming to connected wallet without passing address explicitly
|
||||
[EVM] - `useClaimNFT()` allow claiming to connected wallet without passing address explicitly
|
||||
|
||||
- [#298](https://github.com/thirdweb-dev/js/pull/298) [`e8c25ed`](https://github.com/thirdweb-dev/js/commit/e8c25ed0f28d566203d70bf59a8908e59216334a) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - fix useBalance
|
||||
|
||||
- [#263](https://github.com/thirdweb-dev/js/pull/263) [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - add chainId getter on contracts
|
||||
|
||||
- [#266](https://github.com/thirdweb-dev/js/pull/266) [`3b7d10e`](https://github.com/thirdweb-dev/js/commit/3b7d10ed93d8e3b698e9f905c93ba79863b35325) Thanks [@adam-maj](https://github.com/adam-maj)! - Add storage hooks to react
|
||||
|
||||
- [#282](https://github.com/thirdweb-dev/js/pull/282) [`1f41adb`](https://github.com/thirdweb-dev/js/commit/1f41adbd2ccfbc367757620e0842ef32bc783a08) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - Fix useActiveClaimCondition invariant failing for token ID 0
|
||||
|
||||
- [#296](https://github.com/thirdweb-dev/js/pull/296) [`cf88795`](https://github.com/thirdweb-dev/js/commit/cf88795376d3110f9d3aa839928d22276904b15a) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - add `useBalance` hook
|
||||
|
||||
## 3.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#264](https://github.com/thirdweb-dev/js/pull/264) [`f669d3e`](https://github.com/thirdweb-dev/js/commit/f669d3ef4a84368c23f0359aec304f66a4063042) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Standarize data between evm/solana for useLazyMint hook
|
||||
|
||||
## 3.2.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -26,11 +26,11 @@ The `useContractCall` hook to call a smart contract function has been renamed to
|
||||
|
||||
### Prebuilt Contract Hooks
|
||||
|
||||
Hooks to connect to prebuilt contracts such as `useNFTDrop` and `useMarketplace` are now deprecated in favour of using the generic `useContract` hook:
|
||||
Hooks to connect to prebuilt contracts such as `useNFTDrop` and `useMarketplace` are now deprecated in favour of using the generic `useContract` hook followed by the contract type:
|
||||
|
||||
```diff
|
||||
- const contract = useNFTDrop("0x...")
|
||||
+ const { contract } = useContract("0x...")
|
||||
+ const { contract } = useContract("0x...", "nft-drop")
|
||||
```
|
||||
|
||||
You can currently still continue to use the prebuilt hooks, however they provide a warning in the console informing you of their deprecation. We will likely remove these hooks in a future version of the SDK.
|
||||
|
||||
@@ -71,7 +71,7 @@ Below are examples of where to set this up in your application:
|
||||
|
||||
### Connect to a User's Wallet
|
||||
|
||||
Now the provider is set up, we can use all of the hooks and UI components available in the SDK, such as the [ConnectWallet](https://portal.thirdweb.com/sdk/ui-components/connectwalletbutton) component.
|
||||
Now the provider is set up, we can use all of the hooks and UI components available in the SDK, such as the [ConnectWallet](https://portal.thirdweb.com/ui-components/connectwalletbutton) component.
|
||||
|
||||
Once the user has connected their wallet, all the calls we make to interact with contracts using the SDK will be on behalf of the user.
|
||||
|
||||
@@ -143,7 +143,7 @@ Each [extension](https://portal.thirdweb.com/extensions) you implement in your s
|
||||
|
||||
These hooks make it easy to interact with your smart contracts by implementing the complex logic for you under the hood.
|
||||
|
||||
For example, if your smart contract implements [ERC721Supply](https://portal.thirdweb.com/extensions/erc721supply#unlocked-features), you unlock the ability to [view all NFTs](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721supply#get-all-minted-nfts) on that contract using the SDK; which fetches all of your NFT metadata and the current owner of each NFT in parallel. In the React SDK, that is available using `useNFTs`:
|
||||
For example, if your smart contract implements [ERC721Supply](https://portal.thirdweb.com/contractkit/interfaces/erc721supply#unlocked-features), you unlock the ability to [view all NFTs](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721supply#get-all-minted-nfts) on that contract using the SDK; which fetches all of your NFT metadata and the current owner of each NFT in parallel. In the React SDK, that is available using `useNFTs`:
|
||||
|
||||
```jsx
|
||||
import { useContract, useNFTs } from "@thirdweb-dev/react";
|
||||
@@ -154,7 +154,7 @@ export default function Home() {
|
||||
}
|
||||
```
|
||||
|
||||
If we want to mint an NFT and our contract implements [ERC721Mintable](https://portal.thirdweb.com/extensions/erc721mintable#unlocked-features), we can use the [`useMintNFT`](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721mintable) hook to mint an NFT from the connected wallet; handling all of the logic of uploading and pinning the metadata to IPFS for us behind the scenes.
|
||||
If we want to mint an NFT and our contract implements [ERC721Mintable](https://portal.thirdweb.com/contractkit/interfaces/erc721mintable#unlocked-features), we can use the [`useMintNFT`](https://portal.thirdweb.com/sdk/interacting-with-contracts/erc721mintable) hook to mint an NFT from the connected wallet; handling all of the logic of uploading and pinning the metadata to IPFS for us behind the scenes.
|
||||
|
||||
```jsx
|
||||
import { useContract, useNFTs, useMintNFT } from "@thirdweb-dev/react";
|
||||
@@ -172,7 +172,7 @@ export default function Home() {
|
||||
|
||||
The SDK provides many UI components to help you build your application.
|
||||
|
||||
For example, we can render each of the NFTs using the [`NFT Media Renderer`](https://portal.thirdweb.com/sdk/ui-components/nft-renderer)
|
||||
For example, we can render each of the NFTs using the [`NFT Media Renderer`](https://portal.thirdweb.com/ui-components/nft-renderer)
|
||||
component, making use of the loading state from `useNFTs`:
|
||||
|
||||
```jsx title="pages/index.jsx"
|
||||
@@ -191,7 +191,7 @@ export default function Home() {
|
||||
<div>
|
||||
{nfts.map((nft) => (
|
||||
<ThirdwebNftMedia
|
||||
key={nft.tokenId}
|
||||
key={nft.metadata.id}
|
||||
metadata={nft.metadata}
|
||||
height={200}
|
||||
/>
|
||||
@@ -203,7 +203,7 @@ export default function Home() {
|
||||
}
|
||||
```
|
||||
|
||||
The [`Web3Button`](https://portal.thirdweb.com/sdk/ui-components/web3button) component ensures the user has connected their wallet and is currently configured to the same network as your smart contract before calling the function. It also has access to the `contract` directly, allowing you to perform any action on your smart contract when the button is clicked.
|
||||
The [`Web3Button`](https://portal.thirdweb.com/ui-components/web3button) component ensures the user has connected their wallet and is currently configured to the same network as your smart contract before calling the function. It also has access to the `contract` directly, allowing you to perform any action on your smart contract when the button is clicked.
|
||||
|
||||
For example, we can mint an NFT like so:
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-react-evm-connectors-gnosis-safe.cjs.js",
|
||||
"module": "dist/thirdweb-dev-react-evm-connectors-gnosis-safe.esm.js"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-react-evm-connectors-magic.cjs.js",
|
||||
"module": "dist/thirdweb-dev-react-evm-connectors-magic.esm.js"
|
||||
}
|
||||
+21
-19
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.2.0",
|
||||
"version": "3.6.2",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,6 +20,14 @@
|
||||
"module": "./solana/dist/thirdweb-dev-react-solana.esm.js",
|
||||
"default": "./solana/dist/thirdweb-dev-react-solana.cjs.js"
|
||||
},
|
||||
"./evm/connectors/magic": {
|
||||
"module": "./evm/connectors/magic/dist/thirdweb-dev-react-evm-connectors-magic.esm.js",
|
||||
"default": "./evm/connectors/magic/dist/thirdweb-dev-react-evm-connectors-magic.cjs.js"
|
||||
},
|
||||
"./evm/connectors/gnosis-safe": {
|
||||
"module": "./evm/connectors/gnosis-safe/dist/thirdweb-dev-react-evm-connectors-gnosis-safe.esm.js",
|
||||
"default": "./evm/connectors/gnosis-safe/dist/thirdweb-dev-react-evm-connectors-gnosis-safe.cjs.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
@@ -31,7 +39,8 @@
|
||||
"entrypoints": [
|
||||
"index.ts",
|
||||
"solana/index.ts",
|
||||
"evm/index.ts"
|
||||
"evm/index.ts",
|
||||
"evm/connectors/**.ts"
|
||||
],
|
||||
"___experimentalFlags_WILL_CHANGE_IN_PATCH": {
|
||||
"exports": true
|
||||
@@ -49,13 +58,11 @@
|
||||
"generate-docs:solana": "api-extractor run --local --config ./config/api-extractor-solana.json && api-documenter markdown -i ./temp -o ./docs/solana && rm -rf ./temp-solana && mv ./temp ./temp-solana",
|
||||
"generate-snippets": "node ./scripts/generate-snippets-evm.mjs && node ./scripts/generate-snippets-solana.mjs && node ./scripts/generate-feature-snippets-evm.mjs",
|
||||
"build": "tsc && preconstruct build",
|
||||
"e2e": "yarn test-ct",
|
||||
"test-ct": "playwright test -c playwright-ct.config.ts",
|
||||
"push": "yalc push"
|
||||
},
|
||||
"sideEffects:": false,
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.10",
|
||||
"@babel/core": "^7.19.3",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
@@ -63,15 +70,12 @@
|
||||
"@microsoft/api-documenter": "^7.19.4",
|
||||
"@microsoft/api-extractor": "^7.29.2",
|
||||
"@microsoft/tsdoc": "^0.14.1",
|
||||
"@playwright/experimental-ct-react": "1.25.1",
|
||||
"@playwright/test": "1.25.1",
|
||||
"@preconstruct/cli": "^2.2.1",
|
||||
"@solana/wallet-adapter-base": "^0.9.17",
|
||||
"@solana/wallet-adapter-react": "^0.15.19",
|
||||
"@solana/wallet-adapter-wallets": "^0.19.0",
|
||||
"@solana/web3.js": "^1.63.0",
|
||||
"@solana/web3.js": "^1.62.0",
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"@thirdweb-dev/solana": "*",
|
||||
"@types/color": "^3.0.3",
|
||||
"@types/mime": "^3.0.1",
|
||||
"@types/react": "^18.0.5",
|
||||
@@ -84,7 +88,7 @@
|
||||
"eslint-plugin-inclusive-language": "^2.2.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-tsdoc": "^0.2.16",
|
||||
"ethers": "^5.7.1",
|
||||
"ethers": "^5.7.2",
|
||||
"prettier": "^2.7.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@@ -93,13 +97,14 @@
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@emotion/styled": "^11.10.0",
|
||||
"@gnosis.pm/safe-core-sdk": "2.1.0",
|
||||
"@gnosis.pm/safe-ethers-adapters": "0.1.0-alpha.9",
|
||||
"@gnosis.pm/safe-ethers-lib": "1.1.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",
|
||||
"@solana/wallet-adapter-phantom": "^0.9.17",
|
||||
"@tanstack/react-query": "^4.0.10",
|
||||
"@zag-js/menu": "0.1.14",
|
||||
"@zag-js/react": "0.1.16",
|
||||
"@zag-js/menu": "^0.1.16",
|
||||
"@zag-js/react": "^0.1.18",
|
||||
"buffer": "^6.0.3",
|
||||
"color": "^4.2.3",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
@@ -114,7 +119,7 @@
|
||||
"@solana/wallet-adapter-base": "^0.9.17",
|
||||
"@solana/wallet-adapter-react": "^0.15.19",
|
||||
"@solana/wallet-adapter-wallets": "^0.19.0",
|
||||
"@solana/web3.js": "^1.63.0",
|
||||
"@solana/web3.js": "^1.62.0",
|
||||
"@thirdweb-dev/sdk": "*",
|
||||
"ethers": ">=5.5.1",
|
||||
"react": ">=18.0.0",
|
||||
@@ -136,9 +141,6 @@
|
||||
},
|
||||
"@solana/web3.js": {
|
||||
"optional": true
|
||||
},
|
||||
"@thirdweb-dev/solana": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import type { PlaywrightTestConfig } from "@playwright/experimental-ct-react";
|
||||
import { devices } from "@playwright/experimental-ct-react";
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: "./",
|
||||
/* The base directory, relative to the config file, for snapshot files created with toMatchSnapshot and toHaveScreenshot. */
|
||||
snapshotDir: "./__snapshots__",
|
||||
/* Maximum time one test can run for. */
|
||||
timeout: 10 * 1000,
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
// workers: process.env.CI ? 1 : undefined,
|
||||
// we can use parallel tests because we have more cores now
|
||||
workers: undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
|
||||
/* Port to use for Playwright component endpoint. */
|
||||
ctPort: 3100,
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "firefox",
|
||||
use: {
|
||||
...devices["Desktop Firefox"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "webkit",
|
||||
use: {
|
||||
...devices["Desktop Safari"],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,4 +0,0 @@
|
||||
// Import styles, initialize component theme here.
|
||||
// import '../src/common.css';
|
||||
|
||||
export {};
|
||||
@@ -70,7 +70,7 @@ function parseExampleTag(docComment) {
|
||||
return examples;
|
||||
}
|
||||
|
||||
const baseDocUrl = "https://portal.thirdweb.com/react/react.";
|
||||
const baseDocUrl = "https://portal.thirdweb.com/react/solana/react.";
|
||||
|
||||
const extractReferenceLink = (m, kind, contractName) => {
|
||||
if (kind === "Property") {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RequiredParam } from "../types/shared";
|
||||
import { QueryKey } from "@tanstack/react-query";
|
||||
import type { ChainId } from "@thirdweb-dev/sdk";
|
||||
import { RequiredParam } from "./required-param";
|
||||
import type { QueryKey } from "@tanstack/react-query";
|
||||
import type { ValidContractInstance } from "@thirdweb-dev/sdk";
|
||||
import type {
|
||||
Network,
|
||||
NFTCollection,
|
||||
@@ -13,7 +13,7 @@ const TW_QUERY_KEY_PREFIX = "__tw__";
|
||||
// marker to make sure the query will not get stored in local storage by a query persister
|
||||
const NEVER_PERSIST_QUERY_POSTFIX = { persist: false } as const;
|
||||
|
||||
function ensureTWPrefix<TKey extends QueryKey>(key: TKey) {
|
||||
export function ensureTWPrefix<TKey extends QueryKey>(key: TKey) {
|
||||
if (key[0] === TW_QUERY_KEY_PREFIX) {
|
||||
return key as unknown as Readonly<[typeof TW_QUERY_KEY_PREFIX, ...TKey]>;
|
||||
}
|
||||
@@ -37,11 +37,23 @@ export function createEVMQueryKey<TKey extends QueryKey>(key: TKey) {
|
||||
}
|
||||
export function createEVMQueryKeyWithChain<TKey extends QueryKey>(
|
||||
key: TKey,
|
||||
chainId: RequiredParam<ChainId>,
|
||||
chainId: RequiredParam<number>,
|
||||
) {
|
||||
return ensureTWPrefix([chainId, ...key] as const);
|
||||
}
|
||||
|
||||
export function createEVMContractQueryKey<TKey extends QueryKey>(
|
||||
contract: RequiredParam<ValidContractInstance>,
|
||||
key: TKey = [] as unknown as TKey,
|
||||
) {
|
||||
const chainId = contract?.chainId;
|
||||
const address = contract?.getAddress();
|
||||
return createEVMQueryKeyWithChain(
|
||||
["contract", address, ...key] as const,
|
||||
chainId,
|
||||
);
|
||||
}
|
||||
|
||||
// SOL
|
||||
|
||||
export function createSOLQueryKey<TKey extends QueryKey>(key: TKey) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
* Makes a parameter required to be passed, but still allowes it to be null or undefined.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export type RequiredParam<T> = T | null | undefined;
|
||||
|
||||
export function requiredParamInvariant<TValue>(
|
||||
condition: RequiredParam<TValue>,
|
||||
message?: string,
|
||||
): asserts condition {
|
||||
invariant(condition !== null || condition !== undefined, message);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
/**
|
||||
* Makes a parameter required to be passed, but still allowes it to be null or undefined.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export type RequiredParam<T> = T | null | undefined;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { ConnectWallet } from ".";
|
||||
import { ThirdwebProvider } from "../../providers/full";
|
||||
import { test, expect } from "@playwright/experimental-ct-react";
|
||||
|
||||
test.use({ viewport: { width: 500, height: 500 } });
|
||||
|
||||
test("should render the connect wallet button", async ({ mount }) => {
|
||||
const component = await mount(
|
||||
<ThirdwebProvider desiredChainId={1}>
|
||||
<ConnectWallet />
|
||||
</ThirdwebProvider>,
|
||||
);
|
||||
|
||||
await expect(component).toContainText("Connect Wallet");
|
||||
});
|
||||
@@ -76,10 +76,8 @@ const chainIdToCurrencyMap: Record<
|
||||
> = {
|
||||
[ChainId.Mainnet]: "ethereum",
|
||||
[ChainId.Goerli]: "ethereum",
|
||||
[ChainId.Rinkeby]: "ethereum",
|
||||
|
||||
[ChainId.Arbitrum]: "arbitrum",
|
||||
[ChainId.ArbitrumRinkeby]: "arbitrum",
|
||||
[ChainId.ArbitrumGoerli]: "arbitrum",
|
||||
|
||||
[ChainId.Avalanche]: "avalanche",
|
||||
@@ -89,7 +87,6 @@ const chainIdToCurrencyMap: Record<
|
||||
[ChainId.FantomTestnet]: "fantom",
|
||||
|
||||
[ChainId.Optimism]: "optimism",
|
||||
[ChainId.OptimismKovan]: "optimism",
|
||||
[ChainId.OptimismGoerli]: "optimism",
|
||||
|
||||
[ChainId.Polygon]: "polygon",
|
||||
@@ -147,7 +144,7 @@ export const ConnectWallet: React.FC<ConnectWalletProps> = ({
|
||||
},
|
||||
connect,
|
||||
] = useConnect();
|
||||
const disconnect = useDisconnect({ reconnectAfterGnosis: false });
|
||||
const disconnect = useDisconnect({ reconnectPrevious: false });
|
||||
|
||||
const supportedConnectors = connectors.filter((c) =>
|
||||
SUPPORTED_CONNECTORS.includes(c.id as typeof SUPPORTED_CONNECTORS[number]),
|
||||
|
||||
@@ -13,12 +13,14 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This component can be used to render NFTs from the thirdweb SDK.
|
||||
* Props: {@link ThirdwebNftMediaProps}
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { ThirdwebNftMedia, useNFTCollection, useNFT } from "@thirdweb-dev/react";
|
||||
* import { ThirdwebNftMedia, useContract, useNFT } from "@thirdweb-dev/react";
|
||||
* export default function NFTCollectionRender() {
|
||||
* const contract = useNFTCollection(<your-contract-address>);
|
||||
* const { contract } = useContract(<your-contract-address>);
|
||||
* const { data: nft, isLoading } = useNFT(contract, 0);
|
||||
*
|
||||
* return (
|
||||
@@ -32,11 +34,6 @@ export interface ThirdwebNftMediaProps extends SharedMediaProps {
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Use this to get the primary sales recipient of your {@link SmartContract}
|
||||
* @param contract - an instance of a {@link SmartContract}
|
||||
* @returns the wallet address of the primary sales recipient
|
||||
* @beta
|
||||
*/
|
||||
export const ThirdwebNftMedia = React.forwardRef<
|
||||
HTMLMediaElement,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Web3Button } from ".";
|
||||
import { ThirdwebProvider } from "../../providers/full";
|
||||
import { test, expect } from "@playwright/experimental-ct-react";
|
||||
|
||||
test.use({ viewport: { width: 500, height: 500 } });
|
||||
|
||||
test("should render the connect wallet button", async ({ mount }) => {
|
||||
const component = await mount(
|
||||
<ThirdwebProvider desiredChainId={1}>
|
||||
<Web3Button contractAddress="0x123" action={() => undefined}>
|
||||
Do the thing
|
||||
</Web3Button>
|
||||
</ThirdwebProvider>,
|
||||
);
|
||||
await expect(component).toContainText("Connect Wallet");
|
||||
});
|
||||
@@ -37,8 +37,19 @@ const BaseButton = styled.button<BaseButtonProps>`
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&:focus {
|
||||
outline: 2px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
|
||||
outline-offset: 1px;
|
||||
position: relative;
|
||||
border-radius: 0.5em;
|
||||
outline: 0;
|
||||
}
|
||||
&:focus:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: -5px;
|
||||
bottom: -5px;
|
||||
left: -5px;
|
||||
border: 2px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
|
||||
border-radius: 11px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -8,10 +8,23 @@ export const Menu = styled.ul<MenuProps>`
|
||||
margin: 0;
|
||||
background: ${(props) => (props.theme as TwUiTheme).colors.background};
|
||||
color: ${(props) => (props.theme as TwUiTheme).colors.text};
|
||||
outline-color: ${(props) => (props.theme as TwUiTheme).colors.accent};
|
||||
outline-style: solid;
|
||||
outline-offset: 0px;
|
||||
list-style: none;
|
||||
border-radius: 0.5em;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
pointer-events: auto;
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border: 1px solid ${(props) => (props.theme as TwUiTheme).colors.accent};
|
||||
border-radius: 0.5em;
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { ChainId } from "@thirdweb-dev/sdk";
|
||||
import { useConnect } from "../hooks/wagmi-required/useConnect";
|
||||
import { Signer, ethers, utils } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Chain, Connector, ConnectorData, normalizeChainId } from "wagmi";
|
||||
import {
|
||||
Chain,
|
||||
Connector,
|
||||
ConnectorData,
|
||||
normalizeChainId,
|
||||
useContext as useWagmiContext,
|
||||
} from "wagmi";
|
||||
|
||||
// exerpt from https://docs.gnosis-safe.io/backend/available-services
|
||||
const CHAIN_ID_TO_GNOSIS_SERVER_URL = {
|
||||
[ChainId.Mainnet]: "https://safe-transaction.mainnet.gnosis.io",
|
||||
[ChainId.Avalanche]: "https://safe-transaction.avalanche.gnosis.io",
|
||||
[ChainId.Polygon]: "https://safe-transaction.polygon.gnosis.io",
|
||||
[ChainId.Goerli]: "https://safe-transaction.goerli.gnosis.io",
|
||||
[ChainId.Rinkeby]: "https://safe-transaction.rinkeby.gnosis.io",
|
||||
};
|
||||
// mainnet
|
||||
1: "https://safe-transaction-mainnet.safe.global",
|
||||
// avalanche
|
||||
43114: "https://safe-transaction-avalanche.safe.global",
|
||||
// polygon
|
||||
137: "https://safe-transaction-polygon.safe.global",
|
||||
// goerli
|
||||
5: "https://safe-transaction-goerli.safe.global",
|
||||
// bsc
|
||||
56: "https://safe-transaction-bsc.safe.global",
|
||||
// optimism
|
||||
10: "https://safe-transaction-optimism.safe.global",
|
||||
} as const;
|
||||
|
||||
export interface GnosisConnectorArguments {
|
||||
safeAddress: string;
|
||||
@@ -19,6 +33,8 @@ export interface GnosisConnectorArguments {
|
||||
const __IS_SERVER__ = typeof window === "undefined";
|
||||
|
||||
export class GnosisSafeConnector extends Connector {
|
||||
static supportedChains = Object.keys(CHAIN_ID_TO_GNOSIS_SERVER_URL);
|
||||
public supportedChains = GnosisSafeConnector.supportedChains;
|
||||
id = "gnosis";
|
||||
ready = __IS_SERVER__;
|
||||
name = "Gnosis Safe";
|
||||
@@ -51,6 +67,11 @@ export class GnosisSafeConnector extends Connector {
|
||||
};
|
||||
}
|
||||
|
||||
public isChainSupported(chainId: string | number) {
|
||||
const id = normalizeChainId(chainId);
|
||||
return !this.isChainUnsupported(id);
|
||||
}
|
||||
|
||||
private async createSafeSigner() {
|
||||
const signer = await this.previousConnector?.getSigner();
|
||||
const safeAddress = this.config?.safeAddress;
|
||||
@@ -164,3 +185,67 @@ export class GnosisSafeConnector extends Connector {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for connecting to a Gnosis Safe. This enables multisig wallets to connect to your application and sing transactions.
|
||||
*
|
||||
* ```javascript
|
||||
* import { useGnosis } from "@thirdweb-dev/react"
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useGnosis } from "@thirdweb-dev/react"
|
||||
*
|
||||
* const App = () => {
|
||||
* const connectWithGnosis = useGnosis()
|
||||
*
|
||||
* return (
|
||||
* <button onClick={() => connectWithGnosis({ safeAddress: "0x...", safeChainId: 1 })}>
|
||||
* Connect Gnosis Safe
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useGnosis() {
|
||||
const wagmiContext = useWagmiContext();
|
||||
invariant(
|
||||
wagmiContext,
|
||||
`useGnosis() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
|
||||
);
|
||||
const [connectors, connect] = useConnect();
|
||||
if (connectors.loading) {
|
||||
return () => Promise.reject("Gnosis connector not ready to be used, yet");
|
||||
}
|
||||
const connector = connectors.data.connectors.find((c) => c.id === "gnosis");
|
||||
invariant(
|
||||
connector,
|
||||
"Gnosis connector not found, please make sure it is provided to your <ThirdwebProvider />",
|
||||
);
|
||||
|
||||
return async (config: GnosisConnectorArguments) => {
|
||||
const previousConnector = connectors.data.connector;
|
||||
const previousConnectorChain = await previousConnector?.getChainId();
|
||||
invariant(
|
||||
!!previousConnector,
|
||||
"Cannot connect to Gnosis Safe without first being connected to a personal wallet.",
|
||||
);
|
||||
invariant(
|
||||
previousConnectorChain === config.safeChainId,
|
||||
"Gnosis safe chain id must match personal wallet chain id.",
|
||||
);
|
||||
invariant(
|
||||
utils.isAddress(config.safeAddress),
|
||||
"Gnosis safe address must be a valid address.",
|
||||
);
|
||||
(connector as GnosisSafeConnector).setConfiguration(
|
||||
previousConnector,
|
||||
config,
|
||||
);
|
||||
return connect(connector);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useConnect } from "../hooks/wagmi-required/useConnect";
|
||||
import { providers, utils } from "ethers";
|
||||
import type {
|
||||
LoginWithMagicLinkConfiguration,
|
||||
@@ -5,7 +6,12 @@ import type {
|
||||
MagicSDKAdditionalConfiguration,
|
||||
} from "magic-sdk";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Chain, Connector, normalizeChainId } from "wagmi";
|
||||
import {
|
||||
Chain,
|
||||
Connector,
|
||||
normalizeChainId,
|
||||
useContext as useWagmiContext,
|
||||
} from "wagmi";
|
||||
|
||||
export interface MagicConnectorArguments
|
||||
extends MagicSDKAdditionalConfiguration {
|
||||
@@ -186,3 +192,89 @@ export class MagicConnector extends Connector {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for connecting to an email wallet using magic link.
|
||||
* This enables users without their own wallets to connect to your application and sign transactions securely using their email.
|
||||
*
|
||||
* ```javascript
|
||||
* import { useMagic } from "@thirdweb-dev/react"
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* Before using this hook, you first need to set up the magic configuration in your `ThirdwebProvider`, including your magic API key.
|
||||
*
|
||||
* ```javascript
|
||||
* // Add the magic configuration object to your wallet connectors
|
||||
* const connectors = [
|
||||
* "metamask",
|
||||
* "walletConnect",
|
||||
* "walletLink",
|
||||
* {
|
||||
* name: "magic",
|
||||
* options: {
|
||||
* apiKey: "your-magic-api-key",
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* // Add the above to the walletConnectors prop of your <ThirdwebProvider />
|
||||
* const Provider = ({ children }) => (
|
||||
* return (
|
||||
* <ThirdwebProvider
|
||||
* walletConnectors={connectors}
|
||||
* // Specify remaining parameters
|
||||
* ...
|
||||
* >
|
||||
* {children}
|
||||
* </ThirdwebProvider>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In order to use the hook to connect users with magic link, you just need to provide the users email to the connect function.
|
||||
*
|
||||
* You can setup the hook with the following configuration:
|
||||
* ```javascript
|
||||
* import { useMagic } from "@thirdweb-dev/react"
|
||||
* import { useState } from "react"
|
||||
*
|
||||
* const LoginWithMagicLink = () => {
|
||||
* const connectWithMagic = useMagic()
|
||||
* const [email, setEmail] = useState()
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <input value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
* <button onClick={() => connectWithMagic({ email })}>Login</button>
|
||||
* </div>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useMagic() {
|
||||
const wagmiContext = useWagmiContext();
|
||||
invariant(
|
||||
wagmiContext,
|
||||
`useMagic() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
|
||||
);
|
||||
const [connectors, connect] = useConnect();
|
||||
if (connectors.loading) {
|
||||
return () => Promise.reject("Magic connector not ready to be used, yet");
|
||||
}
|
||||
|
||||
const connector = connectors.data.connectors.find((c) => c.id === "magic");
|
||||
|
||||
invariant(
|
||||
connector,
|
||||
"Magic connector not found, please make sure it is provided to your <ThirdwebProvider />",
|
||||
);
|
||||
|
||||
return (configuration: LoginWithMagicLinkConfiguration) => {
|
||||
(connector as MagicConnector).setConfiguration(configuration);
|
||||
return connect(connector);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,20 +16,7 @@ const chain: Record<string, Chain> = {
|
||||
},
|
||||
],
|
||||
},
|
||||
rinkeby: {
|
||||
id: ChainId.Rinkeby,
|
||||
name: "Rinkeby",
|
||||
nativeCurrency: NATIVE_TOKENS[ChainId.Rinkeby],
|
||||
rpcUrls: ["https://rinkeby.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"],
|
||||
blockExplorers: [
|
||||
{
|
||||
name: "Etherscan",
|
||||
url: "https://rinkeby.etherscan.io",
|
||||
},
|
||||
],
|
||||
testnet: true,
|
||||
deprecated: true,
|
||||
},
|
||||
|
||||
goerli: {
|
||||
id: ChainId.Goerli,
|
||||
name: "Goerli",
|
||||
@@ -147,20 +134,7 @@ const chain: Record<string, Chain> = {
|
||||
],
|
||||
testnet: false,
|
||||
},
|
||||
optimismKovan: {
|
||||
id: ChainId.OptimismKovan,
|
||||
name: "Optimism Kovan",
|
||||
nativeCurrency: NATIVE_TOKENS[ChainId.OptimismKovan],
|
||||
rpcUrls: ["https://kovan.optimism.io"],
|
||||
blockExplorers: [
|
||||
{
|
||||
name: "Etherscan",
|
||||
url: "https://kovan-optimistic.etherscan.io/",
|
||||
},
|
||||
],
|
||||
testnet: true,
|
||||
deprecated: true,
|
||||
},
|
||||
|
||||
optimismGoerli: {
|
||||
id: ChainId.OptimismGoerli,
|
||||
name: "Optimism Goerli Testnet",
|
||||
@@ -187,20 +161,7 @@ const chain: Record<string, Chain> = {
|
||||
],
|
||||
testnet: false,
|
||||
},
|
||||
arbitrumRinkeby: {
|
||||
id: ChainId.ArbitrumRinkeby,
|
||||
name: "Arbitrum Rinkeby",
|
||||
nativeCurrency: NATIVE_TOKENS[ChainId.ArbitrumRinkeby],
|
||||
rpcUrls: ["https://rinkeby.arbitrum.io/rpc"],
|
||||
blockExplorers: [
|
||||
{
|
||||
name: "Arbiscan",
|
||||
url: "https://testnet.arbiscan.io/",
|
||||
},
|
||||
],
|
||||
testnet: true,
|
||||
deprecated: true,
|
||||
},
|
||||
|
||||
arbitrumGoerli: {
|
||||
id: ChainId.ArbitrumGoerli,
|
||||
name: "Arbitrum Goerli",
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
SupportedChainId,
|
||||
defaultSupportedChains,
|
||||
} from "../constants/chain";
|
||||
import { ChainId } from "@thirdweb-dev/sdk";
|
||||
import { DEFAULT_RPC_URLS } from "@thirdweb-dev/sdk";
|
||||
import React, { PropsWithChildren, createContext, useContext } from "react";
|
||||
|
||||
interface ThirdwebConfigContext {
|
||||
@@ -11,18 +11,8 @@ interface ThirdwebConfigContext {
|
||||
supportedChains: Chain[];
|
||||
}
|
||||
|
||||
export const defaultChainRpc: Record<SupportedChainId | number, string> = {
|
||||
[ChainId.Mainnet]: "mainnet",
|
||||
[ChainId.Rinkeby]: "rinkeby",
|
||||
[ChainId.Goerli]: "goerli",
|
||||
[ChainId.Polygon]: "polygon",
|
||||
[ChainId.Mumbai]: "mumbai",
|
||||
[ChainId.Fantom]: "fantom",
|
||||
[ChainId.Avalanche]: "avalanche",
|
||||
};
|
||||
|
||||
const ThirdwebConfigContext = createContext<ThirdwebConfigContext>({
|
||||
rpcUrlMap: defaultChainRpc,
|
||||
rpcUrlMap: DEFAULT_RPC_URLS,
|
||||
supportedChains: defaultSupportedChains,
|
||||
});
|
||||
|
||||
|
||||
@@ -33,7 +33,17 @@ export const ThirdwebConnectedWalletProvider: React.FC<
|
||||
const { rpcUrlMap } = useThirdwebConfigContext();
|
||||
|
||||
const [contextValue, setContextValue] =
|
||||
useState<ThirdwebConnectedWalletContext>(INITIAL_CONTEXT_VALUE);
|
||||
useState<ThirdwebConnectedWalletContext>({
|
||||
...INITIAL_CONTEXT_VALUE,
|
||||
signer: signer ? signer : undefined,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setContextValue((val) => ({
|
||||
...val,
|
||||
signer: signer ? signer : undefined,
|
||||
}));
|
||||
}, [signer]);
|
||||
|
||||
useEffect(() => {
|
||||
let s = signer;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import {
|
||||
RequiredParam,
|
||||
requiredParamInvariant,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDK, useSDKChainId } from "../../providers/base";
|
||||
import { getErcs, DropContract, WalletAddress } from "../../types";
|
||||
import {
|
||||
cacheKeys,
|
||||
@@ -7,8 +10,16 @@ import {
|
||||
} from "../../utils/cache-keys";
|
||||
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { ClaimCondition, ClaimConditionInput } from "@thirdweb-dev/sdk";
|
||||
import { BigNumberish } from "ethers";
|
||||
import {
|
||||
ClaimCondition,
|
||||
ClaimConditionFetchOptions,
|
||||
ClaimConditionInput,
|
||||
SnapshotEntryWithProof,
|
||||
fetchCurrencyValue,
|
||||
convertToReadableQuantity,
|
||||
fetchCurrencyMetadata,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { BigNumberish, constants, utils } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
@@ -38,7 +49,7 @@ export type SetClaimConditionsParams = {
|
||||
/** **********************/
|
||||
|
||||
/**
|
||||
* Use this to get the active claim conditon for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
* Use this to get the active claim condition for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
@@ -53,7 +64,7 @@ export type SetClaimConditionsParams = {
|
||||
* const { data: activeClaimCondition, isLoading, error } = useActiveClaimCondition(<YourERC1155ContractInstance>, <tokenId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
|
||||
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
|
||||
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
|
||||
* @returns a response object with the currently active claim condition
|
||||
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
|
||||
@@ -62,22 +73,30 @@ export type SetClaimConditionsParams = {
|
||||
export function useActiveClaimCondition(
|
||||
contract: RequiredParam<DropContract>,
|
||||
tokenId?: BigNumberish,
|
||||
options?: ClaimConditionFetchOptions,
|
||||
) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc1155, erc721, erc20 } = getErcs(contract);
|
||||
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.claimConditions.getActive(contractAddress, tokenId),
|
||||
cacheKeys.extensions.claimConditions.getActive(
|
||||
contractAddress,
|
||||
tokenId,
|
||||
options,
|
||||
),
|
||||
() => {
|
||||
if (erc1155) {
|
||||
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
|
||||
return erc1155.claimConditions.getActive(tokenId);
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
return erc1155.claimConditions.getActive(tokenId, options);
|
||||
}
|
||||
if (erc721) {
|
||||
return erc721.claimConditions.getActive();
|
||||
return erc721.claimConditions.getActive(options);
|
||||
}
|
||||
if (erc20) {
|
||||
return erc20.claimConditions.getActive();
|
||||
return erc20.claimConditions.getActive(options);
|
||||
}
|
||||
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
|
||||
},
|
||||
@@ -91,7 +110,80 @@ export function useActiveClaimCondition(
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get all claim conditons for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
* Use this to get the claimer proofs for an adddress for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC20ContractInstance>);
|
||||
* ```
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC721ContractInstance>);
|
||||
* ```
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: claimerProofs, isLoading, error } = useClaimerProofs(<YourERC1155ContractInstance>, <tokenId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
|
||||
* @param claimerAddress - the address of the claimer to fetch the claimer proofs for
|
||||
* @param tokenId - the id of the token to fetch the claimer proofs for (if the contract is an ERC1155 contract)
|
||||
* @param claimConditionId - optional the claim condition id to get the proofs for
|
||||
* @returns a response object with the snapshot for the provided address
|
||||
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
|
||||
* @beta
|
||||
*/
|
||||
export function useClaimerProofs(
|
||||
contract: RequiredParam<DropContract>,
|
||||
claimerAddress: string,
|
||||
tokenId?: BigNumberish,
|
||||
claimConditionId?: BigNumberish,
|
||||
) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc1155, erc721, erc20 } = getErcs(contract);
|
||||
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.claimConditions.getClaimerProofs(
|
||||
contractAddress,
|
||||
tokenId,
|
||||
),
|
||||
() => {
|
||||
if (erc1155) {
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
return erc1155.claimConditions.getClaimerProofs(
|
||||
tokenId,
|
||||
claimerAddress,
|
||||
claimConditionId,
|
||||
);
|
||||
}
|
||||
if (erc721) {
|
||||
return erc721.claimConditions.getClaimerProofs(
|
||||
claimerAddress,
|
||||
claimConditionId,
|
||||
);
|
||||
}
|
||||
if (erc20) {
|
||||
return erc20.claimConditions.getClaimerProofs(
|
||||
claimerAddress,
|
||||
claimConditionId,
|
||||
);
|
||||
}
|
||||
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
|
||||
},
|
||||
{
|
||||
// Checks that happen here:
|
||||
// 1. if the contract is based on ERC1155 contract => tokenId cannot be `undefined`
|
||||
// 2. if the contract is NOT based on ERC1155 => we have to have either an ERC721 or ERC20 contract
|
||||
enabled: erc1155 ? tokenId !== undefined : !!erc721 || !!erc20,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get all claim conditions for ERC20, ERC721 or ERC1155 based contracts. They need to extend the `claimCondition` extension for this hook to work.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
@@ -106,7 +198,7 @@ export function useActiveClaimCondition(
|
||||
* const { data: claimConditions, isLoading, error } = useClaimConditions(<YourERC1155ContractInstance>, <tokenId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC721 or ERC1155 spec and implements the `claimConditions` extension.
|
||||
* @param contract - an instance of a contract that extends the ERC721, ERC1155 or ERC20 spec and implements the `claimConditions` extension.
|
||||
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
|
||||
* @returns a response object with the list of claim conditions
|
||||
* @twfeature ERC721ClaimableWithConditions | ERC1155ClaimableWithConditions | ERC20ClaimableWithConditions
|
||||
@@ -115,22 +207,30 @@ export function useActiveClaimCondition(
|
||||
export function useClaimConditions(
|
||||
contract: RequiredParam<DropContract>,
|
||||
tokenId?: BigNumberish,
|
||||
options?: ClaimConditionFetchOptions,
|
||||
) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc1155, erc721, erc20 } = getErcs(contract);
|
||||
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.claimConditions.getAll(contractAddress, tokenId),
|
||||
cacheKeys.extensions.claimConditions.getAll(
|
||||
contractAddress,
|
||||
tokenId,
|
||||
options,
|
||||
),
|
||||
() => {
|
||||
if (erc1155) {
|
||||
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
|
||||
return erc1155.claimConditions.getAll(tokenId);
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
return erc1155.claimConditions.getAll(tokenId, options);
|
||||
}
|
||||
if (erc721) {
|
||||
return erc721.claimConditions.getAll();
|
||||
return erc721.claimConditions.getAll(options);
|
||||
}
|
||||
if (erc20) {
|
||||
return erc20.claimConditions.getAll();
|
||||
return erc20.claimConditions.getAll(options);
|
||||
}
|
||||
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
|
||||
},
|
||||
@@ -181,7 +281,7 @@ export function useClaimIneligibilityReasons(
|
||||
),
|
||||
() => {
|
||||
if (erc1155) {
|
||||
invariant(
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim ineligibility reasons",
|
||||
);
|
||||
@@ -219,6 +319,136 @@ export function useClaimIneligibilityReasons(
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to check the claim condition for a given wallet address (including checking for overrides that may exist for that given wallet address).
|
||||
*
|
||||
* @param contract - an instance of a contract that extends the ERC20, ERC721 or ERC1155 spec and implements the `claimConditions` extension.
|
||||
* @param walletAddress - the wallet address to check the active claim condition for
|
||||
* @param tokenId - the id of the token to fetch the claim conditions for (if the contract is an ERC1155 contract)
|
||||
* @returns the active claim conditon for the wallet address or null if there is no active claim condition
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export function useActiveClaimConditionForWallet(
|
||||
contract: RequiredParam<DropContract>,
|
||||
walletAddress: RequiredParam<WalletAddress>,
|
||||
tokenId?: BigNumberish,
|
||||
) {
|
||||
const sdk = useSDK();
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc1155, erc721, erc20 } = getErcs(contract);
|
||||
return useQueryWithNetwork<ClaimCondition | null>(
|
||||
cacheKeys.extensions.claimConditions.useActiveClaimConditionForWallet(
|
||||
contractAddress,
|
||||
walletAddress || "_NO_WALLET_",
|
||||
tokenId,
|
||||
),
|
||||
async () => {
|
||||
// if we do not have a walletAddress just do the same logic as basic useClaimCondition
|
||||
if (!walletAddress) {
|
||||
if (erc1155) {
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
return erc1155.claimConditions.getActive(tokenId);
|
||||
}
|
||||
if (erc721) {
|
||||
return erc721.claimConditions.getActive();
|
||||
}
|
||||
if (erc20) {
|
||||
return erc20.claimConditions.getActive();
|
||||
}
|
||||
throw new Error("Contract must be ERC721, ERC1155 or ERC20");
|
||||
}
|
||||
invariant(sdk, "sdk is required");
|
||||
let activeGeneralClaimCondition: ClaimCondition | null = null;
|
||||
let claimerProofForWallet: SnapshotEntryWithProof | null = null;
|
||||
|
||||
if (erc1155) {
|
||||
requiredParamInvariant(tokenId, "tokenId is required for ERC1155");
|
||||
const [cc, cp] = await Promise.all([
|
||||
erc1155.claimConditions.getActive(tokenId),
|
||||
erc1155.claimConditions.getClaimerProofs(tokenId, walletAddress),
|
||||
]);
|
||||
activeGeneralClaimCondition = cc;
|
||||
claimerProofForWallet = cp;
|
||||
}
|
||||
if (erc721) {
|
||||
const [cc, cp] = await Promise.all([
|
||||
erc721.claimConditions.getActive(),
|
||||
erc721.claimConditions.getClaimerProofs(walletAddress),
|
||||
]);
|
||||
activeGeneralClaimCondition = cc;
|
||||
claimerProofForWallet = cp;
|
||||
}
|
||||
if (erc20) {
|
||||
const [cc, cp] = await Promise.all([
|
||||
erc20.claimConditions.getActive(),
|
||||
erc20.claimConditions.getClaimerProofs(walletAddress),
|
||||
]);
|
||||
activeGeneralClaimCondition = cc;
|
||||
claimerProofForWallet = cp;
|
||||
}
|
||||
// if there is no active claim condition nothing matters, return null
|
||||
if (!activeGeneralClaimCondition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if there is no claimer proof then just fall back to the active general claim condition
|
||||
if (!claimerProofForWallet) {
|
||||
return activeGeneralClaimCondition;
|
||||
}
|
||||
|
||||
const { maxClaimable, currencyAddress, price } = claimerProofForWallet;
|
||||
|
||||
const currencyWithOverride =
|
||||
currencyAddress || activeGeneralClaimCondition.currencyAddress;
|
||||
|
||||
const currencyMetadata = await fetchCurrencyMetadata(
|
||||
sdk.getProvider(),
|
||||
currencyWithOverride,
|
||||
);
|
||||
|
||||
const normalizedPrize = price
|
||||
? price === "unlimited"
|
||||
? constants.MaxUint256
|
||||
: utils.parseUnits(price, currencyMetadata.decimals)
|
||||
: null;
|
||||
|
||||
const priceWithOverride =
|
||||
normalizedPrize || activeGeneralClaimCondition.price;
|
||||
|
||||
const maxClaimableWithOverride =
|
||||
// have to transform this the same way that claim conditions do it in SDK
|
||||
convertToReadableQuantity(maxClaimable, currencyMetadata.decimals) ||
|
||||
activeGeneralClaimCondition.maxClaimablePerWallet;
|
||||
|
||||
const currencyValueWithOverride = await fetchCurrencyValue(
|
||||
sdk.getProvider(),
|
||||
currencyWithOverride,
|
||||
priceWithOverride,
|
||||
);
|
||||
return {
|
||||
// inherit the entire claim condition
|
||||
...activeGeneralClaimCondition,
|
||||
// overwrite all keys that could be changed based on overwrites
|
||||
maxClaimablePerWallet: maxClaimableWithOverride,
|
||||
price: priceWithOverride,
|
||||
currency: currencyWithOverride,
|
||||
currencyAddress: currencyWithOverride,
|
||||
currencyMetadata: currencyValueWithOverride,
|
||||
};
|
||||
},
|
||||
{
|
||||
// Checks that happen here:
|
||||
// 1. if the contract is based on ERC1155 contract => tokenId cannot be `undefined`
|
||||
// 2. if the contract is NOT based on ERC1155 => we have to have either an ERC721 or ERC20 contract
|
||||
enabled: erc1155 ? tokenId !== undefined : !!erc721 || !!erc20,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** **********************/
|
||||
/** WRITE HOOKS **/
|
||||
/** **********************/
|
||||
@@ -267,11 +497,14 @@ export function useSetClaimConditions(
|
||||
|
||||
return useMutation(
|
||||
async (data: SetClaimConditionsParams) => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
const { phases, reset = false } = data;
|
||||
invariant(phases, 'No "phases" provided');
|
||||
if (erc1155) {
|
||||
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
return erc1155.claimConditions.set(tokenId, phases, reset);
|
||||
}
|
||||
if (erc721) {
|
||||
@@ -338,35 +571,42 @@ export function useResetClaimConditions(
|
||||
|
||||
return useMutation(
|
||||
async () => {
|
||||
const cleanConditions = (conditions: ClaimCondition[]) => {
|
||||
const cleanConditions = async (conditions: ClaimCondition[]) => {
|
||||
return conditions.map((c) => ({
|
||||
...c,
|
||||
price: c.currencyMetadata.displayValue,
|
||||
maxQuantity: c.maxQuantity.toString(),
|
||||
quantityLimitPerTransaction: c.quantityLimitPerTransaction.toString(),
|
||||
}));
|
||||
};
|
||||
|
||||
if (erc1155) {
|
||||
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
|
||||
const claimConditions = await erc1155.claimConditions.getAll(tokenId);
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim conditions",
|
||||
);
|
||||
const claimConditions = await erc1155.claimConditions.getAll(tokenId, {
|
||||
withAllowList: true,
|
||||
});
|
||||
return erc1155.claimConditions.set(
|
||||
tokenId,
|
||||
cleanConditions(claimConditions || []),
|
||||
await cleanConditions(claimConditions || []),
|
||||
true,
|
||||
);
|
||||
}
|
||||
if (erc721) {
|
||||
const claimConditions = await erc721.claimConditions.getAll();
|
||||
const claimConditions = await erc721.claimConditions.getAll({
|
||||
withAllowList: true,
|
||||
});
|
||||
return await erc721.claimConditions.set(
|
||||
cleanConditions(claimConditions || []),
|
||||
await cleanConditions(claimConditions || []),
|
||||
true,
|
||||
);
|
||||
}
|
||||
if (erc20) {
|
||||
const claimConditions = await erc20.claimConditions.getAll();
|
||||
const claimConditions = await erc20.claimConditions.getAll({
|
||||
withAllowList: true,
|
||||
});
|
||||
return await erc20.claimConditions.set(
|
||||
cleanConditions(claimConditions || []),
|
||||
await cleanConditions(claimConditions || []),
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
RequiredParam,
|
||||
requiredParamInvariant,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import { WalletAddress } from "../../types";
|
||||
import {
|
||||
@@ -35,7 +38,7 @@ export function usePrimarySaleRecipient(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.sales.getRecipient(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"sales" in contract && contract.sales,
|
||||
"Contract does not support primarySale",
|
||||
@@ -86,7 +89,7 @@ export function useUpdatePrimarySaleRecipient(
|
||||
const activeChainId = useSDKChainId();
|
||||
return useMutation(
|
||||
(newRecipient: WalletAddress) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"sales" in contract && contract.sales,
|
||||
"Contract does not support primarySale",
|
||||
@@ -128,7 +131,7 @@ export function useRoyaltySettings(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.royalties.getDefaultRoyaltyInfo(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"royalties" in contract && contract.royalties,
|
||||
"Contract does not support royalties",
|
||||
@@ -182,7 +185,7 @@ export function useUpdateRoyaltySettings(
|
||||
seller_fee_basis_points?: number;
|
||||
fee_recipient?: WalletAddress;
|
||||
}) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"royalties" in contract && contract.royalties,
|
||||
"Contract does not support royalties",
|
||||
@@ -224,7 +227,7 @@ export function usePlatformFees(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.platformFees.get(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"platformFees" in contract && contract.platformFees,
|
||||
"Contract does not support platformFees",
|
||||
@@ -277,7 +280,7 @@ export function useUpdatePlatformFees(
|
||||
platform_fee_basis_points?: number;
|
||||
fee_recipient?: WalletAddress;
|
||||
}) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"platformFees" in contract && contract.platformFees,
|
||||
"Contract does not support platformFees",
|
||||
@@ -317,7 +320,7 @@ export function useMetadata(contract: RequiredParam<ValidContractInstance>) {
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.metadata.get(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"metadata" in contract && contract.metadata,
|
||||
"Contract does not support metadata",
|
||||
@@ -366,7 +369,7 @@ export function useUpdateMetadata(
|
||||
const activeChainId = useSDKChainId();
|
||||
return useMutation(
|
||||
(updatePayload: CustomContractMetadata) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(
|
||||
"metadata" in contract && contract.metadata,
|
||||
"Contract does not support metadata",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { neverPersist } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
RequiredParam,
|
||||
requiredParamInvariant,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDK, useSDKChainId } from "../../providers/base";
|
||||
import { ContractAddress } from "../../types";
|
||||
import {
|
||||
@@ -20,7 +23,7 @@ import {
|
||||
ContractEvent,
|
||||
ContractForPrebuiltContractType,
|
||||
ContractType,
|
||||
EventQueryFilter,
|
||||
EventQueryOptions,
|
||||
PrebuiltContractType,
|
||||
SUPPORTED_CHAIN_ID,
|
||||
ThirdwebSDK,
|
||||
@@ -216,7 +219,7 @@ export function useContract(
|
||||
{ wallet, walletChainId, sdkTimestamp },
|
||||
]),
|
||||
async () => {
|
||||
invariant(contractAddress, "contract address is required");
|
||||
requiredParamInvariant(contractAddress, "contract address is required");
|
||||
invariant(sdk, "SDK not initialized");
|
||||
invariant(activeChainId, "active chain id is required");
|
||||
|
||||
@@ -290,7 +293,7 @@ export function useContractMetadata<TContract extends ValidContractInstance>(
|
||||
>(
|
||||
cacheKeys.contract.metadata(contract?.getAddress()),
|
||||
async () => {
|
||||
invariant(contract, "contract is required");
|
||||
requiredParamInvariant(contract, "contract is required");
|
||||
return await contract.metadata.get();
|
||||
},
|
||||
{
|
||||
@@ -311,7 +314,7 @@ export function useContractMetadataUpdate(
|
||||
|
||||
return useMutation(
|
||||
async (metadata: CommonContractSchemaInput) => {
|
||||
invariant(contract, "contract must be defined");
|
||||
requiredParamInvariant(contract, "contract must be defined");
|
||||
|
||||
return contract.metadata.update(metadata);
|
||||
},
|
||||
@@ -343,7 +346,7 @@ export function useContractMetadataUpdate(
|
||||
export function useContractEvents(
|
||||
contract: RequiredParam<ValidContractInstance>,
|
||||
eventName?: string,
|
||||
options: { queryFilter?: EventQueryFilter; subscribe?: boolean } = {
|
||||
options: { queryFilter?: EventQueryOptions; subscribe?: boolean } = {
|
||||
subscribe: true,
|
||||
},
|
||||
) {
|
||||
@@ -406,7 +409,7 @@ export function useContractEvents(
|
||||
return useQuery(
|
||||
cacheKey,
|
||||
() => {
|
||||
invariant(contract, "contract must be defined");
|
||||
requiredParamInvariant(contract, "contract must be defined");
|
||||
if (eventName) {
|
||||
return contract.events.getEvents(eventName, options.queryFilter);
|
||||
}
|
||||
@@ -447,8 +450,8 @@ export function useContractRead(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.call(contractAddress, functionName, args),
|
||||
() => {
|
||||
invariant(contract, "contract must be defined");
|
||||
invariant(functionName, "function name must be provided");
|
||||
requiredParamInvariant(contract, "contract must be defined");
|
||||
requiredParamInvariant(functionName, "function name must be provided");
|
||||
return contract.call(functionName, ...args);
|
||||
},
|
||||
{
|
||||
@@ -485,8 +488,8 @@ export function useContractWrite(
|
||||
|
||||
return useMutation(
|
||||
async (callParams?: unknown[] | [...unknown[], CallOverrides]) => {
|
||||
invariant(contract, "contract must be defined");
|
||||
invariant(functionName, "function name must be provided");
|
||||
requiredParamInvariant(contract, "contract must be defined");
|
||||
requiredParamInvariant(functionName, "function name must be provided");
|
||||
if (!callParams?.length) {
|
||||
return contract.call(functionName);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
RequiredParam,
|
||||
requiredParamInvariant,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import {
|
||||
ClaimNFTParams,
|
||||
@@ -51,7 +54,7 @@ export function useUnclaimedNFTs(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.nft.drop.getAllUnclaimed(contractAddress, queryParams),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
// TODO make this work for custom contracts (needs ABI change)
|
||||
invariant(
|
||||
contract.getAllUnclaimed,
|
||||
@@ -94,18 +97,18 @@ export function useClaimedNFTs(
|
||||
*/
|
||||
export function useUnclaimedNFTSupply(contract: RequiredParam<DropContract>) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc721 } = getErcs(contract);
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.nft.drop.totalUnclaimedSupply(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
// TODO make this work for custom contracts (needs ABI change)
|
||||
invariant(
|
||||
"totalUnclaimedSupply" in contract && contract.totalUnclaimedSupply,
|
||||
"Contract instance does not support fetching unclaimed supply",
|
||||
);
|
||||
return contract.totalUnclaimedSupply();
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
if (erc721) {
|
||||
invariant(erc721, "No ERC721 Contract instance provided");
|
||||
return erc721.totalUnclaimedSupply();
|
||||
}
|
||||
invariant(false, "Contract is not an instance of ERC721")
|
||||
},
|
||||
{ enabled: !!contract },
|
||||
{ enabled: !!erc721 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,18 +121,17 @@ export function useUnclaimedNFTSupply(contract: RequiredParam<DropContract>) {
|
||||
*/
|
||||
export function useClaimedNFTSupply(contract: RequiredParam<DropContract>) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
const { erc721 } = getErcs(contract);
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.nft.drop.totalClaimedSupply(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
// TODO make this work for custom contracts (needs ABI change)
|
||||
invariant(
|
||||
"totalClaimedSupply" in contract && contract.totalClaimedSupply,
|
||||
"Contract instance does not support fetching unclaimed supply",
|
||||
);
|
||||
return contract.totalClaimedSupply();
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
if (erc721) {
|
||||
return erc721.totalClaimedSupply();
|
||||
}
|
||||
invariant(false, "Contract is not an instance of ERC721")
|
||||
},
|
||||
{ enabled: !!contract },
|
||||
{ enabled: !!erc721 },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -206,11 +208,17 @@ export function useClaimNFT<TContract extends DropContract>(
|
||||
|
||||
return useMutation(
|
||||
async (data: ClaimNFTParams) => {
|
||||
invariant(data.to, 'No "to" address provided');
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
|
||||
if (erc1155) {
|
||||
invariant(!!data.tokenId, "tokenId not provided");
|
||||
requiredParamInvariant(data.tokenId, "tokenId not provided");
|
||||
if (!data.to) {
|
||||
return (await erc1155.claim(
|
||||
data.tokenId,
|
||||
data.quantity,
|
||||
data.options,
|
||||
)) as ClaimNFTReturnType;
|
||||
}
|
||||
return (await erc1155.claimTo(
|
||||
data.to,
|
||||
data.tokenId,
|
||||
@@ -219,6 +227,12 @@ export function useClaimNFT<TContract extends DropContract>(
|
||||
)) as ClaimNFTReturnType;
|
||||
}
|
||||
if (erc721) {
|
||||
if (!data.to) {
|
||||
return (await erc721.claim(
|
||||
data.quantity,
|
||||
data.options,
|
||||
)) as ClaimNFTReturnType;
|
||||
}
|
||||
return (await erc721.claimTo(
|
||||
data.to,
|
||||
data.quantity,
|
||||
@@ -258,7 +272,7 @@ export function useLazyMint<TContract extends DropContract>(
|
||||
|
||||
return useMutation(
|
||||
async (data: { metadatas: NFTMetadataInput[] }) => {
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
let options;
|
||||
if (onProgress) {
|
||||
options = {
|
||||
@@ -304,7 +318,7 @@ export function useDelayedRevealLazyMint<TContract extends RevealableContract>(
|
||||
|
||||
return useMutation(
|
||||
async (data: DelayedRevealLazyMintInput) => {
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
let options;
|
||||
if (onProgress) {
|
||||
options = {
|
||||
@@ -358,7 +372,7 @@ export function useRevealLazyMint<TContract extends RevealableContract>(
|
||||
|
||||
return useMutation(
|
||||
async (data: RevealLazyMintInput) => {
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
const { erc721, erc1155 } = getErcs(contract);
|
||||
if (erc721) {
|
||||
return await erc721.revealer.reveal(data.batchId, data.password);
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
RequiredParam,
|
||||
requiredParamInvariant,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import { BuyNowParams, MakeBidParams } from "../../types";
|
||||
import {
|
||||
AcceptDirectOffer,
|
||||
BuyNowParams,
|
||||
ExecuteAuctionSale,
|
||||
MakeBidParams,
|
||||
MakeOfferParams,
|
||||
} from "../../types";
|
||||
import {
|
||||
cacheKeys,
|
||||
invalidateContractAndBalances,
|
||||
} from "../../utils/cache-keys";
|
||||
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
|
||||
import { useAddress } from "../wallet";
|
||||
import { useContractEvents } from "./contracts";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type {
|
||||
AuctionListing,
|
||||
@@ -17,7 +27,7 @@ import type {
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { ListingType } from "@thirdweb-dev/sdk";
|
||||
import { Marketplace } from "@thirdweb-dev/sdk/dist/declarations/src/evm/contracts/prebuilt-implementations/marketplace";
|
||||
import { BigNumber, BigNumberish } from "ethers";
|
||||
import { BigNumberish } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/** **********************/
|
||||
@@ -45,8 +55,9 @@ export function useListing(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.marketplace.getListing(contractAddress, listingId),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
return contract.getListing(BigNumber.from(listingId || 0));
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(listingId, "No listing id provided");
|
||||
return contract.getListing(listingId);
|
||||
},
|
||||
{
|
||||
enabled: !!contract,
|
||||
@@ -76,7 +87,7 @@ export function useListings(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.marketplace.getAllListings(contractAddress, filter),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
return contract.getAllListings(filter);
|
||||
},
|
||||
{
|
||||
@@ -103,7 +114,7 @@ export function useListingsCount(contract: RequiredParam<Marketplace>) {
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.marketplace.getTotalCount(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
return contract.getTotalCount();
|
||||
},
|
||||
{
|
||||
@@ -133,7 +144,7 @@ export function useActiveListings(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.marketplace.getActiveListings(contractAddress, filter),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
return contract.getActiveListings(filter);
|
||||
},
|
||||
@@ -168,8 +179,9 @@ export function useWinningBid(
|
||||
listingId,
|
||||
),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
return contract.auction.getWinningBid(BigNumber.from(listingId || 0));
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(listingId, "No listing id provided");
|
||||
return contract.auction.getWinningBid(listingId);
|
||||
},
|
||||
{
|
||||
enabled: !!contract && listingId !== undefined,
|
||||
@@ -201,12 +213,11 @@ export function useAuctionWinner(
|
||||
listingId,
|
||||
),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(listingId, "No listing id provided");
|
||||
let winner: string | undefined;
|
||||
try {
|
||||
winner = await contract.auction.getWinner(
|
||||
BigNumber.from(listingId || 0),
|
||||
);
|
||||
winner = await contract.auction.getWinner(listingId);
|
||||
} catch (err) {
|
||||
if (!(err as Error)?.message?.includes("Could not find auction")) {
|
||||
throw err;
|
||||
@@ -238,7 +249,7 @@ export function useBidBuffer(contract: RequiredParam<Marketplace>) {
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.marketplace.getBidBufferBps(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
return contract.getBidBufferBps();
|
||||
},
|
||||
{
|
||||
@@ -247,6 +258,40 @@ export function useBidBuffer(contract: RequiredParam<Marketplace>) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to get the minimum next bid for the auction listing from your marketplace contract.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: minimumNextBid, isLoading, error } = useMinimumNextBid(<YourMarketplaceContractInstance>, <listingId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a marketplace contract
|
||||
* @param listingId - the listing id to check
|
||||
* @returns a response object that includes the minimum next bid for the auction listing
|
||||
* @beta
|
||||
*/
|
||||
export function useMinimumNextBid(
|
||||
contract: RequiredParam<Marketplace>,
|
||||
listingId: RequiredParam<BigNumberish>,
|
||||
) {
|
||||
const contractAddress = contract?.getAddress();
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.marketplace.auction.getWinner(
|
||||
contractAddress,
|
||||
listingId,
|
||||
),
|
||||
async () => {
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(listingId, "No listing id provided");
|
||||
return await contract.auction.getMinimumNextBid(listingId);
|
||||
},
|
||||
{
|
||||
enabled: !!contract && listingId !== undefined,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** **********************/
|
||||
/** WRITE HOOKS **/
|
||||
/** **********************/
|
||||
@@ -290,8 +335,9 @@ export function useCreateDirectListing(contract: RequiredParam<Marketplace>) {
|
||||
return useMutation(
|
||||
async (data: NewDirectListing) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot create listing");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
invariant(
|
||||
contract?.direct?.createListing,
|
||||
contract.direct.createListing,
|
||||
"contract does not support direct.createListing",
|
||||
);
|
||||
return await contract.direct.createListing(data);
|
||||
@@ -346,8 +392,9 @@ export function useCreateAuctionListing(contract: RequiredParam<Marketplace>) {
|
||||
return useMutation(
|
||||
async (data: NewAuctionListing) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot create listing");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
invariant(
|
||||
contract?.direct?.createListing,
|
||||
contract.direct.createListing,
|
||||
"contract does not support auction.createListing",
|
||||
);
|
||||
return await contract.auction.createListing(data);
|
||||
@@ -456,8 +503,9 @@ export function useMakeBid(contract: RequiredParam<Marketplace>) {
|
||||
return useMutation(
|
||||
async (data: MakeBidParams) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot make bid");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
invariant(
|
||||
contract?.auction?.makeBid,
|
||||
contract.auction.makeBid,
|
||||
"contract does not support auction.makeBid",
|
||||
);
|
||||
return await contract.auction.makeBid(data.listingId, data.bid);
|
||||
@@ -473,6 +521,203 @@ export function useMakeBid(contract: RequiredParam<Marketplace>) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to make an offer on direct or auction listing from your marketplace contract.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const {
|
||||
* mutate: makeOffer,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useMakeOffer(">>YourMarketplaceContractInstance<<");
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to make a bid", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => makeOffer({ listingId: 1, pricePerToken: 0.5, quantity: 1 })}
|
||||
* >
|
||||
* Bid!
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a Marketplace contract
|
||||
* @returns a mutation object that can be used to make a bid on an auction listing
|
||||
* @beta
|
||||
*/
|
||||
export function useMakeOffer(contract: RequiredParam<Marketplace>) {
|
||||
const activeChainId = useSDKChainId();
|
||||
const contractAddress = contract?.getAddress();
|
||||
const queryClient = useQueryClient();
|
||||
const walletAddress = useAddress();
|
||||
return useMutation(
|
||||
async (data: MakeOfferParams) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot make bid");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
return await contract.makeOffer(
|
||||
data.listingId,
|
||||
data.pricePerToken,
|
||||
data.quantity,
|
||||
);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
invalidateContractAndBalances(
|
||||
queryClient,
|
||||
contractAddress,
|
||||
activeChainId,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept an offer on a direct listing from an offeror, will accept the latest offer by the given offeror.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const {
|
||||
* mutate: acceptOffer,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useAcceptDirectListingOffer(">>YourMarketplaceContractInstance<<");
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to accept offer", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => acceptOffer({ listingId: 1, addressOfOfferor: "0x..." })}
|
||||
* >
|
||||
* Accept offer
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a Marketplace contract
|
||||
* @returns a mutation object that can be used to accept an offer on a direct listing
|
||||
* @beta
|
||||
*/
|
||||
export function useAcceptDirectListingOffer(
|
||||
contract: RequiredParam<Marketplace>,
|
||||
) {
|
||||
const activeChainId = useSDKChainId();
|
||||
const contractAddress = contract?.getAddress();
|
||||
const queryClient = useQueryClient();
|
||||
const walletAddress = useAddress();
|
||||
return useMutation(
|
||||
async (data: AcceptDirectOffer) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot make bid");
|
||||
requiredParamInvariant(contract?.direct, "No Direct instance provided");
|
||||
return await contract.direct.acceptOffer(
|
||||
data.listingId,
|
||||
data.addressOfOfferor,
|
||||
);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
invalidateContractAndBalances(
|
||||
queryClient,
|
||||
contractAddress,
|
||||
activeChainId,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute an auction sale. Can only be executed once the auction has ended and the auction has a winning bid.
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const {
|
||||
* mutate: executeAuctionSale,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useExecuteAuctionSale(">>YourMarketplaceContractInstance<<");
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to execute sale", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => executeAuctionSale({ listingId: 1 })}
|
||||
* >
|
||||
* Execute sale
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a Marketplace contract
|
||||
* @returns a mutation object that can be used to accept an offer on a direct listing
|
||||
* @beta
|
||||
*/
|
||||
export function useExecuteAuctionSale(contract: RequiredParam<Marketplace>) {
|
||||
const activeChainId = useSDKChainId();
|
||||
const contractAddress = contract?.getAddress();
|
||||
const queryClient = useQueryClient();
|
||||
const walletAddress = useAddress();
|
||||
return useMutation(
|
||||
async (data: ExecuteAuctionSale) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot make bid");
|
||||
requiredParamInvariant(
|
||||
contract?.auction,
|
||||
"No Auction marketplace instance provided",
|
||||
);
|
||||
return await contract.auction.executeSale(data.listingId);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
invalidateContractAndBalances(
|
||||
queryClient,
|
||||
contractAddress,
|
||||
activeChainId,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the offers for a listing
|
||||
*
|
||||
* @remarks Fetch all the offers for a specified direct or auction listing.
|
||||
* @example
|
||||
* ```javascript
|
||||
* const { data: offers, isLoading, error } = useOffers(<YourMarketplaceContractInstance>, <listingId>);
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a Marketplace contract
|
||||
* @param listingId - the id of the listing to fetch offers for
|
||||
* @beta
|
||||
*/
|
||||
export function useOffers(
|
||||
contract: RequiredParam<Marketplace>,
|
||||
listingId: RequiredParam<BigNumberish>,
|
||||
) {
|
||||
const result = useContractEvents(contract, "NewOffer");
|
||||
return {
|
||||
...result,
|
||||
data: result.data
|
||||
?.filter((ev) => ev.data.listingId.eq(listingId))
|
||||
?.map((ev) => ev.data),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this to buy out an auction listing from your marketplace contract.
|
||||
*
|
||||
@@ -512,9 +757,10 @@ export function useBuyNow(contract: RequiredParam<Marketplace>) {
|
||||
return useMutation(
|
||||
async (data: BuyNowParams) => {
|
||||
invariant(walletAddress, "no wallet connected, cannot make bid");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
if (data.type === ListingType.Direct) {
|
||||
invariant(
|
||||
contract?.direct.buyoutListing,
|
||||
contract.direct.buyoutListing,
|
||||
"contract does not support direct.buyoutListing",
|
||||
);
|
||||
|
||||
@@ -525,7 +771,7 @@ export function useBuyNow(contract: RequiredParam<Marketplace>) {
|
||||
);
|
||||
}
|
||||
invariant(
|
||||
contract?.auction?.buyoutListing,
|
||||
contract.auction.buyoutListing,
|
||||
"contract does not support auction.buyoutListing",
|
||||
);
|
||||
return await contract.auction.buyoutListing(data.id);
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import {
|
||||
AirdropNFTParams,
|
||||
@@ -50,7 +53,7 @@ export function useNFT<TContract extends NFTContract>(
|
||||
return useQueryWithNetwork<NFT>(
|
||||
cacheKeys.contract.nft.get(contractAddress, tokenId),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
if (erc1155) {
|
||||
invariant(erc1155.get, "Contract instance does not support get");
|
||||
@@ -93,7 +96,7 @@ export function useNFTs<TContract extends NFTContract>(
|
||||
return useQueryWithNetwork<NFT[]>(
|
||||
cacheKeys.contract.nft.query.all(contractAddress, queryParams),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
if (erc721) {
|
||||
invariant(erc721.getAll, "Contract instance does not support getAll");
|
||||
@@ -134,7 +137,7 @@ export function useTotalCount<TContract extends NFTContract>(
|
||||
return useQueryWithNetwork<BigNumber>(
|
||||
cacheKeys.contract.nft.query.totalCount(contractAddress),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
if (erc1155) {
|
||||
invariant(
|
||||
@@ -182,14 +185,14 @@ export function useTotalCirculatingSupply(
|
||||
return useQueryWithNetwork<BigNumber>(
|
||||
cacheKeys.contract.nft.query.totalCirculatingSupply(contractAddress),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
if (erc1155) {
|
||||
invariant(
|
||||
erc1155.totalCirculatingSupply,
|
||||
"Contract instance does not support totalCirculatingSupply",
|
||||
);
|
||||
invariant(tokenId, "No tokenId provided");
|
||||
requiredParamInvariant(tokenId, "No tokenId provided");
|
||||
return await erc1155.totalCirculatingSupply(tokenId);
|
||||
}
|
||||
if (erc721) {
|
||||
@@ -232,7 +235,7 @@ export function useOwnedNFTs<TContract extends NFTContract>(
|
||||
return useQueryWithNetwork<NFT[]>(
|
||||
cacheKeys.contract.nft.query.owned.all(contractAddress, ownerWalletAddress),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
invariant(ownerWalletAddress, "No wallet address provided");
|
||||
if (erc721) {
|
||||
return await erc721.getOwned(ownerWalletAddress);
|
||||
@@ -277,11 +280,11 @@ export function useNFTBalance(
|
||||
tokenId,
|
||||
),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
invariant(ownerWalletAddress, "No owner wallet address provided");
|
||||
if (erc1155) {
|
||||
invariant(tokenId, "No tokenId provided");
|
||||
requiredParamInvariant(tokenId, "No tokenId provided");
|
||||
invariant(
|
||||
erc1155.balanceOf,
|
||||
"Contract instance does not support balanceOf",
|
||||
@@ -375,7 +378,7 @@ export function useMintNFT<TContract extends NFTContract>(
|
||||
return useMutation(
|
||||
async (data: MintNFTParams) => {
|
||||
invariant(data.to, 'No "to" address provided');
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
if (erc1155) {
|
||||
invariant("supply" in data, "supply not provided");
|
||||
const { to, metadata, supply } = data;
|
||||
@@ -444,9 +447,9 @@ export function useMintNFTSupply(contract: Erc1155) {
|
||||
return useMutation(
|
||||
async (data: MintNFTSupplyParams) => {
|
||||
invariant(data.to, 'No "to" address provided');
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
|
||||
invariant("tokenId" in data, "tokenId not provided");
|
||||
requiredParamInvariant(data.tokenId, "tokenId not provided");
|
||||
invariant("additionalSupply" in data, "additionalSupply not provided");
|
||||
const { to, tokenId, additionalSupply } = data;
|
||||
return await contract.mintAdditionalSupplyTo(
|
||||
@@ -512,7 +515,7 @@ export function useTransferNFT<TContract extends NFTContract>(
|
||||
invariant("to" in data, "to not provided");
|
||||
if (erc1155) {
|
||||
invariant(erc1155.transfer, "contract does not support transfer");
|
||||
invariant("tokenId" in data, "tokenId not provided");
|
||||
requiredParamInvariant(data.tokenId, "tokenId not provided");
|
||||
invariant("amount" in data, "amount not provided");
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return erc1155.transfer(data.to, data.tokenId, data.amount!);
|
||||
@@ -539,7 +542,7 @@ export function useTransferNFT<TContract extends NFTContract>(
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const editionDrop = useEditionDrop(<ContractAddress>);
|
||||
* const editionDrop = useContract(<ContractAddress>, "edition-drop");
|
||||
* const {
|
||||
* mutate: airdropNFT,
|
||||
* isLoading,
|
||||
@@ -602,7 +605,8 @@ export function useAirdropNFT(contract: Erc1155) {
|
||||
|
||||
return useMutation(
|
||||
({ tokenId, addresses }: AirdropNFTParams) => {
|
||||
invariant(contract?.airdrop, "contract does not support airdrop");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
invariant(contract.airdrop, "contract does not support airdrop");
|
||||
|
||||
return contract.airdrop(tokenId, addresses);
|
||||
},
|
||||
@@ -688,8 +692,8 @@ export function useBurnNFT<TContract extends NFTContract>(
|
||||
|
||||
return useMutation(
|
||||
async (data: BurnNFTParams) => {
|
||||
invariant(data.tokenId, "No tokenId provided");
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(data.tokenId, "No tokenId provided");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
if (erc1155) {
|
||||
invariant("amount" in data, "amount not provided");
|
||||
const { tokenId, amount } = data;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import { WalletAddress } from "../../types";
|
||||
import {
|
||||
@@ -61,7 +64,7 @@ export function useAllRoleMembers<TContract extends ContractWithRoles>(
|
||||
return useQueryWithNetwork<Awaited<GetAllReturnType<TContract>>>(
|
||||
cacheKeys.extensions.roles.getAll(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(contract.roles, "Contract does not support roles");
|
||||
// have to cast to any because of role bs, type is defined in the useQueryWithNetwork definition above
|
||||
return contract.roles.getAll() as any;
|
||||
@@ -94,7 +97,7 @@ export function useRoleMembers<TContract extends ContractWithRoles>(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.extensions.roles.get(contractAddress, role),
|
||||
() => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(contract.roles, "Contract does not support roles");
|
||||
return contract.roles.get(role);
|
||||
},
|
||||
@@ -192,7 +195,7 @@ export function useSetAllRoleMembers<TContract extends ContractWithRoles>(
|
||||
async (rolesWithAddresses: {
|
||||
[role in RolesForContract<TContract>]: string[];
|
||||
}) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(contract.roles, "Contract does not support roles");
|
||||
await contract.roles.setAll(rolesWithAddresses);
|
||||
},
|
||||
@@ -253,7 +256,7 @@ export function useGrantRole<TContract extends ContractWithRoles>(
|
||||
role: RolesForContract<TContract>;
|
||||
address: WalletAddress;
|
||||
}) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(contract.roles, "Contract does not support roles");
|
||||
await contract.roles.grant(params.role as any, params.address);
|
||||
},
|
||||
@@ -311,7 +314,7 @@ export function useRevokeRole<TContract extends ContractWithRoles>(
|
||||
role: RolesForContract<TContract>;
|
||||
address: WalletAddress;
|
||||
}) => {
|
||||
invariant(contract, "No contract provided");
|
||||
requiredParamInvariant(contract, "No contract provided");
|
||||
invariant(contract.roles, "Contract does not support roles");
|
||||
await contract.roles.revoke(params.role as any, params.address);
|
||||
},
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../../providers/base";
|
||||
import {
|
||||
ClaimTokenParams,
|
||||
@@ -14,7 +17,6 @@ import {
|
||||
} from "../../utils/cache-keys";
|
||||
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { Erc20 } from "@thirdweb-dev/sdk";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/** **********************/
|
||||
@@ -40,7 +42,7 @@ export function useTokenSupply(contract: RequiredParam<TokenContract>) {
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.token.totalSupply(contractAddress),
|
||||
() => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
const erc20 = getErc20(contract);
|
||||
if (erc20) {
|
||||
return erc20.totalSupply();
|
||||
@@ -76,7 +78,7 @@ export function useTokenBalance(
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.token.balanceOf(contractAddress, walletAddress),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
invariant(walletAddress, "No address provided");
|
||||
if (erc20) {
|
||||
return await erc20.balanceOf(walletAddress);
|
||||
@@ -110,7 +112,7 @@ export function useTokenDecimals(contract: RequiredParam<TokenContract>) {
|
||||
return useQueryWithNetwork(
|
||||
cacheKeys.contract.token.decimals(contractAddress),
|
||||
async () => {
|
||||
invariant(contract, "No Contract instance provided");
|
||||
requiredParamInvariant(contract, "No Contract instance provided");
|
||||
|
||||
if (erc20) {
|
||||
return (await erc20.get()).decimals;
|
||||
@@ -169,7 +171,7 @@ export function useMintToken(contract: RequiredParam<TokenContract>) {
|
||||
return useMutation(
|
||||
(data: TokenParams) => {
|
||||
const { to, amount } = data;
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
if (erc20) {
|
||||
return erc20.mintTo(to, amount);
|
||||
}
|
||||
@@ -230,11 +232,9 @@ export function useClaimToken(contract: RequiredParam<TokenContract>) {
|
||||
invariant(data.to, 'No "to" address provided');
|
||||
if (erc20) {
|
||||
invariant(erc20?.claimTo, "contract does not support claimTo");
|
||||
return await erc20.claimTo(
|
||||
data.to,
|
||||
data.amount,
|
||||
data.checkERC20Allowance,
|
||||
);
|
||||
return await erc20.claimTo(data.to, data.amount, {
|
||||
checkERC20Allowance: data.checkERC20Allowance,
|
||||
});
|
||||
}
|
||||
invariant(false, "Smart contract is not a valid erc20 contract");
|
||||
},
|
||||
@@ -351,7 +351,7 @@ export function useTransferBatchToken(contract: RequiredParam<TokenContract>) {
|
||||
(data: TokenParams[]) => {
|
||||
if (erc20) {
|
||||
invariant(
|
||||
erc20?.transferBatch,
|
||||
erc20.transferBatch,
|
||||
"contract does not support transferBatch",
|
||||
);
|
||||
const convertedData = data.map((token) => ({
|
||||
@@ -416,9 +416,9 @@ export function useBurnToken(contract: RequiredParam<TokenContract>) {
|
||||
return useMutation(
|
||||
(data: TokenBurnParams) => {
|
||||
const { amount } = data;
|
||||
invariant(contract, "contract is undefined");
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
if (erc20) {
|
||||
invariant(erc20?.burn, "contract does not support burn");
|
||||
invariant(erc20.burn, "contract does not support burn");
|
||||
return erc20.burn(amount);
|
||||
}
|
||||
invariant(false, "Smart contract is not a valid erc20 contract");
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import {
|
||||
GnosisConnectorArguments,
|
||||
GnosisSafeConnector,
|
||||
} from "../../connectors/gnosis-safe";
|
||||
import { useConnect } from "../wagmi-required/useConnect";
|
||||
import { utils } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useContext as useWagmiContext } from "wagmi";
|
||||
|
||||
/**
|
||||
* Hook for connecting to a Gnosis Safe. This enables multisig wallets to connect to your application and sing transactions.
|
||||
*
|
||||
* ```javascript
|
||||
* import { useGnosis } from "@thirdweb-dev/react"
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useGnosis } from "@thirdweb-dev/react"
|
||||
*
|
||||
* const App = () => {
|
||||
* const connectWithGnosis = useGnosis()
|
||||
*
|
||||
* return (
|
||||
* <button onClick={() => connectWithGnosis({ safeAddress: "0x...", safeChainId: 1 })}>
|
||||
* Connect Gnosis Safe
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useGnosis() {
|
||||
const wagmiContext = useWagmiContext();
|
||||
invariant(
|
||||
wagmiContext,
|
||||
`useGnosis() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
|
||||
);
|
||||
const [connectors, connect] = useConnect();
|
||||
if (connectors.loading) {
|
||||
return () => Promise.reject("Gnosis connector not ready to be used, yet");
|
||||
}
|
||||
const connector = connectors.data.connectors.find((c) => c.id === "gnosis");
|
||||
invariant(
|
||||
connector,
|
||||
"Gnosis connector not found, please make sure it is provided to your <ThirdwebProvider />",
|
||||
);
|
||||
|
||||
return async (config: GnosisConnectorArguments) => {
|
||||
const previousConnector = connectors.data.connector;
|
||||
const previousConnectorChain = await previousConnector?.getChainId();
|
||||
invariant(
|
||||
!!previousConnector,
|
||||
"Cannot connect to Gnosis Safe without first being connected to a personal wallet.",
|
||||
);
|
||||
invariant(
|
||||
previousConnectorChain === config.safeChainId,
|
||||
"Gnosis safe chain id must match personal wallet chain id.",
|
||||
);
|
||||
invariant(
|
||||
utils.isAddress(config.safeAddress),
|
||||
"Gnosis safe address must be a valid address.",
|
||||
);
|
||||
(connector as GnosisSafeConnector).setConfiguration(
|
||||
previousConnector,
|
||||
config,
|
||||
);
|
||||
return connect(connector);
|
||||
};
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import { MagicConnector } from "../../connectors/magic";
|
||||
import { useConnect } from "../wagmi-required/useConnect";
|
||||
import { LoginWithMagicLinkConfiguration } from "magic-sdk";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useContext as useWagmiContext } from "wagmi";
|
||||
|
||||
/**
|
||||
* Hook for connecting to an email wallet using magic link.
|
||||
* This enables users without their own wallets to connect to your application and sign transactions securely using their email.
|
||||
*
|
||||
* ```javascript
|
||||
* import { useMagic } from "@thirdweb-dev/react"
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* Before using this hook, you first need to set up the magic configuration in your `ThirdwebProvider`, including your magic API key.
|
||||
*
|
||||
* ```javascript
|
||||
* // Add the magic configuration object to your wallet connectors
|
||||
* const connectors = [
|
||||
* "metamask",
|
||||
* "walletConnect",
|
||||
* "walletLink",
|
||||
* {
|
||||
* name: "magic",
|
||||
* options: {
|
||||
* apiKey: "your-magic-api-key",
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
* // Add the above to the walletConnectors prop of your <ThirdwebProvider />
|
||||
* const Provider = ({ children }) => (
|
||||
* return (
|
||||
* <ThirdwebProvider
|
||||
* walletConnectors={connectors}
|
||||
* // Specify remaining parameters
|
||||
* ...
|
||||
* >
|
||||
* {children}
|
||||
* </ThirdwebProvider>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In order to use the hook to connect users with magic link, you just need to provide the users email to the connect function.
|
||||
*
|
||||
* You can setup the hook with the following configuration:
|
||||
* ```javascript
|
||||
* import { useMagic } from "@thirdweb-dev/react"
|
||||
* import { useState } from "react"
|
||||
*
|
||||
* const LoginWithMagicLink = () => {
|
||||
* const connectWithMagic = useMagic()
|
||||
* const [email, setEmail] = useState()
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <input value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
* <button onClick={() => connectWithMagic({ email })}>Login</button>
|
||||
* </div>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useMagic() {
|
||||
const wagmiContext = useWagmiContext();
|
||||
invariant(
|
||||
wagmiContext,
|
||||
`useMagic() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own wallet-connection logic.`,
|
||||
);
|
||||
const [connectors, connect] = useConnect();
|
||||
if (connectors.loading) {
|
||||
return () => Promise.reject("Magic connector not ready to be used, yet");
|
||||
}
|
||||
|
||||
const connector = connectors.data.connectors.find((c) => c.id === "magic");
|
||||
|
||||
invariant(
|
||||
connector,
|
||||
"Magic connector not found, please make sure it is provided to your <ThirdwebProvider />",
|
||||
);
|
||||
|
||||
return (configuration: LoginWithMagicLinkConfiguration) => {
|
||||
(connector as MagicConnector).setConfiguration(configuration);
|
||||
return connect(connector);
|
||||
};
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useConnect } from "../wagmi-required/useConnect";
|
||||
import { Buffer } from "buffer";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useContext as useWagmiContext } from "wagmi";
|
||||
|
||||
if (!globalThis.Buffer) {
|
||||
globalThis.Buffer = require("buffer/").Buffer;
|
||||
}
|
||||
globalThis.Buffer = Buffer;
|
||||
|
||||
/**
|
||||
* Hook for connecting to a mobile wallet with Wallet Connect
|
||||
*
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useConnect } from "../wagmi-required/useConnect";
|
||||
import { Buffer } from "buffer";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useContext as useWagmiContext } from "wagmi";
|
||||
|
||||
if (!globalThis.Buffer) {
|
||||
globalThis.Buffer = require("buffer/").Buffer;
|
||||
}
|
||||
globalThis.Buffer = Buffer;
|
||||
|
||||
/**
|
||||
* Hook for connecting to a Coinbase wallet.
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useEdition } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const edition = useEdition("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "edition")
|
||||
*
|
||||
* // Now you can use the edition contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will return all the NFTs on this contract
|
||||
* async function getNFTs() {
|
||||
* const nfts = await edition.getAll()
|
||||
* const nfts = await contract.getAll()
|
||||
* return nfts
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const edition = await sdk.useEdition("0x1234...");
|
||||
* + const edition = await sdk.useContract("0x1234...", "edition").contract;
|
||||
* - const edition = useEdition("0x1234...");
|
||||
* + const edition = useContract("0x1234...", "edition").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useEdition(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useEditionDrop } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const editionDrop = useEditionDrop("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "edition-drop")
|
||||
*
|
||||
* // Now you can use the edition drop contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will let the connected wallet claim a new NFT
|
||||
* async function claim(tokenId, quantity) {
|
||||
* await editionDrop.claim(tokenId, quantity)
|
||||
* await contract.claim(tokenId, quantity)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const editionDrop = await sdk.useEditionDrop("0x1234...");
|
||||
* + const editionDrop = await sdk.useContract("0x1234...", "edition-drop").contract;
|
||||
* - const editionDrop = useEditionDrop("0x1234...");
|
||||
* + const editionDrop = useContract("0x1234...", "edition-drop").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useEditionDrop(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useMarketplace } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const marketplace = useMarketplace("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "marketplace")
|
||||
*
|
||||
* // Now you can use the marketplace contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will return all the listings on the marketplace
|
||||
* async function getListings() {
|
||||
* const listings = await marketplace.getAll()
|
||||
* const listings = await contract.getAll()
|
||||
* return listings
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const marketplace = await sdk.useMarketplace("0x1234...");
|
||||
* + const marketplace = await sdk.useContract("0x1234...", "marketplace").contract;
|
||||
* - const marketplace = useMarketplace("0x1234...");
|
||||
* + const marketplace = useContract("0x1234...", "marketplace").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useMarketplace(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useMultiwrap } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const multiwrap = useMultiwrap("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "multiwrap")
|
||||
*
|
||||
* // Now you can use the multiwrap contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will let the connected wallet wrap tokens
|
||||
* async function wrap(tokensToWrap, wrappedNFTMetadata) {
|
||||
* await multiwrap.wrap(tokensToWrap, wrappedNFTMetadata)
|
||||
* await contract.wrap(tokensToWrap, wrappedNFTMetadata)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const multiwrap = await sdk.useMultiwrap("0x1234...");
|
||||
* + const multiwrap = await sdk.useContract("0x1234...", "multiwrap").contract;
|
||||
* - const multiwrap = useMultiwrap("0x1234...");
|
||||
* + const multiwrap = useContract("0x1234...", "multiwrap").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useMultiwrap(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useNFTCollection } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const nftCollection = useNFTCollection("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract, isLoading, error } = useContract("<YOUR-CONTRACT-ADDRESS>", "nft-collection")
|
||||
*
|
||||
* // Now you can use the nftCollection contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will return all the NFTs on this contract
|
||||
* async function getNFTs() {
|
||||
* const nfts = await nftCollection.getAll()
|
||||
* const nfts = await contract.getAll()
|
||||
* return nfts
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const nftCollection = await sdk.useNFTCollection("0x1234...");
|
||||
* + const nftCollection = await sdk.useContract("0x1234...", "nft-collection").contract;
|
||||
* - const nftCollection = useNFTCollection("0x1234...");
|
||||
* + const nftCollection = useContract("0x1234...", "nft-collection").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useNFTCollection(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useNFTDrop } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const nftDrop = useNFTDrop("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "nft-drop")
|
||||
*
|
||||
* // Now you can use the nft drop contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will let the connected wallet claim a new NFT
|
||||
* async function claim(quantity) {
|
||||
* await nftDrop.claim(quantity)
|
||||
* await contract.claim(quantity)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const nftDrop = await sdk.useNFTDrop("0x1234...");
|
||||
* + const nftDrop = await sdk.useContract("0x1234...", "nft-drop").contract;
|
||||
* - const nftDrop = useNFTDrop("0x1234...");
|
||||
* + const nftDrop = useContract("0x1234...", "nft-drop").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useNFTDrop(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { usePack } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const pack = usePack("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = usePack("<YOUR-CONTRACT-ADDRESS>", "pack")
|
||||
*
|
||||
* // Now you can use the pack contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will get all the packs on this contract
|
||||
* async function getPacks() {
|
||||
* const packs = await pack.getAll()
|
||||
* const packs = await contract.getAll()
|
||||
* return packs
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const pack = await sdk.usePack("0x1234...");
|
||||
* + const pack = await sdk.useContract("0x1234...", "pack").contract;
|
||||
* - const pack = usePack("0x1234...");
|
||||
* + const pack = useContract("0x1234...", "pack").contract;
|
||||
* ```
|
||||
*/
|
||||
export function usePack(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useSignatureDrop } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const signatureDrop = useSignatureDrop("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "signature-drop")
|
||||
*
|
||||
* // Now you can use the Signature drop contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will let the connected wallet claim a new NFT
|
||||
* async function claim(quantity) {
|
||||
* await signatureDrop.claim(quantity)
|
||||
* await contract.claim(quantity)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
@@ -27,8 +27,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const signatureDrop = await sdk.useSignatureDrop("0x1234...");
|
||||
* + const signatureDrop = await sdk.useContract("0x1234...", "signature-drop").contract;
|
||||
* - const signatureDrop = useSignatureDrop("0x1234...");
|
||||
* + const signatureDrop = useContract("0x1234...", "signature-drop").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useSignatureDrop(contractAddress?: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useSplit } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const split = useSplit("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "split")
|
||||
*
|
||||
* // Now you can use the split contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will retrun all the receipients of the split
|
||||
* async function getRecipients() {
|
||||
* const recipients = await split.getAllRecipients()
|
||||
* const recipients = await contract.getAllRecipients()
|
||||
* return recipients
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const split = await sdk.useSplit("0x1234...");
|
||||
* + const split = await sdk.useContract("0x1234...", "split").contract;
|
||||
* - const split = useSplit("0x1234...");
|
||||
* + const split = useContract("0x1234...", "split").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useSplit(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useToken } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const token = useToken("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "token")
|
||||
*
|
||||
* // Now you can use the token contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will get the connected wallets token balance
|
||||
* async function balance() {
|
||||
* const balance = await token.balance()
|
||||
* const balance = await contract.balance()
|
||||
* return balance
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const token = await sdk.useToken("0x1234...");
|
||||
* + const token = await sdk.useContract("0x1234...", "token").contract;
|
||||
* - const token = useToken("0x1234...");
|
||||
* + const token = useContract("0x1234...", "token").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useToken(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useTokenDrop } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const tokenDrop = useTokenDrop("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "token-drop")
|
||||
*
|
||||
* // Now you can use the token drop contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will get the connected wallets token balance
|
||||
* async function balance() {
|
||||
* const balance = await tokenDrop.balance()
|
||||
* const balance = await contract.balance()
|
||||
* return balance
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const token = await sdk.useTokenDrop("0x1234...");
|
||||
* + const token = await sdk.useContract("0x1234...", "token-drop").contract;
|
||||
* - const token = useTokenDrop("0x1234...");
|
||||
* + const token = useContract("0x1234...", "token-drop").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useTokenDrop(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { showDeprecationWarning } from "../../utils/deprecation-warning";
|
||||
import { useContract } from "../async/contracts";
|
||||
|
||||
@@ -8,16 +8,16 @@ import { useContract } from "../async/contracts";
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* import { useVote } from '@thirdweb-dev/react'
|
||||
* import { useContract } from '@thirdweb-dev/react'
|
||||
*
|
||||
* export default function Component() {
|
||||
* const vote = useVote("<YOUR-CONTRACT-ADDRESS>")
|
||||
* const { contract } = useContract("<YOUR-CONTRACT-ADDRESS>", "vote")
|
||||
*
|
||||
* // Now you can use the vote contract in the rest of the component
|
||||
*
|
||||
* // For example, this function will get all the proposals on this contract
|
||||
* async function getProposals() {
|
||||
* const proposals = await vote.getAll()
|
||||
* const proposals = await contract.getAll()
|
||||
* return proposals
|
||||
* }
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import { useContract } from "../async/contracts";
|
||||
* @deprecated
|
||||
* This hook is deprecated and will be removed in a future major version. You should use {@link useContract} instead.
|
||||
* ```diff
|
||||
* - const vote = await sdk.useVote("0x1234...");
|
||||
* + const vote = await sdk.useContract("0x1234...", "vote").contract;
|
||||
* - const vote = useVote("0x1234...");
|
||||
* + const vote = useContract("0x1234...", "vote").contract;
|
||||
* ```
|
||||
*/
|
||||
export function useVote(contractAddress: RequiredParam<string>) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { useSDK } from "../../providers/base";
|
||||
|
||||
export * from "./useStorageUpload";
|
||||
|
||||
/**
|
||||
* Get the configured `ThirdwebStorage` instance
|
||||
* @returns The `storageInterface` configured on the `ThirdwebProvider`
|
||||
*/
|
||||
export function useStorage() {
|
||||
const sdk = useSDK();
|
||||
return sdk?.storage;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { useSDK } from "../../providers/base";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { IpfsUploadBatchOptions, UploadOptions } from "@thirdweb-dev/storage";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
interface StorageUploadOptions<T extends UploadOptions> {
|
||||
data: unknown[];
|
||||
options?: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook used to upload any files or JSON data to decentralized storage systems like IPFS,
|
||||
* using the `storageInterface` configured on the `ThirdwebProvider`
|
||||
*
|
||||
* @param options - Configure the options for your upload
|
||||
* @returns Function used to upload files or JSON to decentralized storage systems
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useStorageUpload } from "@thirdweb-dev/react";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { mutateAsync: upload, isLoading } = useStorageUpload();
|
||||
*
|
||||
* async function uploadData() {
|
||||
* const filesToUpload = [...];
|
||||
* const uris = await upload({ data: files });
|
||||
* console.log(uris);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button onClick={uploadData}>
|
||||
* Upload
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function useStorageUpload<
|
||||
T extends UploadOptions = IpfsUploadBatchOptions,
|
||||
>(uploadOptions?: T) {
|
||||
const sdk = useSDK();
|
||||
|
||||
return useMutation(async ({ data, options }: StorageUploadOptions<T>) => {
|
||||
invariant(sdk, "sdk must be defined");
|
||||
|
||||
return await sdk.storage.uploadBatch(data, options || uploadOptions);
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { GnosisSafeConnector } from "../../connectors/gnosis-safe";
|
||||
import { useConnect } from "./useConnect";
|
||||
import invariant from "tiny-invariant";
|
||||
import { useAccount, useContext as useWagmiContext } from "wagmi";
|
||||
import { Connector, useAccount, useContext as useWagmiContext } from "wagmi";
|
||||
|
||||
/**
|
||||
* Hook for disconnecting the currently connected wallet
|
||||
@@ -31,23 +30,22 @@ import { useAccount, useContext as useWagmiContext } from "wagmi";
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useDisconnect(options?: { reconnectAfterGnosis?: boolean }) {
|
||||
export function useDisconnect(options?: { reconnectPrevious?: boolean }) {
|
||||
const wagmiContext = useWagmiContext();
|
||||
invariant(
|
||||
wagmiContext,
|
||||
`useDisconnect() can only be used inside <ThirdwebProvider />. If you are using <ThirdwebSDKProvider /> you will have to use your own connection logic.`,
|
||||
);
|
||||
const optsWithDefaults = { ...{ reconnectAfterGnosis: true }, ...options };
|
||||
const optsWithDefaults = { ...{ reconnectPrevious: true }, ...options };
|
||||
const [, connect] = useConnect();
|
||||
const [data, disconnect] = useAccount();
|
||||
|
||||
return async () => {
|
||||
const previousConnector =
|
||||
(data.data?.connector instanceof GnosisSafeConnector &&
|
||||
data.data.connector.previousConnector) ||
|
||||
undefined;
|
||||
// if there is a previous connector get it
|
||||
const previousConnector: Connector | undefined =
|
||||
(data?.data?.connector as any)?.previousConnector || undefined;
|
||||
// if it's gnosis, just connect the previous connector
|
||||
if (optsWithDefaults.reconnectAfterGnosis && previousConnector) {
|
||||
if (optsWithDefaults.reconnectPrevious && previousConnector) {
|
||||
try {
|
||||
return await connect(previousConnector);
|
||||
} catch (err) {
|
||||
|
||||
@@ -15,8 +15,9 @@ export * from "./hooks/async/claim-conditions";
|
||||
export * from "./hooks/async/contract-settings";
|
||||
export * from "./hooks/async/roles";
|
||||
|
||||
// thirdweb hooks (work as long as at least `<ThirdwebSDkProvider>` is used)
|
||||
// thirdweb hooks (work as long as at least `<ThirdwebSdkProvider>` is used)
|
||||
export * from "./hooks/auth";
|
||||
export * from "./hooks/storage";
|
||||
export * from "./hooks/useSigner";
|
||||
export * from "./hooks/useReadonlySDK";
|
||||
export * from "./hooks/useNetworkMismatch";
|
||||
@@ -30,8 +31,6 @@ export * from "./hooks/wagmi-required/useConnect";
|
||||
export * from "./hooks/connectors/useMetamask";
|
||||
export * from "./hooks/connectors/useWalletConnect";
|
||||
export * from "./hooks/connectors/useWalletLink";
|
||||
export * from "./hooks/connectors/useGnosis";
|
||||
export * from "./hooks/connectors/useMagic";
|
||||
|
||||
// re-exports
|
||||
export { ChainId } from "@thirdweb-dev/sdk";
|
||||
|
||||
@@ -1,90 +1,93 @@
|
||||
import { useState, useCallback, useLayoutEffect, useEffect, useRef, PropsWithChildren } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import {
|
||||
useState,
|
||||
useCallback,
|
||||
useLayoutEffect,
|
||||
useEffect,
|
||||
useRef,
|
||||
PropsWithChildren,
|
||||
} from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
|
||||
function canUseDOM() {
|
||||
function canUseDOM() {
|
||||
return !!(
|
||||
typeof window !== "undefined" &&
|
||||
window.document &&
|
||||
window.document.createElement
|
||||
);
|
||||
typeof window !== "undefined" &&
|
||||
window.document &&
|
||||
window.document.createElement
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a re-render, similar to `forceUpdate` in class components.
|
||||
*/
|
||||
function useForceUpdate() {
|
||||
const [, dispatch] = useState<{}>(Object.create(null));
|
||||
return useCallback(() => {
|
||||
dispatch(Object.create(null));
|
||||
}, []);
|
||||
const [, dispatch] = useState<{}>(Object.create(null));
|
||||
return useCallback(() => {
|
||||
dispatch(Object.create(null));
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
const useIsomorphicLayoutEffect = canUseDOM() ? useLayoutEffect : useEffect;
|
||||
|
||||
/**
|
||||
* Portal from `@reach/portal`
|
||||
*
|
||||
* @see Docs https://reach.tech/portal#portal
|
||||
*/
|
||||
const PortalImpl: React.FC<PortalProps> = ({
|
||||
children,
|
||||
type = "reach-portal",
|
||||
containerRef,
|
||||
}) => {
|
||||
const mountNode = useRef<HTMLDivElement | null>(null);
|
||||
const portalNode = useRef<HTMLElement | null>(null);
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
/**
|
||||
* Portal from `@reach/portal`
|
||||
*
|
||||
* @see Docs https://reach.tech/portal#portal
|
||||
*/
|
||||
const PortalImpl: React.FC<PortalProps> = ({
|
||||
children,
|
||||
type = "reach-portal",
|
||||
containerRef,
|
||||
}) => {
|
||||
const mountNode = useRef<HTMLDivElement | null>(null);
|
||||
const portalNode = useRef<HTMLElement | null>(null);
|
||||
const forceUpdate = useForceUpdate();
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
// This ref may be null when a hot-loader replaces components on the page
|
||||
if (!mountNode.current) {
|
||||
return;
|
||||
}
|
||||
// It's possible that the content of the portal has, itself, been portaled.
|
||||
// In that case, it's important to append to the correct document element.
|
||||
const ownerDocument = mountNode.current.ownerDocument;
|
||||
const body = containerRef?.current || ownerDocument.body;
|
||||
portalNode.current = ownerDocument?.createElement(type);
|
||||
body.appendChild(portalNode.current);
|
||||
forceUpdate();
|
||||
return () => {
|
||||
if (portalNode.current && body) {
|
||||
body.removeChild(portalNode.current);
|
||||
}
|
||||
};
|
||||
}, [type, forceUpdate, containerRef]);
|
||||
|
||||
return portalNode.current ? (
|
||||
createPortal(children, portalNode.current)
|
||||
) : (
|
||||
<span ref={mountNode} />
|
||||
);
|
||||
};
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
// This ref may be null when a hot-loader replaces components on the page
|
||||
if (!mountNode.current) {return;}
|
||||
// It's possible that the content of the portal has, itself, been portaled.
|
||||
// In that case, it's important to append to the correct document element.
|
||||
const ownerDocument = mountNode.current.ownerDocument;
|
||||
const body = containerRef?.current || ownerDocument.body;
|
||||
portalNode.current = ownerDocument?.createElement(type);
|
||||
body.appendChild(portalNode.current);
|
||||
forceUpdate();
|
||||
return () => {
|
||||
if (portalNode.current && body) {
|
||||
body.removeChild(portalNode.current);
|
||||
}
|
||||
};
|
||||
}, [type, forceUpdate, containerRef]);
|
||||
export const Portal: React.FC<PortalProps> = ({
|
||||
unstable_skipInitialRender,
|
||||
...props
|
||||
}) => {
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
useEffect(() => {
|
||||
if (unstable_skipInitialRender) {
|
||||
setHydrated(true);
|
||||
}
|
||||
}, [unstable_skipInitialRender]);
|
||||
if (unstable_skipInitialRender && !hydrated) {
|
||||
return null;
|
||||
}
|
||||
return <PortalImpl {...props} />;
|
||||
};
|
||||
|
||||
return portalNode.current ? (
|
||||
createPortal(children, portalNode.current)
|
||||
) : (
|
||||
<span ref={mountNode} />
|
||||
);
|
||||
};
|
||||
|
||||
export const Portal: React.FC<PortalProps> = ({
|
||||
unstable_skipInitialRender,
|
||||
...props
|
||||
}) => {
|
||||
const [hydrated, setHydrated] = useState(false);
|
||||
useEffect(() => {
|
||||
if (unstable_skipInitialRender) {
|
||||
setHydrated(true);
|
||||
}
|
||||
}, [unstable_skipInitialRender]);
|
||||
if (unstable_skipInitialRender && !hydrated) {
|
||||
return null;
|
||||
}
|
||||
return <PortalImpl {...props} />;
|
||||
};
|
||||
|
||||
|
||||
export type PortalProps = PropsWithChildren<{
|
||||
type?: string;
|
||||
containerRef?: React.RefObject<Node>;
|
||||
unstable_skipInitialRender?: boolean;
|
||||
}>;
|
||||
|
||||
Portal.displayName = "Portal";
|
||||
export type PortalProps = PropsWithChildren<{
|
||||
type?: string;
|
||||
containerRef?: React.RefObject<Node>;
|
||||
unstable_skipInitialRender?: boolean;
|
||||
}>;
|
||||
|
||||
Portal.displayName = "Portal";
|
||||
|
||||
@@ -2,8 +2,8 @@ import {
|
||||
QueryClientProviderProps,
|
||||
QueryClientProviderWithDefault,
|
||||
} from "../../core/providers/query-client";
|
||||
import { RequiredParam } from "../../core/query-utils/required-param";
|
||||
import { ComponentWithChildren } from "../../core/types/component";
|
||||
import { RequiredParam } from "../../core/types/shared";
|
||||
import {
|
||||
ThirdwebAuthConfig,
|
||||
ThirdwebAuthConfigProvider,
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { Signer } from "ethers";
|
||||
import { createContext, useContext, useEffect, useMemo } from "react";
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
interface TWSDKContext {
|
||||
@@ -60,21 +60,36 @@ export const WrappedThirdwebSDKProvider: ComponentWithChildren<
|
||||
}) => {
|
||||
const { signer } = useThirdwebConnectedWalletContext();
|
||||
|
||||
const sdk = useMemo(() => {
|
||||
const [sdk, setSDK] = useState<ThirdwebSDK>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!desiredChainId || typeof window === "undefined") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const _sdk = new ThirdwebSDK(provider, sdkOptions, storageInterface);
|
||||
// if we already have a signer from the wallet context, use it immediately
|
||||
if (signer) {
|
||||
_sdk.updateSignerOrProvider(signer);
|
||||
}
|
||||
|
||||
(_sdk as any)._constructedAt = Date.now();
|
||||
(_sdk as any)._chainId = desiredChainId;
|
||||
return _sdk;
|
||||
setSDK(_sdk);
|
||||
|
||||
// explicitly *not* passing the signer, if we have it we use it if we don't we don't
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [provider, sdkOptions, storageInterface, desiredChainId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (signer && sdk && (sdk as any)._chainId === desiredChainId) {
|
||||
sdk.updateSignerOrProvider(signer);
|
||||
if (sdk && (sdk as any)._chainId === desiredChainId) {
|
||||
if (signer) {
|
||||
sdk.updateSignerOrProvider(signer);
|
||||
} else {
|
||||
sdk.updateSignerOrProvider(provider);
|
||||
}
|
||||
}
|
||||
}, [signer, sdk, desiredChainId]);
|
||||
}, [signer, sdk, desiredChainId, provider]);
|
||||
|
||||
const ctxValue = useMemo(
|
||||
() => ({
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user