Compare commits
59
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
@@ -1,5 +1,21 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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.13",
|
||||
"version": "2.0.19",
|
||||
"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"
|
||||
],
|
||||
|
||||
@@ -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,83 @@
|
||||
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 };
|
||||
}
|
||||
@@ -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,83 @@
|
||||
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);
|
||||
user = { 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,59 @@
|
||||
# thirdweb
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.7.24",
|
||||
"version": "0.8.5",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.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",
|
||||
"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",
|
||||
"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,5 +1,4 @@
|
||||
#!/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";
|
||||
@@ -43,10 +42,10 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
program
|
||||
.command("create")
|
||||
.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.`)
|
||||
@@ -71,11 +70,19 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
await twCreate(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 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")
|
||||
@@ -118,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)")
|
||||
@@ -131,22 +138,10 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
open(url.toString());
|
||||
});
|
||||
|
||||
program
|
||||
.command("detect")
|
||||
.description(
|
||||
"Compile contracts and detect implemented 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 detectExtensions(options);
|
||||
});
|
||||
|
||||
program
|
||||
.command("upload")
|
||||
.description("Upload any file or directory using decentralized storage")
|
||||
.argument("<upload-path>", "path to file or directory to 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);
|
||||
@@ -159,12 +154,15 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
|
||||
});
|
||||
|
||||
program
|
||||
.command("install-ci")
|
||||
.command("detect")
|
||||
.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.",
|
||||
"(deprecated) Compile contracts and detect implemented 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 installGithubAction(options);
|
||||
await detectExtensions(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())
|
||||
);
|
||||
}
|
||||
@@ -1,14 +1,46 @@
|
||||
import { logger } from "../core/helpers/logger";
|
||||
import { spinner } from "../core/helpers/logger";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
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(
|
||||
@@ -22,11 +54,7 @@ export async function upload(
|
||||
let uri = "";
|
||||
const fileType = fs.lstatSync(uploadPath);
|
||||
if (fileType.isDirectory()) {
|
||||
const subPaths = fs.readdirSync(uploadPath);
|
||||
const filePaths = subPaths
|
||||
.map((subPath) => path.join(uploadPath, subPath))
|
||||
.filter((subPath) => fs.lstatSync(subPath).isFile());
|
||||
const files = filePaths.map((filePath) => fs.readFileSync(filePath));
|
||||
const files = recurseFiles(uploadPath, uploadPath);
|
||||
|
||||
if (files.length === 0) {
|
||||
logger.error(
|
||||
@@ -44,11 +72,14 @@ export async function upload(
|
||||
spin.succeed("Successfully uploaded directory to IPFS");
|
||||
uri = uris[0].substring(0, uris[0].lastIndexOf("/"));
|
||||
} else if (fileType.isFile()) {
|
||||
const file = fs.readFileSync(uploadPath);
|
||||
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);
|
||||
uri = await storage.upload(file, { uploadWithoutDirectory: true });
|
||||
spin.succeed("Succesfully uploaded file to IPFS.");
|
||||
} else {
|
||||
logger.error(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,5 +1,62 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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"
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.2.1",
|
||||
"version": "3.3.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -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;
|
||||
@@ -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",
|
||||
|
||||
@@ -8,7 +8,6 @@ const CHAIN_ID_TO_GNOSIS_SERVER_URL = {
|
||||
[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",
|
||||
};
|
||||
|
||||
export interface GnosisConnectorArguments {
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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 { getErcs, DropContract, WalletAddress } from "../../types";
|
||||
import {
|
||||
@@ -7,7 +10,11 @@ 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 {
|
||||
ClaimCondition,
|
||||
ClaimConditionFetchOptions,
|
||||
ClaimConditionInput,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { BigNumberish } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -62,22 +69,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");
|
||||
},
|
||||
@@ -115,22 +130,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 +204,7 @@ export function useClaimIneligibilityReasons(
|
||||
),
|
||||
() => {
|
||||
if (erc1155) {
|
||||
invariant(
|
||||
requiredParamInvariant(
|
||||
tokenId,
|
||||
"tokenId is required for ERC1155 claim ineligibility reasons",
|
||||
);
|
||||
@@ -267,11 +290,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,7 +364,7 @@ export function useResetClaimConditions(
|
||||
|
||||
return useMutation(
|
||||
async () => {
|
||||
const cleanConditions = (conditions: ClaimCondition[]) => {
|
||||
const cleanConditions = async (conditions: ClaimCondition[]) => {
|
||||
return conditions.map((c) => ({
|
||||
...c,
|
||||
price: c.currencyMetadata.displayValue,
|
||||
@@ -348,25 +374,34 @@ export function useResetClaimConditions(
|
||||
};
|
||||
|
||||
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 {
|
||||
@@ -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);
|
||||
},
|
||||
@@ -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,4 +1,7 @@
|
||||
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 {
|
||||
@@ -17,7 +20,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 +48,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 +80,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 +107,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 +137,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 +172,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 +206,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 +242,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 +251,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 +328,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 +385,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 +496,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);
|
||||
@@ -512,9 +553,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 +567,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!);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -351,7 +353,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 +418,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,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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
|
||||
@@ -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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
|
||||
@@ -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 } = useNFTCollection("<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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
|
||||
@@ -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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
|
||||
@@ -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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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
|
||||
* }
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -9,10 +9,7 @@ import {
|
||||
defaultSupportedChains,
|
||||
} from "../constants/chain";
|
||||
import { ThirdwebAuthConfig } from "../contexts/thirdweb-auth";
|
||||
import {
|
||||
ThirdwebConfigProvider,
|
||||
defaultChainRpc,
|
||||
} from "../contexts/thirdweb-config";
|
||||
import { ThirdwebConfigProvider } from "../contexts/thirdweb-config";
|
||||
import { ThirdwebSDKProvider, ThirdwebSDKProviderProps } from "./base";
|
||||
import { QueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
@@ -20,6 +17,7 @@ import {
|
||||
getProviderForNetwork,
|
||||
SDKOptionsOutput,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { DEFAULT_RPC_URLS } from "@thirdweb-dev/sdk";
|
||||
import type { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import React, { useMemo } from "react";
|
||||
import {
|
||||
@@ -223,7 +221,7 @@ export const ThirdwebProvider = <
|
||||
TSupportedChain extends SupportedChain = SupportedChain,
|
||||
>({
|
||||
sdkOptions,
|
||||
chainRpc = defaultChainRpc,
|
||||
chainRpc = DEFAULT_RPC_URLS,
|
||||
supportedChains = defaultSupportedChains.map(
|
||||
(c) => c.id,
|
||||
) as TSupportedChain[],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { RequiredParam } from "../core/types/shared";
|
||||
import { RequiredParam } from "../core/query-utils/required-param";
|
||||
import type {
|
||||
AirdropInput,
|
||||
Amount,
|
||||
@@ -191,7 +191,7 @@ export type RevealLazyMintInput = {
|
||||
* @beta
|
||||
*/
|
||||
export type ClaimNFTParams = {
|
||||
to: WalletAddress;
|
||||
to?: WalletAddress;
|
||||
quantity: BigNumberish;
|
||||
options?: ClaimOptions;
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { RequiredParam } from "../../core/types/shared";
|
||||
import { RequiredParam } from "../../core/query-utils/required-param";
|
||||
import { SupportedChainId } from "../constants/chain";
|
||||
import { ContractAddress, WalletAddress } from "../types";
|
||||
import { QueryClient, QueryKey } from "@tanstack/react-query";
|
||||
import type {
|
||||
ClaimConditionFetchOptions,
|
||||
MarketplaceFilter,
|
||||
QueryAllParams,
|
||||
SUPPORTED_CHAIN_ID,
|
||||
@@ -267,22 +268,24 @@ export const cacheKeys = {
|
||||
getActive: (
|
||||
contractAddress: RequiredParam<ContractAddress>,
|
||||
tokenId?: BigNumberish,
|
||||
options?: ClaimConditionFetchOptions,
|
||||
) =>
|
||||
createContractCacheKey(
|
||||
contractAddress,
|
||||
tokenId
|
||||
? ["claimConditions", "getActive", { tokenId }]
|
||||
: ["claimConditions", "getActive"],
|
||||
? ["claimConditions", "getActive", { tokenId }, options]
|
||||
: ["claimConditions", "getActive", options],
|
||||
),
|
||||
getAll: (
|
||||
contractAddress: RequiredParam<ContractAddress>,
|
||||
tokenId?: BigNumberish,
|
||||
options?: ClaimConditionFetchOptions,
|
||||
) =>
|
||||
createContractCacheKey(
|
||||
contractAddress,
|
||||
tokenId
|
||||
? ["claimConditions", "getAll", { tokenId }]
|
||||
: ["claimConditions", "getAll"],
|
||||
? ["claimConditions", "getAll", { tokenId }, options]
|
||||
: ["claimConditions", "getAll", options],
|
||||
),
|
||||
getClaimIneligibilityReasons: (
|
||||
contractAddress: RequiredParam<ContractAddress>,
|
||||
|
||||
@@ -3,4 +3,4 @@ export * from "./evm";
|
||||
|
||||
// core exports
|
||||
export { shouldNeverPersistQuery } from "./core/query-utils/query-key";
|
||||
export type { RequiredParam } from "./core/types/shared";
|
||||
export type { RequiredParam } from "./core/query-utils/required-param";
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
createContext,
|
||||
useContext,
|
||||
useMemo,
|
||||
} from "react";
|
||||
|
||||
/**
|
||||
* The configuration to use the react SDK with an [auth](https://portal.thirdweb.com/auth) server.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export interface ThirdwebAuthConfig {
|
||||
/**
|
||||
* The backend URL of the authentication endoints. For example, if your endpoints are
|
||||
* at `/api/auth/login`, `/api/auth/logout`, etc. then this should be set to `/api/auth`.
|
||||
*/
|
||||
authUrl: string;
|
||||
|
||||
/**
|
||||
* The frontend domain used to generate the login payload.
|
||||
* This domain should match the domain used on your auth backend.
|
||||
*/
|
||||
domain: string;
|
||||
|
||||
/**
|
||||
* The URL to redirect to after a succesful login.
|
||||
*/
|
||||
loginRedirect?: string;
|
||||
}
|
||||
|
||||
const ThirdwebAuthConfigContext = createContext<ThirdwebAuthConfig | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
export const ThirdwebAuthConfigProvider: React.FC<
|
||||
PropsWithChildren<{ value?: ThirdwebAuthConfig }>
|
||||
> = ({ value, children }) => {
|
||||
// Remove trailing slash from URL if present
|
||||
const authConfig = useMemo(
|
||||
() =>
|
||||
value
|
||||
? {
|
||||
...value,
|
||||
authUrl: value.authUrl.replace(/\/$/, ""),
|
||||
}
|
||||
: undefined,
|
||||
[value],
|
||||
);
|
||||
return (
|
||||
<ThirdwebAuthConfigContext.Provider value={authConfig}>
|
||||
{children}
|
||||
</ThirdwebAuthConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export function useThirdwebAuthConfig() {
|
||||
return useContext(ThirdwebAuthConfigContext);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { LoginConfig, useLogin } from "./useLogin";
|
||||
import { useLogout } from "./useLogout";
|
||||
import { useUser } from "./useUser";
|
||||
|
||||
export * from "./useLogin";
|
||||
export * from "./useLogout";
|
||||
export * from "./useUser";
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
* @internal
|
||||
*/
|
||||
export function useAuth(loginConfig?: LoginConfig) {
|
||||
const user = useUser();
|
||||
const login = useLogin(loginConfig);
|
||||
const logout = useLogout();
|
||||
return { ...user, login, logout };
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { ensureTWPrefix } from "../../../core/query-utils/query-key";
|
||||
import { useThirdwebAuthConfig } from "../../contexts/thirdweb-auth";
|
||||
import { useSDK } from "../../providers/base";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { LoginOptions } from "@thirdweb-dev/sdk";
|
||||
import React from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export interface LoginConfig {
|
||||
/**
|
||||
* The URL to redirect to on login.
|
||||
*/
|
||||
redirectTo?: string;
|
||||
/**
|
||||
* Function to run on error.
|
||||
*/
|
||||
onError?: (error: string) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to securely login to a backend with the connected wallet. The backend
|
||||
* authentication URL must be configured on the ThirdwebProvider.
|
||||
*
|
||||
* @param config - Configuration for the login.
|
||||
* @returns - A function to invoke to login with the connected wallet.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export function useLogin(config?: LoginConfig) {
|
||||
const sdk = useSDK();
|
||||
const queryClient = useQueryClient();
|
||||
const authConfig = useThirdwebAuthConfig();
|
||||
|
||||
React.useEffect(() => {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const error = queryParams.get("error");
|
||||
|
||||
if (error && config?.onError) {
|
||||
// If there is an error, parse it and trigger the onError callback
|
||||
config.onError(decodeURI(error));
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
async function login(cfg?: LoginOptions) {
|
||||
invariant(
|
||||
authConfig,
|
||||
"Please specify an authConfig in the ThirdwebProvider",
|
||||
);
|
||||
const payload = await sdk?.auth.login(authConfig.domain, cfg);
|
||||
|
||||
const encodedPayload = encodeURIComponent(btoa(JSON.stringify(payload)));
|
||||
const encodedRedirectTo = encodeURIComponent(
|
||||
config?.redirectTo ||
|
||||
authConfig.loginRedirect ||
|
||||
window.location.toString(),
|
||||
);
|
||||
|
||||
queryClient.invalidateQueries(ensureTWPrefix(["user"]));
|
||||
|
||||
// Redirect to the login URL with the encoded payload
|
||||
window.location.href = `${authConfig.authUrl}/login?payload=${encodedPayload}&redirect=${encodedRedirectTo}`;
|
||||
}
|
||||
|
||||
return login;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { ensureTWPrefix } from "../../../core/query-utils/query-key";
|
||||
import { useThirdwebAuthConfig } from "../../contexts/thirdweb-auth";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
* Hook to logout the connected wallet from the backend.
|
||||
* The backend logout URL must be configured on the ThirdwebProvider.
|
||||
*
|
||||
* @returns - A function to invoke to logout.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export function useLogout() {
|
||||
const queryClient = useQueryClient();
|
||||
const authConfig = useThirdwebAuthConfig();
|
||||
|
||||
function logout() {
|
||||
invariant(
|
||||
authConfig,
|
||||
"Please specify an authConfig in the ThirdwebProvider",
|
||||
);
|
||||
queryClient.invalidateQueries(ensureTWPrefix(["user"]));
|
||||
window.location.href = `${authConfig.authUrl}/logout`;
|
||||
}
|
||||
|
||||
return logout;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { ensureTWPrefix } from "../../../core/query-utils/query-key";
|
||||
import { useThirdwebAuthConfig } from "../../contexts/thirdweb-auth";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export interface ThirdwebAuthUser {
|
||||
address: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get the currently logged in user.
|
||||
*
|
||||
* @returns - The currently logged in user or null if not logged in, as well as a loading state.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export function useUser() {
|
||||
const authConfig = useThirdwebAuthConfig();
|
||||
|
||||
const { data: user, isLoading } = useQuery(
|
||||
ensureTWPrefix(["user"]),
|
||||
async () => {
|
||||
invariant(
|
||||
authConfig,
|
||||
"Please specify an authConfig in the ThirdwebProvider",
|
||||
);
|
||||
const res = await fetch(`${authConfig.authUrl}/user`);
|
||||
return (await res.json()) as ThirdwebAuthUser;
|
||||
},
|
||||
{
|
||||
enabled: !!authConfig,
|
||||
},
|
||||
);
|
||||
|
||||
return { user, isLoading };
|
||||
}
|
||||
+9
-11
@@ -1,6 +1,6 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { MintNFTParams } from "../../../evm/types";
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/query-utils/required-param";
|
||||
import { MintNFTParams } from "../../../types";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
@@ -14,16 +14,11 @@ import invariant from "tiny-invariant";
|
||||
* import { useProgram, useMintNFT } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: mint, isLoading, error } = useMintNFT(program);
|
||||
*
|
||||
* function mintNFT() {
|
||||
* const metadata = { name: "First NFT", description: "This is a cool NFT!" };
|
||||
* mint({ to: "{{wallet_address}}", metadata });
|
||||
* }
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: mintNFT, isLoading, error } = useMintNFT(program);
|
||||
*
|
||||
* return (
|
||||
* <button onClick={() => mintNFT()}>
|
||||
* <button onClick={() => mintNFT({ metadata: { name: "First NFT" } })}>
|
||||
* Mint
|
||||
* </button>
|
||||
* )
|
||||
@@ -37,6 +32,9 @@ export function useMintNFT(program: RequiredParam<NFTCollection>) {
|
||||
return useMutation(
|
||||
async (data: MintNFTParams) => {
|
||||
invariant(program, "program is required");
|
||||
if (!data.to) {
|
||||
return await program.mint(data.metadata);
|
||||
}
|
||||
return await program.mintTo(data.to, data.metadata);
|
||||
},
|
||||
{
|
||||
@@ -0,0 +1,51 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../../core/query-utils/required-param";
|
||||
import { MintNFTSupplyParams } from "../../../types";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
/**
|
||||
* Mint additional supply for an NFT on your NFT program
|
||||
* @param program - The NFT program to mint additional NFTs to
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useMintNFTSupply } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: mintSupply, isLoading, error } = useMintNFTSupply(program);
|
||||
*
|
||||
* return (
|
||||
* <button onClick={() => mintSupply({ nftAddress: "111...", amount: 10 })}>
|
||||
* Mint Additional Supply
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useMintNFTSupply(program: RequiredParam<NFTCollection>) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (data: MintNFTSupplyParams) => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
if (!data.to) {
|
||||
return await program.mintAdditionalSupply(data.nftAddress, data.amount);
|
||||
}
|
||||
return await program.mintAdditionalSupplyTo(
|
||||
data.to,
|
||||
data.nftAddress,
|
||||
data.amount,
|
||||
);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
queryClient.invalidateQueries(createSOLProgramQueryKey(program)),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function claimConditionsQuery(program: RequiredParam<NFTDrop>) {
|
||||
return {
|
||||
queryKey: createSOLProgramQueryKey(program, ["claimConditions"] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
invariant(program, "program is required");
|
||||
|
||||
return await program.claimConditions.get();
|
||||
},
|
||||
enabled: !!program,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current claim conditions on an NFT Drop
|
||||
* @param program - The NFT Drop program to get the claim conditions for
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useClaimConditions } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: claimConditions, isLoading } = useClaimConditions(program);
|
||||
*
|
||||
* return (
|
||||
* <p>{claimConditions?.price.displayValue}</p>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useClaimConditions(program: RequiredParam<NFTDrop>) {
|
||||
return useQuery(claimConditionsQuery(program));
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../../core/query-utils/required-param";
|
||||
import { ClaimNFTParams } from "../../../types";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
@@ -13,11 +14,11 @@ import invariant from "tiny-invariant";
|
||||
* import { useProgram, useClaimNFT } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: claim, isLoading, error } = useClaimNFT(program);
|
||||
*
|
||||
* return (
|
||||
* <button onClick={() => claim(1)}>
|
||||
* <button onClick={() => claim({amount: 1})}>
|
||||
* Claim
|
||||
* </button>
|
||||
* )
|
||||
@@ -29,9 +30,12 @@ import invariant from "tiny-invariant";
|
||||
export function useClaimNFT(program: RequiredParam<NFTDrop>) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (quantity: number) => {
|
||||
async (data: ClaimNFTParams) => {
|
||||
invariant(program, "program is required");
|
||||
return await program.claim(quantity);
|
||||
if (!data.to) {
|
||||
return await program.claim(data.amount);
|
||||
}
|
||||
return await program.claimTo(data.to, data.amount);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function dropTotalClaimedSupplyQuery(program: RequiredParam<NFTDrop>) {
|
||||
return {
|
||||
@@ -11,7 +13,7 @@ export function dropTotalClaimedSupplyQuery(program: RequiredParam<NFTDrop>) {
|
||||
] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
|
||||
return await program.totalClaimedSupply();
|
||||
},
|
||||
@@ -28,7 +30,7 @@ export function dropTotalClaimedSupplyQuery(program: RequiredParam<NFTDrop>) {
|
||||
* import { useProgram, useDropTotalClaimedSupply } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: claimedSupply, isLoading } = useDropTotalClaimedSupply(program);
|
||||
*
|
||||
* return (
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { NFTMetadataInput, UploadProgressEvent } from "@thirdweb-dev/sdk";
|
||||
import type { NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
* Lazy mint NFTs on an NFT Drop program
|
||||
@@ -14,7 +16,7 @@ import invariant from "tiny-invariant";
|
||||
* import { useProgram, useLazyMintNFT } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: lazyMint, isLoading, error } = useLazyMintNFT(program);
|
||||
*
|
||||
* return (
|
||||
@@ -34,7 +36,7 @@ export function useLazyMint(
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (data: { metadatas: NFTMetadataInput[] }) => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
let options;
|
||||
if (onProgress) {
|
||||
options = {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTDrop, NFTDropConditionsInput } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
* Set Claim Conditions to an NFT Drop program
|
||||
* @param program - The NFT Drop program to set claim conditions for
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useSetClaimConditions } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: setClaimConditions, isLoading, error } = useSetClaimConditions(program);
|
||||
*
|
||||
* return (
|
||||
* <button onClick={() => setClaimConditions(metadata)}>
|
||||
* Set Claim Conditions
|
||||
* </button>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useSetClaimConditions(program: RequiredParam<NFTDrop>) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (metadata: NFTDropConditionsInput) => {
|
||||
invariant(program, "program is required");
|
||||
return await program.claimConditions.set(metadata);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
queryClient.invalidateQueries(createSOLProgramQueryKey(program)),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function dropUnclaimedSupplyQuery(program: RequiredParam<NFTDrop>) {
|
||||
return {
|
||||
@@ -11,7 +13,7 @@ export function dropUnclaimedSupplyQuery(program: RequiredParam<NFTDrop>) {
|
||||
] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
|
||||
return await program.totalUnclaimedSupply();
|
||||
},
|
||||
@@ -28,7 +30,7 @@ export function dropUnclaimedSupplyQuery(program: RequiredParam<NFTDrop>) {
|
||||
* import { useProgram, useDropTotalUnclaimedSupply } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: unclaimedSupply, isLoading } = useDropTotalUnclaimedSupply(program);
|
||||
*
|
||||
* return (
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
* Burn an NFT owned by the connected wallet
|
||||
@@ -13,7 +15,7 @@ import invariant from "tiny-invariant";
|
||||
* import { useProgram, useBurnNFT } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: burn, isLoading, error } = useBurnNFT(program);
|
||||
*
|
||||
* return (
|
||||
@@ -32,7 +34,7 @@ export function useBurnNFT(program: RequiredParam<NFTCollection | NFTDrop>) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (nftAddress: string) => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.burn(nftAddress);
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
export function nftCreatorsQuery(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
) {
|
||||
return {
|
||||
queryKey: createSOLProgramQueryKey(program, ["creators"] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.getCreators();
|
||||
},
|
||||
enabled: !!program,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the creators for an NFT program
|
||||
* @param program - The NFT program to get the creators for
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useCreators } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: creators, isLoading } = useCreators(program);
|
||||
*
|
||||
* return (
|
||||
* <pre>{JSON.stringify(creators)}</pre>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useCreators(program: RequiredParam<NFTCollection | NFTDrop>) {
|
||||
return useQuery(nftCreatorsQuery(program));
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function nftGetOneQuery(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
@@ -15,8 +17,8 @@ export function nftGetOneQuery(
|
||||
] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
invariant(program, "program is required");
|
||||
invariant(tokenAddress, "tokenAddress is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
requiredParamInvariant(tokenAddress, "tokenAddress is required");
|
||||
return await program.get(tokenAddress);
|
||||
},
|
||||
enabled: !!program && !!tokenAddress,
|
||||
@@ -33,7 +35,7 @@ export function nftGetOneQuery(
|
||||
* import { useProgram, useNFT } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: metadata, isLoading } = useNFT(program, mintAddress);
|
||||
*
|
||||
* return (
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function nftGetAllQuery(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
@@ -11,7 +13,7 @@ export function nftGetAllQuery(
|
||||
queryKey: createSOLProgramQueryKey(program, ["getAll"] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.getAll();
|
||||
},
|
||||
enabled: !!program,
|
||||
@@ -27,7 +29,7 @@ export function nftGetAllQuery(
|
||||
* import { useProgram, useNFTs } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: metadata, isLoading } = useNFTs(program);
|
||||
*
|
||||
* return (
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
|
||||
export function nftRoyaltyQuery(
|
||||
program: RequiredParam<NFTCollection | NFTDrop>,
|
||||
) {
|
||||
return {
|
||||
queryKey: createSOLProgramQueryKey(program, ["royalty"] as const),
|
||||
|
||||
queryFn: async () => {
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.getRoyalty();
|
||||
},
|
||||
enabled: !!program,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the royalty for an NFT program
|
||||
* @param program - The NFT program to get the royalty for
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { useProgram, useRoyalty } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: royalty, isLoading } = useRoyalty(program);
|
||||
*
|
||||
* return (
|
||||
* <p>{royalty}</p>
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useRoyalty(program: RequiredParam<NFTCollection | NFTDrop>) {
|
||||
return useQuery(nftRoyaltyQuery(program));
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { NFTCollection, NFTDrop } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export type TransferNFTMutationParams = {
|
||||
receiverAddress: string;
|
||||
@@ -18,7 +20,7 @@ export type TransferNFTMutationParams = {
|
||||
* import { useProgram, useTransferNFT } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: transfer, isLoading, error } = useTransferNFT(program);
|
||||
*
|
||||
* return (
|
||||
@@ -42,7 +44,7 @@ export function useTransferNFT(
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async ({ tokenAddress, receiverAddress }: TransferNFTMutationParams) => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.transfer(receiverAddress, tokenAddress);
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,17 +2,24 @@ import {
|
||||
createSOLQueryKeyWithNetwork,
|
||||
neverPersist,
|
||||
} from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDK } from "../../providers/base";
|
||||
import { programAccountTypeQuery } from "./useProgramAccountType";
|
||||
import { QueryClient, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import {
|
||||
QueryClient,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
UseQueryResult,
|
||||
} from "@tanstack/react-query";
|
||||
import type {
|
||||
NFTCollection,
|
||||
NFTDrop,
|
||||
ThirdwebSDK,
|
||||
Token,
|
||||
} from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
type ProgramMap = Readonly<{
|
||||
"nft-collection": NFTCollection;
|
||||
@@ -37,8 +44,8 @@ export function programQuery<TProgramType extends ProgramType>(
|
||||
),
|
||||
),
|
||||
queryFn: (async () => {
|
||||
invariant(sdk, "sdk is required");
|
||||
invariant(address, "Address is required");
|
||||
requiredParamInvariant(sdk, "sdk is required");
|
||||
requiredParamInvariant(address, "Address is required");
|
||||
// if the type is not passed in explicitly then we'll try to resolve it
|
||||
if (!type) {
|
||||
// why do we call `fetchQuery` here instead of calling the sdk directly?
|
||||
@@ -77,7 +84,7 @@ export function programQuery<TProgramType extends ProgramType>(
|
||||
* import { useProgram } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}").program;
|
||||
*
|
||||
* // Now you can use the program in the rest of the component
|
||||
*
|
||||
@@ -98,7 +105,22 @@ export function useProgram<TProgramType extends ProgramType>(
|
||||
) {
|
||||
const queryClient = useQueryClient();
|
||||
const sdk = useSDK();
|
||||
return useQuery(programQuery(queryClient, sdk, address, type));
|
||||
const queryResult = useQuery(programQuery(queryClient, sdk, address, type));
|
||||
|
||||
return { ...queryResult, program: queryResult.data } as UseQueryResult<
|
||||
Readonly<{
|
||||
"nft-collection": NFTCollection;
|
||||
"nft-drop": NFTDrop;
|
||||
token: Token;
|
||||
}>[TProgramType],
|
||||
unknown
|
||||
> & {
|
||||
program: Readonly<{
|
||||
"nft-collection": NFTCollection;
|
||||
"nft-drop": NFTDrop;
|
||||
token: Token;
|
||||
}>[TProgramType];
|
||||
};
|
||||
}
|
||||
|
||||
export type UseProgramResult = ReturnType<typeof useProgram>;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { createSOLQueryKeyWithNetwork } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDK } from "../../providers/base";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function programAccountTypeQuery(
|
||||
sdk: RequiredParam<ThirdwebSDK>,
|
||||
@@ -16,10 +18,10 @@ export function programAccountTypeQuery(
|
||||
network || null,
|
||||
),
|
||||
queryFn: async () => {
|
||||
invariant(sdk, "sdk is required");
|
||||
invariant(address, "Address is required");
|
||||
requiredParamInvariant(sdk, "sdk is required");
|
||||
requiredParamInvariant(address, "Address is required");
|
||||
|
||||
return await sdk.registry.getAccountType(address);
|
||||
return await sdk.registry.getProgramType(address);
|
||||
},
|
||||
enabled: !!sdk && !!network && !!address,
|
||||
// this cannot change as it is unique by address & network
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { UseProgramResult } from "./useProgram";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import type { TokenParams } from "../../../evm";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { Token } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
/**
|
||||
* Mint tokens on your token program
|
||||
@@ -14,7 +16,7 @@ import invariant from "tiny-invariant";
|
||||
* import { useProgram, useMintToken } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: mint, isLoading, error } = useMintToken(program);
|
||||
*
|
||||
* return (
|
||||
@@ -31,7 +33,7 @@ export function useMintToken(program: RequiredParam<Token>) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async (params: TokenParams) => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
// TODO switch this to use mintTo once that is exposed
|
||||
return await program.mint(params.amount);
|
||||
},
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Token } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function tokenBalanceQuery(
|
||||
program: RequiredParam<Token>,
|
||||
@@ -14,9 +16,8 @@ export function tokenBalanceQuery(
|
||||
{ walletAddress },
|
||||
] as const),
|
||||
queryFn: async () => {
|
||||
invariant(program, "program is required");
|
||||
|
||||
invariant(walletAddress, "Wallet address is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
requiredParamInvariant(walletAddress, "Wallet address is required");
|
||||
return await program.balanceOf(walletAddress);
|
||||
},
|
||||
enabled: !!program && !!walletAddress,
|
||||
@@ -33,7 +34,7 @@ export function tokenBalanceQuery(
|
||||
* import { useProgram, useTokenBalance } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: balance, isLoading } = useTokenBalance(program, "{{wallet_address}}");
|
||||
*
|
||||
* return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Token } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
@@ -25,7 +25,7 @@ export function tokenSupplyQuery(program: RequiredParam<Token>) {
|
||||
* import { useProgram, useMintToken } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { data: totalSupply, isLoading } = useTokenSupply(program);
|
||||
*
|
||||
* return (
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { createSOLProgramQueryKey } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/types/shared";
|
||||
import {
|
||||
requiredParamInvariant,
|
||||
RequiredParam,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import type { Amount, Token } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export type TransferTokenMutationParams = {
|
||||
amount: Amount;
|
||||
@@ -18,7 +20,7 @@ export type TransferTokenMutationParams = {
|
||||
* import { useProgram, useTransferToken } from "@thirdweb-dev/react/solana";
|
||||
*
|
||||
* export default function Component() {
|
||||
* const program = useProgram("{{program_address}}");
|
||||
* const { program } = useProgram("{{program_address}}");
|
||||
* const { mutateAsync: transfer, isLoading, error } = useTransferToken(program);
|
||||
*
|
||||
* return (
|
||||
@@ -35,7 +37,7 @@ export function useTransferToken(program: RequiredParam<Token>) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
async ({ amount, receiverAddress }: TransferTokenMutationParams) => {
|
||||
invariant(program, "program is required");
|
||||
requiredParamInvariant(program, "program is required");
|
||||
return await program.transfer(receiverAddress, amount);
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { createSOLQueryKeyWithNetwork } from "../../../core/query-utils/query-key";
|
||||
import { RequiredParam } from "../../../core/query-utils/required-param";
|
||||
import { useSDK } from "../../providers/base";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
export function balanceQuery(sdk: RequiredParam<ThirdwebSDK>) {
|
||||
const address = sdk?.wallet?.getAddress();
|
||||
const network = sdk?.network;
|
||||
|
||||
return {
|
||||
queryKey: createSOLQueryKeyWithNetwork(
|
||||
["wallet-balance", { address }] as const,
|
||||
network,
|
||||
),
|
||||
|
||||
queryFn: () => {
|
||||
invariant(sdk, "sdk is required");
|
||||
|
||||
return sdk.wallet.getBalance();
|
||||
},
|
||||
enabled: !!sdk && !!address && !!network,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently connected wallet balance
|
||||
*
|
||||
* @returns the balace of the connected wallet
|
||||
*/
|
||||
export function useBalance() {
|
||||
const sdk = useSDK();
|
||||
return useQuery(balanceQuery(sdk));
|
||||
}
|
||||
@@ -12,19 +12,29 @@ export * from "./hooks/program/useProgramAccountType";
|
||||
|
||||
// NFT Shared
|
||||
export * from "./hooks/nft/useNFTs";
|
||||
export * from "./hooks/nft/useMintNFT";
|
||||
export * from "./hooks/nft/useNFT";
|
||||
export * from "./hooks/nft/useTransferNFT";
|
||||
export * from "./hooks/nft/useBurnNFT";
|
||||
|
||||
// NFT Collection
|
||||
export * from "./hooks/nft/collection/useMintNFT";
|
||||
export * from "./hooks/nft/collection/useMintNFTSupply";
|
||||
|
||||
// NFT Drop
|
||||
export * from "./hooks/nft/drop/useLazyMint";
|
||||
export * from "./hooks/nft/drop/useClaimedSupply";
|
||||
export * from "./hooks/nft/drop/useUnclaimedSupply";
|
||||
export * from "./hooks/nft/drop/useClaimNFT";
|
||||
export * from "./hooks/nft/drop/useClaimConditions";
|
||||
export * from "./hooks/nft/drop/useSetClaimConditions";
|
||||
|
||||
// Token
|
||||
export * from "./hooks/token/useTokenSupply";
|
||||
export * from "./hooks/token/useTokenBalance";
|
||||
export * from "./hooks/token/useMintToken";
|
||||
export * from "./hooks/token/useTransferToken";
|
||||
|
||||
// Auth
|
||||
export * from "./hooks/auth";
|
||||
|
||||
//Wallet
|
||||
export * from "./hooks/wallet/useBalance";
|
||||
|
||||
@@ -2,16 +2,21 @@ 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,
|
||||
} from "../contexts/thirdweb-auth";
|
||||
import type { WalletContextState } from "@solana/wallet-adapter-react";
|
||||
import { Network, ThirdwebSDK } from "@thirdweb-dev/sdk/solana";
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
interface ThirdwebSDKProviderProps extends QueryClientProviderProps {
|
||||
network: RequiredParam<Network>;
|
||||
wallet?: WalletContextState;
|
||||
authConfig?: ThirdwebAuthConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,36 +38,53 @@ interface ThirdwebSDKProviderProps extends QueryClientProviderProps {
|
||||
*/
|
||||
export const ThirdwebSDKProvider: ComponentWithChildren<
|
||||
ThirdwebSDKProviderProps
|
||||
> = ({ children, network, queryClient, wallet }) => {
|
||||
> = ({ children, network, queryClient, wallet, authConfig }) => {
|
||||
const [sdk, setSDK] = useState<ThirdwebSDK | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (network) {
|
||||
const _sdk = ThirdwebSDK.fromNetwork(network);
|
||||
if (wallet && wallet.publicKey) {
|
||||
_sdk.wallet.connect(wallet);
|
||||
}
|
||||
(_sdk as any)._network = network;
|
||||
setSDK(_sdk);
|
||||
} else {
|
||||
setSDK(null);
|
||||
}
|
||||
// disabled wallet on purpose because we handle that below
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [network]);
|
||||
|
||||
useEffect(() => {
|
||||
if (sdk) {
|
||||
if (wallet?.publicKey) {
|
||||
sdk.wallet.connect(wallet);
|
||||
} else {
|
||||
sdk.wallet.disconnect();
|
||||
}
|
||||
if (
|
||||
wallet &&
|
||||
wallet.publicKey &&
|
||||
sdk &&
|
||||
(sdk as any)._network === network
|
||||
) {
|
||||
sdk.wallet.connect(wallet);
|
||||
return;
|
||||
}
|
||||
}, [sdk, wallet]);
|
||||
}, [network, sdk, wallet]);
|
||||
|
||||
const ctxValue = useMemo(
|
||||
() =>
|
||||
({
|
||||
sdk,
|
||||
desiredNetwork: network || "unknown",
|
||||
_inProvider: true,
|
||||
} as const),
|
||||
[sdk, network],
|
||||
);
|
||||
|
||||
return (
|
||||
<QueryClientProviderWithDefault queryClient={queryClient}>
|
||||
<ThirdwebSDKContext.Provider
|
||||
value={{ sdk, desiredNetwork: network || "unknown", _inProvider: true }}
|
||||
>
|
||||
{children}
|
||||
</ThirdwebSDKContext.Provider>
|
||||
<ThirdwebAuthConfigProvider value={authConfig}>
|
||||
<ThirdwebSDKContext.Provider value={ctxValue}>
|
||||
{children}
|
||||
</ThirdwebSDKContext.Provider>
|
||||
</ThirdwebAuthConfigProvider>
|
||||
</QueryClientProviderWithDefault>
|
||||
);
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user