Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
9124b3180c Version Packages (#308)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-18 15:02:14 -07:00
Adam MajmudarandGitHub f41a443c57 Add update creators and update royalty (#320) 2022-10-18 21:50:47 +00:00
Joaquim VergesandGitHub b7cd744cd0 [SDK] Sign multiple solana claims at once (#322) 2022-10-18 21:35:03 +00:00
bb1b4e6ac3 [React] Add useMinimumNextBid hook (#318)
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-18 11:57:26 -07:00
2f8ec89c4c [SDK, React] Expose total claimed/unclaimed supply for erc721 contracts (#319)
Co-authored-by: Nacho Iacovino <[email protected]>
2022-10-17 18:17:54 -07:00
Joaquim VergesandGitHub 6ae52664e5 [React] Fix passing wrong RPC url to UserWallet when using an external signer (#316) 2022-10-17 22:26:15 +00:00
Adam MajmudarandGitHub 4f867e15ed Fix supply in nft.get() (#317) 2022-10-17 19:17:43 +00:00
cc8f983615 fix typo (#314)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-16 22:27:40 +00:00
Joaquim VergesandGitHub f45ef62128 [SDK] return fallback metadata when tokenId is not found on nft contract (#313) 2022-10-16 15:15:26 -07:00
9727502eb3 Add useMintNFTSupply hook (#310)
Co-authored-by: Jonas Daniels <[email protected]>
2022-10-14 13:32:43 -07:00
Adam MajmudarandGitHub b6eec6146c [Solana] Add quantity to mint additional supply (#312) 2022-10-14 19:23:07 +00:00
88daa3c5cb [SDK] Check for undefined ABI (#309)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-14 03:37:40 +00:00
Adam MajmudarandGitHub 53c6507669 Add deploy event listener (#306) 2022-10-13 21:28:24 +00:00
YashandGitHub 782b58d3f6 [SDK] Fix multicall txns in a gasless setup (#307) 2022-10-13 11:57:02 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
e17e41f4d1 Version Packages (#304)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 15:22:33 -07:00
Joaquim VergesandGitHub 712b11a43e [SDK] Fix not getting real owner of Solana NFTs (#303) 2022-10-12 22:12:53 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
306debb029 Version Packages (#302)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 10:57:32 -07:00
Joaquim VergesandGitHub 86f0ceff46 [React] Remove useAddress hook for now (#301) 2022-10-12 17:54:14 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
7f0d1ec947 Version Packages (#300)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 10:23:15 -07:00
Joaquim VergesandGitHub b1218cbd7d [React] Recreate SDK on wallet changes to propagate to useAddress and other hooks (#299) 2022-10-12 10:12:21 -07:00
Joaquim Verges 97ef35ce0f [SDK] docstrings for claim conditions 2022-10-12 08:48:58 -07:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
1f46f270cd Version Packages (#267)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-12 00:36:55 -07:00
Jonas DanielsandGitHub e8c25ed0f2 [React] - fix getBalance (#298) 2022-10-12 05:28:15 +00:00
Jonas DanielsandGitHub 78d6d3c159 [SDK] - fix solana getAddress (#297) 2022-10-12 04:49:39 +00:00
Joaquim Verges c72d92956a update generate snippets scripts 2022-10-11 21:28:39 -07:00
Jonas DanielsandGitHub cf88795376 [SDK, React] - useBalance hook (#296) 2022-10-12 04:15:06 +00:00
Jonas DanielsandGitHub 47287fb189 [Various] - fix lint (#295) 2022-10-11 21:02:38 -07:00
Joaquim Verges 3763326c25 [React] Fix docstrings 2022-10-11 20:32:10 -07:00
Joaquim Verges 894eb27dfa [SDK] Fix docstrings 2022-10-11 20:21:11 -07:00
Joaquim VergesandGitHub 8b3b97c8f7 [SDK] Fix getAllClaimed for Solana NFT drops (#293) 2022-10-12 00:31:19 +00:00
Jonas DanielsandGitHub 89d87a5eb3 [SDK] - fix batch upload for lazy mint (#294) 2022-10-12 00:18:15 +00:00
Jonas DanielsandGitHub 492a818079 [SOL] - expose useAddress() (#292) 2022-10-11 21:10:43 +00:00
Jonas DanielsandGitHub 86f608390a [React] - expose .program on useProgram() (#291) 2022-10-11 21:00:56 +00:00
Jonas DanielsandGitHub 8096c78b46 [SDK, React] - claim changes (#290) 2022-10-11 20:48:51 +00:00
Nacho IacovinoandGitHub 521a49c6ec [Solana] Standarize use claim (#289) 2022-10-11 10:50:35 -07:00
Joaquim VergesandGitHub 25a4773bba [SDK] Add pagination to getAll() for solana nft collections (#286) 2022-10-11 05:16:20 +00:00
18381fac73 Fix errorneous ERC20 Allowance check (#280)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-10 21:56:16 +00:00
Joaquim VergesandGitHub 89b5c6fe07 [SDK] Make solana drop creation and register transactions sequential (#285) 2022-10-10 14:43:09 -07:00
Joaquim VergesandGitHub 4dccccc6fa [SDK] Solana docs update (#284) 2022-10-10 12:44:55 -07:00
Jarrod WattsandGitHub de15e4212f [REACT] - Update Broken Links to Portal in README (#281) 2022-10-10 00:54:37 +00:00
Jarrod WattsandGitHub 1f41adbd2c [REACT] - Fix useActiveClaimCondition invariant failing for token ID 0 (#282) 2022-10-10 11:13:00 +11:00
Furqan RydhanandGitHub 1918495f2e Command Updates (detect -> build) + helper text (#279) 2022-10-09 16:21:07 -07:00
24514a6e9f Fr/command updates (#276)
Co-authored-by: Joaquim Verges <[email protected]>
2022-10-08 03:29:20 +00:00
Adam MajmudarandGitHub ad06c5b289 Add auth support to react and auth packages (#274) 2022-10-07 21:20:36 +00:00
Adam MajmudarandGitHub 9de3f8c8e9 Add support for recursive folder upload (#277) 2022-10-07 19:09:40 +00:00
Joaquim VergesandGitHub b1fa17158d [SDK] Expose maxClaimable and disallow claiming drops by default (#273) 2022-10-07 02:16:52 +00:00
Nacho IacovinoandGitHub 2bdf1984cb [Solana] Add claim conditions hooks (#272) 2022-10-07 02:36:20 +02:00
Adam MajmudarandGitHub df048af605 Add support for auth to Solana (#271) 2022-10-06 17:05:18 -07:00
Joaquim VergesandGitHub 41507d32a7 [SDK] Solana Program Registry Integration (#270) 2022-10-06 16:21:17 -07:00
Jonas DanielsandGitHub b2cadf164c [SDK, React] - remove deprecated chains (#269) 2022-10-06 00:20:53 -07:00
Adam MajmudarandGitHub 57b9c671aa [CLI] Update storage upload CLI (#268) 2022-10-05 15:29:16 -07:00
Adam MajmudarandGitHub 3b7d10ed93 [React] Add hook to create a storage upload component (#266) 2022-10-05 15:08:43 -07:00
Jonas DanielsandGitHub d6bb61b7fa [SDK] - align getProgram & make chainId getter available on EVM contracts (#263) 2022-10-05 20:06:34 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
8e591383fc Version Packages (#265)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2022-10-05 12:53:02 -07:00
Adam MajmudarandGitHub 6f95a520e6 [CLI] Add npx thirdweb upload command (#259) 2022-10-05 18:42:03 +00:00
Nacho IacovinoandGitHub f669d3ef4a [React] Use same data structure for useLazyMint (#264) 2022-10-05 11:25:42 -07:00
187 changed files with 4723 additions and 1301 deletions
+16
View File
@@ -1,5 +1,21 @@
# @thirdweb-dev/auth
## 2.0.18
## 2.0.17
## 2.0.16
## 2.0.15
## 2.0.14
### Patch Changes
- [#274](https://github.com/thirdweb-dev/js/pull/274) [`ad06c5b`](https://github.com/thirdweb-dev/js/commit/ad06c5b28902422f9b416d65255c64c937a5e046) Thanks [@adam-maj](https://github.com/adam-maj)! - Add solana auth support and plugins for react and auth package
## 2.0.13
## 2.0.12
## 2.0.11
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-next-evm.cjs.js",
"module": "dist/thirdweb-dev-auth-next-evm.esm.js"
}
+4
View File
@@ -0,0 +1,4 @@
{
"main": "dist/thirdweb-dev-auth-next-solana.cjs.js",
"module": "dist/thirdweb-dev-auth-next-solana.esm.js"
}
+11 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "2.0.12",
"version": "2.0.18",
"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"
],
+68
View File
@@ -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
View File
@@ -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";
+145
View File
@@ -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
View File
@@ -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";
+83
View File
@@ -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
View File
@@ -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";
+83
View File
@@ -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;
};
+54
View File
@@ -1,5 +1,59 @@
# thirdweb
## 0.8.4
### Patch Changes
- Updated dependencies [[`4f867e1`](https://github.com/thirdweb-dev/js/commit/4f867e15edbe5757afbf54cab207458711a8986c), [`b6eec61`](https://github.com/thirdweb-dev/js/commit/b6eec6146c23b66a3681bbb48b173b9019254214), [`9727502`](https://github.com/thirdweb-dev/js/commit/9727502eb30ac139382b7c7c8e8fc0967cbfbcf1), [`782b58d`](https://github.com/thirdweb-dev/js/commit/782b58d3f6a3b49ff9b3d837551db5161f96171a), [`f41a443`](https://github.com/thirdweb-dev/js/commit/f41a443c5785240197607b5bf93ff18a1c8e8307), [`f45ef62`](https://github.com/thirdweb-dev/js/commit/f45ef62128cfefe06275a38440c90d45e9f3cef7), [`2f8ec89`](https://github.com/thirdweb-dev/js/commit/2f8ec89c4c83ae577092c840c198e6fcfb114e69), [`b7cd744`](https://github.com/thirdweb-dev/js/commit/b7cd744cd0482a6c3fe5ae0c858f20cd380d4114), [`53c6507`](https://github.com/thirdweb-dev/js/commit/53c65076696a43bd9a7e58a7493d4e65c172900f)]:
- @thirdweb-dev/sdk@3.2.6
## 0.8.3
### Patch Changes
- Updated dependencies [[`712b11a`](https://github.com/thirdweb-dev/js/commit/712b11a43e389ae10b7f09648779a69c05b2c913)]:
- @thirdweb-dev/sdk@3.2.5
## 0.8.2
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@3.2.4
## 0.8.1
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/sdk@3.2.3
## 0.8.0
### Minor Changes
- [#276](https://github.com/thirdweb-dev/js/pull/276) [`24514a6`](https://github.com/thirdweb-dev/js/commit/24514a6e9fc1316622c1494e5be225e53f88d26e) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - build is now default compile command, reorganized the help list
- [#279](https://github.com/thirdweb-dev/js/pull/279) [`1918495`](https://github.com/thirdweb-dev/js/commit/1918495f2ee7f50d94083d9577dab0bf1c828c94) Thanks [@furqanrydhan](https://github.com/furqanrydhan)! - Updating command list order, helper text and changed detect -> build
### Patch Changes
- [#277](https://github.com/thirdweb-dev/js/pull/277) [`9de3f8c`](https://github.com/thirdweb-dev/js/commit/9de3f8c8e954733bfc3eb6e8e0e786ff5b296f0c) Thanks [@adam-maj](https://github.com/adam-maj)! - Add recursive folder upload support
- [#268](https://github.com/thirdweb-dev/js/pull/268) [`57b9c67`](https://github.com/thirdweb-dev/js/commit/57b9c671aaf7fce0076dd332c171ba6944752e7a) Thanks [@adam-maj](https://github.com/adam-maj)! - Update storage upload cLI
- Updated dependencies [[`521a49c`](https://github.com/thirdweb-dev/js/commit/521a49c6ec6a73068adcfbc1d94d2f3f17afae86), [`8b3b97c`](https://github.com/thirdweb-dev/js/commit/8b3b97c8f7b3710ce62ed86cfeb4f6de3c23d559), [`25a4773`](https://github.com/thirdweb-dev/js/commit/25a4773bba406f4e270ac86e68c5266d24c08330), [`b2cadf1`](https://github.com/thirdweb-dev/js/commit/b2cadf164cfe9fb27081df7530356baf70ec2b3a), [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c), [`78d6d3c`](https://github.com/thirdweb-dev/js/commit/78d6d3c15968a449924b3f24354e151a90e4b20d), [`89d87a5`](https://github.com/thirdweb-dev/js/commit/89d87a5eb31e78e68e097718e9ae6e4c4d101868), [`41507d3`](https://github.com/thirdweb-dev/js/commit/41507d32a78c556f50c88de81c4d6109b1783c78), [`8096c78`](https://github.com/thirdweb-dev/js/commit/8096c78b466a7f5f9c2aac6e2c56a06f9d3a25d6), [`b1fa171`](https://github.com/thirdweb-dev/js/commit/b1fa17158dcac00265bc8f4d64f1f9708482cdb5), [`18381fa`](https://github.com/thirdweb-dev/js/commit/18381fac73741826934212ac6441842ed42a64a0), [`df048af`](https://github.com/thirdweb-dev/js/commit/df048af605f449279b01d7352b604e7856ac223c), [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c), [`cf88795`](https://github.com/thirdweb-dev/js/commit/cf88795376d3110f9d3aa839928d22276904b15a), [`89b5c6f`](https://github.com/thirdweb-dev/js/commit/89b5c6fe079e4df2cf12cb557aa9784f88c5d159)]:
- @thirdweb-dev/sdk@3.2.2
## 0.7.24
### Patch Changes
- [#259](https://github.com/thirdweb-dev/js/pull/259) [`6f95a52`](https://github.com/thirdweb-dev/js/commit/6f95a520e6cc69e08f734705e8adc71431b091cc) Thanks [@adam-maj](https://github.com/adam-maj)! - Add npx thirdweb upload command
- Updated dependencies []:
- @thirdweb-dev/sdk@3.2.1
## 0.7.23
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.7.23",
"version": "0.8.4",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+33 -18
View File
@@ -1,11 +1,12 @@
#!/usr/bin/env node
import { installGithubAction } from "../common/ci-installer";
import { detectExtensions } from "../common/feature-detector";
import { processProject } from "../common/processor";
import { cliVersion, pkg } from "../constants/urls";
import { info, logger } from "../core/helpers/logger";
import { twCreate } from "../create/command";
import generateDashboardUrl from "../helpers/generate-dashboard-url";
import { upload } from "../storage/command";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import { Command } from "commander";
import open from "open";
@@ -41,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.`)
@@ -69,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")
@@ -116,7 +125,7 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
program
.command("release")
.description(
"Release contracts, making them available for others to deploy and unlocking SDKs, Dashboards and Analytics.",
"Release your protocol so other devs can deploy them and unlock SDKs, Dashboards and Analytics",
)
.option("-p, --path <project-path>", "path to project", ".")
.option("--dry-run", "dry run (skip actually publishing)")
@@ -129,10 +138,25 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
open(url.toString());
});
program
.command("upload")
.description("Upload any file or directory to decentralized storage (IPFS)")
.argument("[upload]", "path to file or directory to upload")
.action(async (path) => {
const storage = new ThirdwebStorage();
const uri = await upload(storage, path);
info(`Files stored at the following IPFS URI:`);
logger.info(chalk.blueBright(uri.toString()));
const url = storage.resolveScheme(uri);
info(`Open this link to view your upload:`);
logger.info(chalk.blueBright(url.toString()));
});
program
.command("detect")
.description(
"Compile contracts and detect implemented thirdweb contract extensions",
"(deprecated) Compile contracts and detect implemented thirdweb contract extensions",
)
.option("-p, --path <project-path>", "path to project", ".")
.option("-d, --debug", "show debug logs")
@@ -141,15 +165,6 @@ $$$$$$\\ $$$$$$$\\ $$\\ $$$$$$\\ $$$$$$$ |$$\\ $$\\ $$\\ $$$$$$\\ $$$$
await detectExtensions(options);
});
program
.command("install-ci")
.description(
"(alpha) Set up continuous integration for your contracts. This adds a github action to deploy the project on pull requests and pushes to branches. Publishes on push the the main branch.",
)
.action(async (options) => {
await installGithubAction(options);
});
await program.parseAsync();
};
+94
View File
@@ -0,0 +1,94 @@
import { logger } from "../core/helpers/logger";
import { spinner } from "../core/helpers/logger";
import { BufferOrStringWithName, ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import fs from "fs";
import path from "path";
function recurseFiles(
uploadPath: string,
basePath: string,
): BufferOrStringWithName[] {
const files = [];
const fileType = fs.lstatSync(uploadPath);
if (fileType.isFile()) {
const fileName = path.relative(basePath, uploadPath);
const fileData = fs.readFileSync(uploadPath);
files.push({ name: fileName, data: fileData });
} else if (fileType.isDirectory()) {
const paths = fs.readdirSync(uploadPath);
paths.forEach((subPath) => {
const fullSubPath = path.join(uploadPath, subPath);
const subFiles = recurseFiles(fullSubPath, basePath);
subFiles.forEach((file) => files.push(file));
});
}
return files;
}
export async function upload(
storage: ThirdwebStorage,
uploadPath: string,
): Promise<string> {
if (!uploadPath) {
logger.error(
`Please pass a path to a file or directory to upload with the following format:\n ${chalk.blueBright(
`npx thirdweb@latest upload path/to/file.jpg`,
)}\n`,
);
process.exit(1);
}
const pathExists = fs.existsSync(uploadPath);
if (!pathExists) {
logger.error(
`Invalid path ${chalk.blueBright(
uploadPath,
)} provided. Please provide a valid path to a file or directory to upload."`,
);
process.exit(1);
}
let uri = "";
const fileType = fs.lstatSync(uploadPath);
if (fileType.isDirectory()) {
const files = recurseFiles(uploadPath, uploadPath);
if (files.length === 0) {
logger.error(
`No files detected in specified directory ${chalk.blueBright(
uploadPath,
)} to upload.`,
);
process.exit(1);
}
const spin = spinner(
"Uploading directory to IPFS. This may take a while depending on file sizes.",
);
const uris = await storage.uploadBatch(files);
spin.succeed("Successfully uploaded directory to IPFS");
uri = uris[0].substring(0, uris[0].lastIndexOf("/"));
} else if (fileType.isFile()) {
const fileName = path.parse(uploadPath).base;
const fileData = fs.readFileSync(uploadPath);
const file = { name: fileName, data: fileData };
const spin = spinner(
"Uploading file to IPFS. This may take a while depending on file sizes.",
);
uri = await storage.upload(file, { uploadWithoutDirectory: true });
spin.succeed("Succesfully uploaded file to IPFS.");
} else {
logger.error(
`Path ${chalk.blueBright(
uploadPath,
)} does not point to a valid file or directory. Please provide a valid path to a file or directory to upload`,
);
process.exit(1);
}
return uri;
}
+2 -1
View File
@@ -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",
+61
View File
@@ -1,5 +1,66 @@
# @thirdweb-dev/react
## 3.2.6
### Patch Changes
- [#316](https://github.com/thirdweb-dev/js/pull/316) [`6ae5266`](https://github.com/thirdweb-dev/js/commit/6ae52664e5865564cd4fc2e00e3120675b9e0ca3) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix SIWE when using an external signer
- [#310](https://github.com/thirdweb-dev/js/pull/310) [`9727502`](https://github.com/thirdweb-dev/js/commit/9727502eb30ac139382b7c7c8e8fc0967cbfbcf1) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - [SOL] - add useMintNFTSupply hook in react and accomodate inputs for it in SDK
- [#319](https://github.com/thirdweb-dev/js/pull/319) [`2f8ec89`](https://github.com/thirdweb-dev/js/commit/2f8ec89c4c83ae577092c840c198e6fcfb114e69) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose total claimed and unclaimed supply for erc721 drop contracts
- [#318](https://github.com/thirdweb-dev/js/pull/318) [`bb1b4e6`](https://github.com/thirdweb-dev/js/commit/bb1b4e6ac39d98cc68b8e31e659cd68be3a5b967) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add useMinimumNextBid hook and change invariant for other marketplace hooks
## 3.2.5
## 3.2.4
### Patch Changes
- [#301](https://github.com/thirdweb-dev/js/pull/301) [`86f0cef`](https://github.com/thirdweb-dev/js/commit/86f0ceff46f72df8ebd81f843e3c66a245f23992) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Remove useAddress hook for now
## 3.2.3
### Patch Changes
- [#299](https://github.com/thirdweb-dev/js/pull/299) [`b1218cb`](https://github.com/thirdweb-dev/js/commit/b1218cbd7d97ca7949d94a7e4ab93fef4ffbacd5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Recreate SDK on wallet changes for hook propagation
## 3.2.2
### Patch Changes
- [#289](https://github.com/thirdweb-dev/js/pull/289) [`521a49c`](https://github.com/thirdweb-dev/js/commit/521a49c6ec6a73068adcfbc1d94d2f3f17afae86) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Standarize useClaimNFT to evm
- [#274](https://github.com/thirdweb-dev/js/pull/274) [`ad06c5b`](https://github.com/thirdweb-dev/js/commit/ad06c5b28902422f9b416d65255c64c937a5e046) Thanks [@adam-maj](https://github.com/adam-maj)! - Add solana auth support and plugins for react and auth package
- [#269](https://github.com/thirdweb-dev/js/pull/269) [`b2cadf1`](https://github.com/thirdweb-dev/js/commit/b2cadf164cfe9fb27081df7530356baf70ec2b3a) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - remove deprecated chains from support
- [#272](https://github.com/thirdweb-dev/js/pull/272) [`2bdf198`](https://github.com/thirdweb-dev/js/commit/2bdf1984cb97121c447cffd27aa6a5f4f92679b3) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Add claim conditions hook for Solana
- [#291](https://github.com/thirdweb-dev/js/pull/291) [`86f6083`](https://github.com/thirdweb-dev/js/commit/86f608390a7848cda82c0f7e8913ca60f7d3901f) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - expose `.program` on `useProgram()` return type to mirror `.contract` on EVM
- [#292](https://github.com/thirdweb-dev/js/pull/292) [`492a818`](https://github.com/thirdweb-dev/js/commit/492a818079aff67c7f88bc43666c87c3714cc957) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] expose `useAddress()` hook that returns the connected wallet address in base58
- [#290](https://github.com/thirdweb-dev/js/pull/290) [`8096c78`](https://github.com/thirdweb-dev/js/commit/8096c78b466a7f5f9c2aac6e2c56a06f9d3a25d6) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - `useClaimNFT()` allow claiming to connected wallet without passing address explicitly
[EVM] - `useClaimNFT()` allow claiming to connected wallet without passing address explicitly
- [#298](https://github.com/thirdweb-dev/js/pull/298) [`e8c25ed`](https://github.com/thirdweb-dev/js/commit/e8c25ed0f28d566203d70bf59a8908e59216334a) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - fix useBalance
- [#263](https://github.com/thirdweb-dev/js/pull/263) [`d6bb61b`](https://github.com/thirdweb-dev/js/commit/d6bb61b7fac759fff7d6293edd46f693f5a7889c) Thanks [@jnsdls](https://github.com/jnsdls)! - [EVM] - add chainId getter on contracts
- [#266](https://github.com/thirdweb-dev/js/pull/266) [`3b7d10e`](https://github.com/thirdweb-dev/js/commit/3b7d10ed93d8e3b698e9f905c93ba79863b35325) Thanks [@adam-maj](https://github.com/adam-maj)! - Add storage hooks to react
- [#282](https://github.com/thirdweb-dev/js/pull/282) [`1f41adb`](https://github.com/thirdweb-dev/js/commit/1f41adbd2ccfbc367757620e0842ef32bc783a08) Thanks [@jarrodwatts](https://github.com/jarrodwatts)! - Fix useActiveClaimCondition invariant failing for token ID 0
- [#296](https://github.com/thirdweb-dev/js/pull/296) [`cf88795`](https://github.com/thirdweb-dev/js/commit/cf88795376d3110f9d3aa839928d22276904b15a) Thanks [@jnsdls](https://github.com/jnsdls)! - [SOL] - add `useBalance` hook
## 3.2.1
### Patch Changes
- [#264](https://github.com/thirdweb-dev/js/pull/264) [`f669d3e`](https://github.com/thirdweb-dev/js/commit/f669d3ef4a84368c23f0359aec304f66a4063042) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Standarize data between evm/solana for useLazyMint hook
## 3.2.0
### Patch Changes
+5 -5
View File
@@ -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 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.2.0",
"version": "3.2.6",
"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);
}
-6
View File
@@ -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 {
+3 -42
View File
@@ -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 {
@@ -70,7 +73,10 @@ export function useActiveClaimCondition(
cacheKeys.extensions.claimConditions.getActive(contractAddress, tokenId),
() => {
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getActive(tokenId);
}
if (erc721) {
@@ -123,7 +129,10 @@ export function useClaimConditions(
cacheKeys.extensions.claimConditions.getAll(contractAddress, tokenId),
() => {
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getAll(tokenId);
}
if (erc721) {
@@ -181,7 +190,7 @@ export function useClaimIneligibilityReasons(
),
() => {
if (erc1155) {
invariant(
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim ineligibility reasons",
);
@@ -267,11 +276,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) {
@@ -348,7 +360,10 @@ export function useResetClaimConditions(
};
if (erc1155) {
invariant(tokenId, "tokenId is required for ERC1155 claim conditions");
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
const claimConditions = await erc1155.claimConditions.getAll(tokenId);
return erc1155.claimConditions.set(
tokenId,
@@ -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);
}
+38 -24
View File
@@ -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);
+20 -16
View File
@@ -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;
+9 -6
View File
@@ -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);
},
+11 -9
View File
@@ -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);
});
}
+2 -1
View File
@@ -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";
+1 -1
View File
@@ -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,
+3 -5
View File
@@ -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[],
+2 -2
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
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";
+1 -1
View File
@@ -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 };
}
@@ -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 (
@@ -33,15 +35,15 @@ export function useLazyMint(
) {
const queryClient = useQueryClient();
return useMutation(
async (metadata: NFTMetadataInput[]) => {
invariant(program, "program is required");
async (data: { metadatas: NFTMetadataInput[] }) => {
requiredParamInvariant(program, "program is required");
let options;
if (onProgress) {
options = {
onProgress,
};
}
return await program.lazyMint(metadata, options);
return await program.lazyMint(data.metadatas, options);
},
{
onSettled: () =>
@@ -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 -2
View File
@@ -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";
+37 -15
View File
@@ -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>
);
};
+17 -5
View File
@@ -1,3 +1,4 @@
import { ThirdwebAuthConfig } from "../contexts/thirdweb-auth";
import { ThirdwebSDKProvider } from "./base";
import type { WalletAdapter } from "@solana/wallet-adapter-base";
import {
@@ -13,6 +14,7 @@ interface ThirdwebProviderProps {
network: Network;
wallets?: WalletAdapter[];
autoConnect?: boolean;
authConfig?: ThirdwebAuthConfig;
}
const DEFAULT_WALLETS = [new PhantomWalletAdapter()];
@@ -35,12 +37,18 @@ const DEFAULT_WALLETS = [new PhantomWalletAdapter()];
*/
export const ThirdwebProvider: React.FC<
PropsWithChildren<ThirdwebProviderProps>
> = ({ network, wallets = DEFAULT_WALLETS, autoConnect = true, children }) => {
> = ({
network,
wallets = DEFAULT_WALLETS,
autoConnect = true,
authConfig,
children,
}) => {
const clusterUrl = getUrlForNetwork(network);
return (
<ConnectionProvider endpoint={clusterUrl}>
<WalletProvider wallets={wallets} autoConnect={autoConnect}>
<ThirdwebWrapperProvider network={network}>
<ThirdwebWrapperProvider network={network} authConfig={authConfig}>
{children}
</ThirdwebWrapperProvider>
</WalletProvider>
@@ -52,11 +60,15 @@ export const ThirdwebProvider: React.FC<
* @internal
*/
export const ThirdwebWrapperProvider: React.FC<
PropsWithChildren<{ network?: Network }>
> = ({ network, children }) => {
PropsWithChildren<{ network?: Network; authConfig?: ThirdwebAuthConfig }>
> = ({ network, authConfig, children }) => {
const wallet = useWallet();
return (
<ThirdwebSDKProvider network={network} wallet={wallet}>
<ThirdwebSDKProvider
network={network}
wallet={wallet}
authConfig={authConfig}
>
{children}
</ThirdwebSDKProvider>
);
+32
View File
@@ -0,0 +1,32 @@
import { Amount, NFTMetadataOrUri } from "@thirdweb-dev/sdk";
/**
* The params for the {@link useMintNFT} hook mutation.
*
* @beta
*/
export type MintNFTParams = {
to?: string;
metadata: NFTMetadataOrUri;
amount?: Amount;
};
/**
* The params for the {@link useClaimNFT} hook mutation.
*
* @beta
*/
export type ClaimNFTParams = {
to?: string;
amount: Amount;
};
/**
* The params to pass to {@link useMintNFTSupply} hook mutation
* @beta
*/
export type MintNFTSupplyParams = {
to?: string;
nftAddress: string;
amount: Amount;
};

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