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
91 changed files with 1343 additions and 466 deletions
+8
View File
@@ -1,5 +1,13 @@
# @thirdweb-dev/auth
## 2.0.18
## 2.0.17
## 2.0.16
## 2.0.15
## 2.0.14
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "2.0.14",
"version": "2.0.18",
"exports": {
"./next": {
"module": "./next/dist/thirdweb-dev-auth-next.esm.js",
+28
View File
@@ -1,5 +1,33 @@
# 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
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.8.0",
"version": "0.8.4",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+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",
+26
View File
@@ -1,5 +1,31 @@
# @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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.2.2",
"version": "3.2.6",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,4 +1,4 @@
import { RequiredParam } from "../types/shared";
import { RequiredParam } from "./required-param";
import type { QueryKey } from "@tanstack/react-query";
import type { ValidContractInstance } from "@thirdweb-dev/sdk";
import type {
@@ -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;
@@ -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,17 +11,8 @@ interface ThirdwebConfigContext {
supportedChains: Chain[];
}
export const defaultChainRpc: Record<SupportedChainId | number, string> = {
[ChainId.Mainnet]: "mainnet",
[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,8 +73,8 @@ export function useActiveClaimCondition(
cacheKeys.extensions.claimConditions.getActive(contractAddress, tokenId),
() => {
if (erc1155) {
invariant(
tokenId !== undefined,
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim conditions",
);
return erc1155.claimConditions.getActive(tokenId);
@@ -126,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) {
@@ -184,7 +190,7 @@ export function useClaimIneligibilityReasons(
),
() => {
if (erc1155) {
invariant(
requiredParamInvariant(
tokenId,
"tokenId is required for ERC1155 claim ineligibility reasons",
);
@@ -270,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) {
@@ -351,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);
}
+25 -23
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,10 +208,10 @@ export function useClaimNFT<TContract extends DropContract>(
return useMutation(
async (data: ClaimNFTParams) => {
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,
@@ -270,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 = {
@@ -316,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 = {
@@ -370,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 -8
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,
@@ -39,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();
@@ -75,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);
@@ -109,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;
@@ -168,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);
}
@@ -350,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) => ({
@@ -415,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
* }
*
+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[],
+1 -1
View File
@@ -1,4 +1,4 @@
import { RequiredParam } from "../core/types/shared";
import { RequiredParam } from "../core/query-utils/required-param";
import type {
AirdropInput,
Amount,
+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";
@@ -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";
@@ -15,15 +15,10 @@ import invariant from "tiny-invariant";
*
* 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 { 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)),
},
);
}
@@ -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 type { NFTDrop } from "@thirdweb-dev/sdk/solana";
import invariant from "tiny-invariant";
@@ -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 { ClaimNFTParams } from "../../../types";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { NFTDrop } from "@thirdweb-dev/sdk/solana";
@@ -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();
},
@@ -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
@@ -34,7 +36,7 @@ export function useLazyMint(
const queryClient = useQueryClient();
return useMutation(
async (data: { metadatas: NFTMetadataInput[] }) => {
invariant(program, "program is required");
requiredParamInvariant(program, "program is required");
let options;
if (onProgress) {
options = {
@@ -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 { useMutation, useQueryClient } from "@tanstack/react-query";
import type { NFTDrop, NFTDropConditionsInput } from "@thirdweb-dev/sdk/solana";
import invariant from "tiny-invariant";
@@ -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();
},
@@ -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
@@ -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,
@@ -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,
@@ -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;
@@ -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,7 +2,10 @@ 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 {
@@ -17,7 +20,6 @@ import type {
ThirdwebSDK,
Token,
} from "@thirdweb-dev/sdk/solana";
import invariant from "tiny-invariant";
type ProgramMap = Readonly<{
"nft-collection": NFTCollection;
@@ -42,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?
@@ -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,8 +18,8 @@ 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.getProgramType(address);
},
@@ -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
@@ -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,
@@ -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";
@@ -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;
@@ -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);
},
{
@@ -1,11 +0,0 @@
import { useSDK } from "../../providers/base";
/**
* Get the currently connected wallet address
*
* @returns the address of the connected wallet (in base58 string format)
*/
export function useAddress() {
const sdk = useSDK();
return sdk?.wallet?.getAddress();
}
@@ -1,5 +1,5 @@
import { createSOLQueryKeyWithNetwork } from "../../../core/query-utils/query-key";
import { RequiredParam } from "../../../core/types/shared";
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";
+4 -3
View File
@@ -12,11 +12,13 @@ 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";
@@ -35,5 +37,4 @@ export * from "./hooks/token/useTransferToken";
export * from "./hooks/auth";
//Wallet
export * from "./hooks/wallet/useAddress";
export * from "./hooks/wallet/useBalance";
+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,
+22 -1
View File
@@ -1,4 +1,15 @@
import { Amount } from "@thirdweb-dev/sdk";
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.
@@ -9,3 +20,13 @@ 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;
};
+32
View File
@@ -1,5 +1,37 @@
# @thirdweb-dev/sdk
## 3.2.6
### Patch Changes
- [#317](https://github.com/thirdweb-dev/js/pull/317) [`4f867e1`](https://github.com/thirdweb-dev/js/commit/4f867e15edbe5757afbf54cab207458711a8986c) Thanks [@adam-maj](https://github.com/adam-maj)! - Add supply to nft.get
- [#312](https://github.com/thirdweb-dev/js/pull/312) [`b6eec61`](https://github.com/thirdweb-dev/js/commit/b6eec6146c23b66a3681bbb48b173b9019254214) Thanks [@adam-maj](https://github.com/adam-maj)! - Add quantity to mint additional supply
- [#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
- [#307](https://github.com/thirdweb-dev/js/pull/307) [`782b58d`](https://github.com/thirdweb-dev/js/commit/782b58d3f6a3b49ff9b3d837551db5161f96171a) Thanks [@kumaryash90](https://github.com/kumaryash90)! - fix multicall gasless
- [#320](https://github.com/thirdweb-dev/js/pull/320) [`f41a443`](https://github.com/thirdweb-dev/js/commit/f41a443c5785240197607b5bf93ff18a1c8e8307) Thanks [@adam-maj](https://github.com/adam-maj)! - Add update creators and update royalty
- [#313](https://github.com/thirdweb-dev/js/pull/313) [`f45ef62`](https://github.com/thirdweb-dev/js/commit/f45ef62128cfefe06275a38440c90d45e9f3cef7) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Return default metadata when failing to fetch a given tokenId on NFT contracts
- [#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
- [#322](https://github.com/thirdweb-dev/js/pull/322) [`b7cd744`](https://github.com/thirdweb-dev/js/commit/b7cd744cd0482a6c3fe5ae0c858f20cd380d4114) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Allow signing multiple claims at once
- [#306](https://github.com/thirdweb-dev/js/pull/306) [`53c6507`](https://github.com/thirdweb-dev/js/commit/53c65076696a43bd9a7e58a7493d4e65c172900f) Thanks [@adam-maj](https://github.com/adam-maj)! - Add contract deploy listener
## 3.2.5
### Patch Changes
- [#303](https://github.com/thirdweb-dev/js/pull/303) [`712b11a`](https://github.com/thirdweb-dev/js/commit/712b11a43e389ae10b7f09648779a69c05b2c913) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - [SOL] Fix getting real NFT owners
## 3.2.4
## 3.2.3
## 3.2.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.2.2",
"version": "3.2.6",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -179,7 +179,7 @@ export function extractEventsFromAbi(
abi: z.input<typeof AbiSchema>,
metadata?: Record<string, any>,
): AbiEvent[] {
const events = abi.filter((el) => el.type === "event");
const events = (abi || []).filter((el) => el.type === "event");
const parsed: AbiEvent[] = [];
for (const e of events) {
const doc = extractCommentFromMetadata(e.name, metadata, "events");
+1 -1
View File
@@ -24,7 +24,7 @@ import type {
} from "@thirdweb-dev/storage";
import { BigNumber, BigNumberish, Contract, ethers, providers } from "ethers";
const FALLBACK_METADATA = {
export const FALLBACK_METADATA = {
name: "Failed to load NFT metadata",
};
+45 -10
View File
@@ -1,5 +1,6 @@
import { SignerOrProvider } from "../core/types";
import { StaticJsonRpcBatchProvider } from "../lib/static-batch-rpc";
import { ChainId, SUPPORTED_CHAIN_ID, SUPPORTED_CHAIN_IDS } from "./chains";
import { ethers, providers } from "ethers";
/**
@@ -26,13 +27,15 @@ export const PINATA_IPFS_URL = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
/**
* @internal
* This is a community API key that is subject to rate limiting. Please use your own key.
*/
export type ChainOrRpc =
const DEFAULT_API_KEY = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC";
type ChainNames =
| "mumbai"
| "polygon"
// common alias for `polygon`
| "matic"
| "rinkeby"
| "goerli"
| "mainnet"
// common alias for `mainnet`
@@ -44,21 +47,53 @@ export type ChainOrRpc =
// actual name
| "avalanche-fuji"
| "optimism"
| "optimism-kovan"
| "optimism-goerli"
| "arbitrum"
| "arbitrum-rinkeby"
| "arbitrum-goerli"
| "binance"
| "binance-testnet"
// ideally we could use `https://${string}` notation here, but doing that causes anything that is a generic string to throw a type error => not worth the hassle for now
| (string & {});
| "binance-testnet";
/**
* @internal
* This is a community API key that is subject to rate limiting. Please use your own key.
*/
const DEFAULT_API_KEY = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC";
export type ChainOrRpc =
// ideally we could use `https://${string}` notation here, but doing that causes anything that is a generic string to throw a type error => not worth the hassle for now
ChainNames | (string & {});
export const CHAIN_NAME_TO_ID: Record<ChainNames, SUPPORTED_CHAIN_ID> = {
"avalanche-fuji": ChainId.AvalancheFujiTestnet,
"avalanche-testnet": ChainId.AvalancheFujiTestnet,
"fantom-testnet": ChainId.FantomTestnet,
ethereum: ChainId.Mainnet,
matic: ChainId.Polygon,
mumbai: ChainId.Mumbai,
goerli: ChainId.Goerli,
polygon: ChainId.Polygon,
mainnet: ChainId.Mainnet,
optimism: ChainId.Optimism,
"optimism-goerli": ChainId.OptimismGoerli,
arbitrum: ChainId.Arbitrum,
"arbitrum-goerli": ChainId.ArbitrumGoerli,
fantom: ChainId.Fantom,
avalanche: ChainId.Avalanche,
binance: ChainId.BinanceSmartChainMainnet,
"binance-testnet": ChainId.BinanceSmartChainTestnet,
};
export const CHAIN_ID_TO_NAME = Object.fromEntries(
Object.entries(CHAIN_NAME_TO_ID).map(([name, id]) => [id, name]),
) as Record<ChainId, ChainNames>;
function buildDefaultMap() {
return SUPPORTED_CHAIN_IDS.reduce((previousValue, currentValue) => {
previousValue[currentValue] = getProviderForNetwork(
CHAIN_ID_TO_NAME[currentValue],
) as string;
return previousValue;
}, {} as Record<SUPPORTED_CHAIN_ID, string>);
}
export const DEFAULT_RPC_URLS: Record<SUPPORTED_CHAIN_ID, string> =
buildDefaultMap();
/**
* @internal
@@ -319,7 +319,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
* @returns the unclaimed supply
*/
public async totalClaimedSupply(): Promise<BigNumber> {
return await this.contractWrapper.readContract.nextTokenIdToClaim();
return this.erc721.totalClaimedSupply();
}
/**
@@ -335,9 +335,7 @@ export class NFTDrop extends StandardErc721<DropERC721> {
* @returns the unclaimed supply
*/
public async totalUnclaimedSupply(): Promise<BigNumber> {
return (await this.contractWrapper.readContract.nextTokenIdToMint()).sub(
await this.totalClaimedSupply(),
);
return this.erc721.totalUnclaimedSupply();
}
/**
@@ -328,7 +328,7 @@ export class SignatureDrop extends StandardErc721<SignatureDropContract> {
* @returns the claimed supply
*/
public async totalClaimedSupply(): Promise<BigNumber> {
return await this.contractWrapper.readContract.totalMinted();
return this.erc721.totalClaimedSupply();
}
/**
@@ -344,10 +344,7 @@ export class SignatureDrop extends StandardErc721<SignatureDropContract> {
* @returns the unclaimed supply
*/
public async totalUnclaimedSupply(): Promise<BigNumber> {
const maxSupply =
await this.contractWrapper.readContract.nextTokenIdToMint();
return maxSupply.sub(await this.totalClaimedSupply());
return this.erc721.totalUnclaimedSupply();
}
/**
@@ -37,6 +37,7 @@ import {
PrebuiltContractType,
} from "../types";
import { ContractFactory } from "./factory";
import { FactoryEvents } from "./factory-events";
import { ContractRegistry } from "./registry";
import { RPCConnectionHandler } from "./rpc-connection-handler";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
@@ -65,6 +66,7 @@ export class ContractDeployer extends RPCConnectionHandler {
* should never be accessed directly, use {@link ContractDeployer.getRegistry} instead
*/
private _registry: Promise<ContractRegistry> | undefined;
public events: FactoryEvents | undefined;
private storage: ThirdwebStorage;
constructor(
@@ -74,6 +76,10 @@ export class ContractDeployer extends RPCConnectionHandler {
) {
super(network, options);
this.storage = storage;
// Initialize factory and registry (we don't need to make these calls async)
this.getFactory();
this.getRegistry();
}
/**
@@ -527,12 +533,15 @@ export class ContractDeployer extends RPCConnectionHandler {
chainId,
"twFactory",
);
return new ContractFactory(
const factory = new ContractFactory(
factoryAddress,
this.getSignerOrProvider(),
this.storage,
this.options,
);
this.events = new FactoryEvents(factory);
return factory;
}));
}
@@ -10,7 +10,7 @@ import { ListenerFn } from "eventemitter3";
* @public
*/
export class ContractEvents<TContract extends BaseContract> {
private contractWrapper;
protected contractWrapper;
constructor(contractWrapper: ContractWrapper<TContract>) {
this.contractWrapper = contractWrapper;
@@ -343,6 +343,13 @@ export class ContractWrapper<
("openzeppelin" in this.options.gasless ||
"biconomy" in this.options.gasless)
) {
if (fn === "multicall" && Array.isArray(args[0]) && args[0].length > 0) {
const from = await this.getSignerAddress();
args[0] = args[0].map((tx: any) =>
ethers.utils.solidityPack(["bytes", "address"], [tx, from]),
);
}
const provider = this.getProvider();
const txHash = await this.sendGaslessTransaction(fn, args, callOverrides);
this.emitTransactionEvent("submitted", txHash);
@@ -7,7 +7,7 @@ import {
hasFunction,
NotFoundError,
} from "../../common";
import { fetchTokenMetadata } from "../../common/nft";
import { FALLBACK_METADATA, fetchTokenMetadata } from "../../common/nft";
import {
FEATURE_EDITION,
FEATURE_EDITION_BATCH_MINTABLE,
@@ -128,7 +128,11 @@ export class Erc1155<
this.contractWrapper.readContract
.totalSupply(tokenId)
.catch(() => BigNumber.from(0)),
this.getTokenMetadata(tokenId),
this.getTokenMetadata(tokenId).catch(() => ({
id: tokenId.toString(),
uri: "",
...FALLBACK_METADATA,
})),
]);
return {
owner: ethers.constants.AddressZero,
@@ -98,7 +98,7 @@ export class Erc721ClaimableWithConditions implements DetectableFeature {
quantity,
options?.checkERC20Allowance === undefined
? true
: options.checkERC20Allowance
: options.checkERC20Allowance,
);
return TransactionTask.make({
+39 -2
View File
@@ -7,7 +7,7 @@ import {
hasFunction,
NotFoundError,
} from "../../common";
import { fetchTokenMetadata } from "../../common/nft";
import { FALLBACK_METADATA, fetchTokenMetadata } from "../../common/nft";
import {
FEATURE_NFT,
FEATURE_NFT_BATCH_MINTABLE,
@@ -124,7 +124,11 @@ export class Erc721<
public async get(tokenId: BigNumberish): Promise<NFT> {
const [owner, metadata] = await Promise.all([
this.ownerOf(tokenId).catch(() => constants.AddressZero),
this.getTokenMetadata(tokenId),
this.getTokenMetadata(tokenId).catch(() => ({
id: tokenId.toString(),
uri: "",
...FALLBACK_METADATA,
})),
]);
return { owner, metadata, type: "ERC721", supply: 1 };
}
@@ -574,6 +578,7 @@ export class Erc721<
*
* @param destinationAddress - Address you want to send the token to
* @param quantity - Quantity of the tokens you want to claim
* @param options
* @twfeature ERC721Claimable
* @returns - an array of results containing the id of the token claimed, the transaction receipt and a promise to optionally fetch the nft metadata
*/
@@ -598,6 +603,7 @@ export class Erc721<
* This is useful for estimating the gas cost of a claim transaction, overriding transaction options and having fine grained control over the transaction execution.
* @param destinationAddress
* @param quantity
* @param options
*/
public async getClaimTransaction(
destinationAddress: string,
@@ -619,6 +625,37 @@ export class Erc721<
throw new ExtensionNotImplementedError(FEATURE_NFT_CLAIMABLE);
}
public async totalClaimedSupply(): Promise<BigNumber> {
const contract = this.contractWrapper;
if (hasFunction<DropERC721>("nextTokenIdToClaim", contract)) {
return contract.readContract.nextTokenIdToClaim();
}
if (hasFunction<SignatureDrop>("totalMinted", contract)) {
return contract.readContract.totalMinted();
}
throw new Error(
"No function found on contract to get total claimed supply",
);
}
/**
* Get the unclaimed supply
*
* @remarks Get the number of unclaimed NFTs in this Drop.
*
* * @example
* ```javascript
* const unclaimedNFTCount = await contract.totalUnclaimedSupply();
* console.log(`NFTs left to claim: ${unclaimedNFTCount}`);
* ```
* @returns the unclaimed supply
*/
public async totalUnclaimedSupply(): Promise<BigNumber> {
return (await this.nextTokenIdToMint()).sub(
await this.totalClaimedSupply(),
);
}
/**
* Configure claim conditions
* @remarks Define who can claim NFTs in the collection, when and how many.
@@ -0,0 +1,38 @@
import { ContractEvents } from "./contract-events";
import { ContractWrapper } from "./contract-wrapper";
import type { TWFactory } from "@thirdweb-dev/contracts-js";
import { ProxyDeployedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/TWFactory";
interface DeployEvent {
transactionHash: string;
contractAddress: string;
}
export class FactoryEvents extends ContractEvents<TWFactory> {
constructor(contractWrapper: ContractWrapper<TWFactory>) {
super(contractWrapper);
}
public addDeployListener(listener: (event: DeployEvent) => void) {
this.addTransactionListener(async (event) => {
if (!event.transactionHash) {
return;
}
const receipt = await this.contractWrapper
.getProvider()
.getTransactionReceipt(event.transactionHash);
const events = this.contractWrapper.parseLogs<ProxyDeployedEvent>(
"ProxyDeployed",
receipt.logs,
);
if (events.length > 0) {
listener({
...event,
contractAddress: events[0].args.proxy,
});
}
});
}
}
@@ -78,6 +78,7 @@ export class ContractFactory extends ContractWrapper<TWFactory> {
encodedFunc,
]);
} catch (e) {
console.log(e);
// if the error is caused by user cancelling the transaction, just re-throw it
if (
(e as TransactionError).message
+1 -1
View File
@@ -200,7 +200,7 @@ export class Deployer {
* const metadata = {
* name: "My NFT Drop",
* symbol: "NFT",
* totalSuppply: 5,
* totalSupply: 5,
* };
*
* const address = await sdk.deployer.createNftDrop(metadata);
@@ -1,4 +1,4 @@
import { CreatorInput } from "../../types/programs";
import { CreatorInput, CreatorOutput } from "../../types/programs";
import type { Creator } from "@metaplex-foundation/js";
import { PublicKey } from "@solana/web3.js";
@@ -23,3 +23,13 @@ export function enforceCreator(
share: creator.sharePercentage,
}));
}
/**
* @internal
*/
export function parseCreators(creators: Creator[]): CreatorOutput[] {
return creators.map((creator) => ({
...creator,
address: creator.address.toBase58(),
}));
}
@@ -8,7 +8,9 @@ import {
METAPLEX_PROGRAM_ID,
} from "../../constants/addresses";
import { TransactionResult } from "../../types/common";
import { getPublicRpc } from "../../utils/urls";
import {
findMasterEditionV2Pda,
GmaBuilder,
JsonMetadata,
Metadata,
@@ -21,9 +23,16 @@ import {
token,
toMetadata,
toMetadataAccount,
toNftOriginalEdition,
toOriginalEditionAccount,
} from "@metaplex-foundation/js";
import { getAccount, getAssociatedTokenAddress } from "@solana/spl-token";
import { ConfirmedSignatureInfo, Connection, PublicKey } from "@solana/web3.js";
import {
ConfirmedSignatureInfo,
Connection,
ParsedAccountData,
PublicKey,
} from "@solana/web3.js";
/**
* @internal
@@ -80,6 +89,27 @@ export class NFTHelper {
}
}
async ownerOf(nftAddress: string): Promise<string | undefined> {
try {
// TODO switch back to normal connection when alchemy supports getTokenLargestAccounts
const connection = new Connection(
getPublicRpc(this.metaplex),
"confirmed",
);
const largestAccounts = await connection.getTokenLargestAccounts(
new PublicKey(nftAddress),
);
const largestAccountInfo = await connection.getParsedAccountInfo(
largestAccounts.value[0].address,
);
const parsedData = largestAccountInfo?.value?.data as ParsedAccountData;
const owner = parsedData ? parsedData.parsed.info.owner : undefined;
return owner;
} catch (err) {
return undefined;
}
}
async getAll(
collectionAddress: string,
queryParams?: QueryAllParams,
@@ -193,6 +223,25 @@ export class NFTHelper {
return nfts;
}
async supplyOf(nftAddress: string): Promise<number> {
let originalEdition;
const originalEditionAccount = await this.metaplex
.rpc()
.getAccount(findMasterEditionV2Pda(new PublicKey(nftAddress)));
if (originalEditionAccount.exists) {
originalEdition = toNftOriginalEdition(
toOriginalEditionAccount(originalEditionAccount),
);
} else {
return 0;
}
// Add one to supply to account for the master edition
return originalEdition.supply.toNumber() + 1;
}
async toNFTMetadata(
meta:
| Nft
@@ -213,18 +262,24 @@ export class NFTHelper {
if (!mint) {
throw new Error("No mint found for NFT");
}
return this.toNFTMetadataResolved(mint, fullModel);
const [owner, supply] = await Promise.all([
this.ownerOf(mint.address.toBase58()),
this.supplyOf(mint.address.toBase58()),
]);
return this.toNFTMetadataResolved(mint, owner, supply, fullModel);
}
private toNFTMetadataResolved(
private async toNFTMetadataResolved(
mint: Mint,
owner: string | undefined,
supply: number,
fullModel:
| Nft
| Sft
| NftWithToken
| SftWithToken
| Metadata<JsonMetadata<string>>,
): NFT {
): Promise<NFT> {
return {
metadata: {
id: mint.address.toBase58(),
@@ -233,8 +288,8 @@ export class NFTHelper {
symbol: fullModel.symbol,
...fullModel.json,
},
owner: fullModel.updateAuthorityAddress.toBase58(),
supply: mint.supply.basisPoints.toNumber(),
owner: owner || PublicKey.default.toBase58(),
supply: supply,
type: "metaplex",
};
}
+144 -104
View File
@@ -1,22 +1,24 @@
import { QueryAllParams } from "../../core/schema/QueryParams";
import { NFT, NFTMetadata, NFTMetadataOrUri } from "../../core/schema/nft";
import {
NFT,
NFTMetadata,
NFTMetadataInput,
NFTMetadataOrUri,
} from "../../core/schema/nft";
import { enforceCreator } from "../classes/helpers/creators-helper";
enforceCreator,
parseCreators,
} from "../classes/helpers/creators-helper";
import { NFTHelper } from "../classes/helpers/nft-helper";
import { TransactionResult } from "../types/common";
import { CreatorInput } from "../types/programs";
import { Amount, TransactionResult } from "../types/common";
import { CreatorInput, CreatorOutput } from "../types/programs";
import { sendMultipartTransaction } from "../utils/transactions";
import { getNework } from "../utils/urls";
import {
findEditionMarkerPda,
findMasterEditionV2Pda,
Metaplex,
toBigNumber,
toNftOriginalEdition,
toOriginalEditionAccount,
} from "@metaplex-foundation/js";
import { EditionMarker } from "@metaplex-foundation/mpl-token-metadata";
import { PublicKey } from "@solana/web3.js";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import invariant from "tiny-invariant";
/**
* A collection of associated NFTs
@@ -41,15 +43,7 @@ export class NFTCollection {
public publicKey: PublicKey;
public accountType = "nft-collection" as const;
public get network() {
const url = new URL(this.metaplex.connection.rpcEndpoint);
// try this first to avoid hitting `custom` network for alchemy urls
if (url.hostname.includes("devnet")) {
return "devnet";
}
if (url.hostname.includes("mainnet")) {
return "mainnet-beta";
}
return this.metaplex.cluster;
return getNework(this.metaplex);
}
constructor(
@@ -70,18 +64,44 @@ export class NFTCollection {
* @example
* ```jsx
* const metadata = await program.getMetadata();
* console.log(metadata.name);
* console.log(metadata);
* ```
*/
async getMetadata(): Promise<NFTMetadata> {
const metadata = await this.metaplex
.nfts()
.findByMint({ mintAddress: this.publicKey })
.run();
const metadata = await this.getCollection();
return (await this.nft.toNFTMetadata(metadata)).metadata;
}
/**
* Get the creators of this program.
* @returns program metadata
*
* @example
* ```jsx
* const creators = await program.getCreators();
* console.log(creators);
* ```
*/
async getCreators(): Promise<CreatorOutput[]> {
const metadata = await this.getCollection();
return parseCreators(metadata.creators);
}
/**
* Get the royalty basis points for this collection
* @returns royalty basis points
*
* @example
* ```jsx
* const royalty = await program.getRoyalty();
* console.log(royalty);
* ```
*/
async getRoyalty(): Promise<number> {
const metadata = await this.getCollection();
return metadata.sellerFeeBasisPoints;
}
/**
* Get the metadata for a specific NFT
* @param nftAddress - the mint address of the NFT to get
@@ -155,6 +175,21 @@ export class NFTCollection {
return this.nft.balanceOf(walletAddress, nftAddress);
}
/**
* Get the current owner of the given NFT
* @param nftAddress - the mint address of the NFT to get the owner of
* @returns the owner of the NFT
* @example
* ```jsx
* const nftAddress = "..."
* const owner = await program.ownerOf(nftAddress);
* console.log(owner);
* ```
*/
async ownerOf(nftAddress: string): Promise<string | undefined> {
return this.nft.ownerOf(nftAddress);
}
/**
* Get the supply of NFT editions minted from a specific NFT
* @param nftAddress - the mint address of the NFT to check the supply of
@@ -163,58 +198,11 @@ export class NFTCollection {
* @example
* ```jsx
* const address = "...";
* const supply = await program.supplyOf(addres);
* const supply = await program.supplyOf(address);
* ```
*/
async supplyOf(nftAddress: string): Promise<bigint> {
let editionMarkerNumber = 0;
let totalSupply = 1;
cursedBitwiseLogicLoop: while (true) {
const editionMarkerAddress = findEditionMarkerPda(
new PublicKey(nftAddress),
toBigNumber(editionMarkerNumber * 248),
);
const editionMarker = await EditionMarker.fromAccountAddress(
this.metaplex.connection,
editionMarkerAddress,
);
// WARNING: Ugly bitwise operations because of Rust :(
const indexCap = editionMarkerNumber === 0 ? 247 : 248;
for (let editionIndex = 0; editionIndex < indexCap; editionIndex++) {
const ledgerIndex =
editionMarkerNumber > 0
? Math.floor(editionIndex / 8)
: editionIndex < 7
? 0
: Math.floor((editionIndex - 7) / 8) + 1;
const size = editionMarkerNumber === 0 && ledgerIndex === 0 ? 7 : 8;
const shiftBase = 0b1 << (size - 1);
const bitmask =
ledgerIndex === 0
? shiftBase >> editionIndex
: editionMarkerNumber > 0
? shiftBase >> editionIndex % (ledgerIndex * 8)
: ledgerIndex === 1
? shiftBase >> (editionIndex - 7)
: shiftBase >> (editionIndex - 7) % ((ledgerIndex - 1) * 8);
const editionExists =
(editionMarker.ledger[ledgerIndex] & bitmask) !== 0;
if (editionExists) {
totalSupply += 1;
} else {
break cursedBitwiseLogicLoop;
}
}
editionMarkerNumber++;
}
return BigInt(totalSupply);
async supplyOf(nftAddress: string): Promise<number> {
return this.nft.supplyOf(nftAddress);
}
/**
@@ -264,7 +252,7 @@ export class NFTCollection {
* console.log(address);
* ```
*/
async mint(metadata: NFTMetadataInput): Promise<string> {
async mint(metadata: NFTMetadataOrUri): Promise<string> {
const address = this.metaplex.identity().publicKey.toBase58();
return this.mintTo(address, metadata);
}
@@ -326,25 +314,29 @@ export class NFTCollection {
/**
* Mint additional supply of an NFT to the connected wallet
* @param nftAddress - the mint address to mint additional supply to
* @param amount - the amount of additional NFTs to mint
* @returns the mint address of the minted NFT
*
* @example
* ```jsx
* // The address of the already minted NFT
* const nftAddress = "..."
* // The amount of additional NFTs to mint
* const amount = 1;
* // Mint an additional NFT of the original NFT
* const address = await program.mintAdditionalSupply(nftAddress);
* const addresses = await program.mintAdditionalSupply(nftAddress, amount);
* ```
*/
async mintAdditionalSupply(nftAddress: string) {
async mintAdditionalSupply(nftAddress: string, amount: Amount) {
const address = this.metaplex.identity().publicKey.toBase58();
return this.mintAdditionalSupplyTo(address, nftAddress);
return this.mintAdditionalSupplyTo(address, nftAddress, amount);
}
/**
* Mint additional supply of an NFT to the specified wallet
* @param to - the address to mint the NFT to
* @param nftAddress - the mint address to mint additional supply to
* @param amount - the amount of NFTs to mint
* @returns the mint address of the minted NFT
*
* @example
@@ -353,23 +345,52 @@ export class NFTCollection {
* const to = "..."
* // The address of the already minted NFT
* const nftAddress = "..."
* * // The amount of additional NFTs to mint
* const amount = 1;
* // Mint an additional NFT of the original NFT
* const address = await program.mintAdditionalSupplyTo(to, nftAddress);
* const addresses = await program.mintAdditionalSupplyTo(to, nftAddress, amount);
* ```
*/
async mintAdditionalSupplyTo(
to: string,
nftAddress: string,
): Promise<string> {
// TODO add quantity param
const result = await this.metaplex
.nfts()
.printNewEdition({
originalMint: new PublicKey(nftAddress),
newOwner: new PublicKey(to),
})
.run();
return result.nft.address.toBase58();
amount: Amount,
): Promise<string[]> {
// need to coerce amount to number always (to make the Array(<number>) work)
amount = typeof amount === "string" ? parseInt(amount) : amount;
// ensure that whatever got passed actually is a number now
invariant(
isNaN(amount) === false,
"amount must be possible to convert to a number",
);
// Better to use metaplex functions directly then our supplyOf function for types/consistency
const originalEditionAccount = await this.metaplex
.rpc()
.getAccount(findMasterEditionV2Pda(new PublicKey(nftAddress)));
const originalEdition = toNftOriginalEdition(
toOriginalEditionAccount(originalEditionAccount),
);
const builders = await Promise.all(
[...Array(amount).keys()].map(async (_, i) => {
return await this.metaplex
.nfts()
.builders()
.printNewEdition({
originalSupply: toBigNumber(
originalEdition.supply.add(toBigNumber(i)),
),
originalMint: new PublicKey(nftAddress),
newOwner: new PublicKey(to),
});
}),
);
const mintAddresses = builders.map((builder) =>
builder.getContext().mintSigner.publicKey.toBase58(),
);
await sendMultipartTransaction(builders, this.metaplex);
return mintAddresses;
}
/**
@@ -399,22 +420,41 @@ export class NFTCollection {
}
/**
* Update the settings of the collection
* @param settings - the settings to update
* SETTINGS
*/
async updateSettings(settings: { creators?: CreatorInput[] }) {
const updateData = {
...(settings.creators && {
creators: enforceCreator(
settings.creators,
this.metaplex.identity().publicKey,
),
}),
/**
* Update the creators of the collection
* @param creators - the creators to update
*/
async updateCreators(creators: CreatorInput[]) {
const tx = await this.metaplex
.nfts()
.update({
nftOrSft: await this.getCollection(),
creators: enforceCreator(creators, this.metaplex.identity().publicKey),
})
.run();
return {
signature: tx.response.signature,
};
}
/**
* Update the royalty basis points of the collection
* @param sellerFeeBasisPoints - the royalty basis points of the collection
*/
async updateRoyalty(sellerFeeBasisPoints: number) {
const tx = await this.metaplex
.nfts()
.update({
nftOrSft: await this.getCollection(),
sellerFeeBasisPoints,
})
.run();
return {
signature: tx.response.signature,
};
this.metaplex.nfts().update({
nftOrSft: await this.getCollection(),
...updateData,
});
}
private async getCollection() {
+129 -57
View File
@@ -5,12 +5,18 @@ import {
NFTMetadataInput,
} from "../../core/schema/nft";
import { ClaimConditions } from "../classes/claim-conditions";
import {
enforceCreator,
parseCreators,
} from "../classes/helpers/creators-helper";
import { NFTHelper } from "../classes/helpers/nft-helper";
import { Amount, TransactionResult } from "../types/common";
import { CreatorInput, CreatorOutput } from "../types/programs";
import { sendMultipartTransaction } from "../utils/transactions";
import { getNework } from "../utils/urls";
import {
CandyMachineItem,
Metaplex,
MintCandyMachineOutput,
toBigNumber,
} from "@metaplex-foundation/js";
import { PublicKey } from "@solana/web3.js";
@@ -41,19 +47,30 @@ export class NFTDrop {
private nft: NFTHelper;
public accountType = "nft-drop" as const;
public publicKey: PublicKey;
public claimConditions: ClaimConditions;
public get network() {
const url = new URL(this.metaplex.connection.rpcEndpoint);
// try this first to avoid hitting `custom` network for alchemy urls
if (url.hostname.includes("devnet")) {
return "devnet";
}
if (url.hostname.includes("mainnet")) {
return "mainnet-beta";
}
return this.metaplex.cluster;
return getNework(this.metaplex);
}
/**
* Manage the claim conditions for this drop
*
* @example
* ```jsx
* // set your claim conditions
* await program.claimConditions.set({
* maxClaimable: 100,
* price: 0.5,
* startTime: new Date(),
* });
*
* // get your claim conditions
* const conditions = await program.claimConditions.get();
* console.log(conditions.maxClaimable);
* console.log(conditions.claimedSupply);
* ```
*/
public claimConditions: ClaimConditions;
constructor(
dropAddress: string,
metaplex: Metaplex,
@@ -83,9 +100,40 @@ export class NFTDrop {
.nfts()
.findByMint({ mintAddress: info.collectionMintAddress })
.run();
return (await this.nft.toNFTMetadata(metadata)).metadata;
}
/**
* Get the creators of this program.
* @returns program metadata
*
* @example
* ```jsx
* const creators = await program.getCreators();
* console.log(creators);
* ```
*/
async getCreators(): Promise<CreatorOutput[]> {
const info = await this.getCandyMachine();
return parseCreators(info.creators);
}
/**
* Get the royalty basis points for this collection
* @returns royalty basis points
*
* @example
* ```jsx
* const royalty = await program.getRoyalty();
* console.log(royalty);
* ```
*/
async getRoyalty(): Promise<number> {
const info = await this.getCandyMachine();
return info.sellerFeeBasisPoints;
}
/**
* Get the metadata for a specific NFT
* @param nftAddress - the mint address of the NFT to get
@@ -204,6 +252,21 @@ export class NFTDrop {
return this.nft.balanceOf(walletAddress, nftAddress);
}
/**
* Get the current owner of the given NFT
* @param nftAddress - the mint address of the NFT to get the owner of
* @returns the owner of the NFT
* @example
* ```jsx
* const nftAddress = "..."
* const owner = await program.ownerOf(nftAddress);
* console.log(owner);
* ```
*/
async ownerOf(nftAddress: string): Promise<string | undefined> {
return this.nft.ownerOf(nftAddress);
}
/**
* Get the total unclaimed supply of this drop
* @returns the total supply
@@ -262,6 +325,7 @@ export class NFTDrop {
/**
* Lazy mint NFTs to be claimed later
* @param metadatas - The metadata of the NFTs to lazy mint
* @param options
* @returns the transaction result of the lazy mint
*
* @example
@@ -307,9 +371,7 @@ export class NFTDrop {
batches.push(items.splice(0, LAZY_MINT_BATCH_SIZE));
}
const block = await this.metaplex.connection.getLatestBlockhash();
const txns = batches.map((batch, i) =>
const builders = batches.map((batch, i) =>
this.metaplex
.candyMachines()
.builders()
@@ -320,38 +382,9 @@ export class NFTDrop {
index: toBigNumber(
i * LAZY_MINT_BATCH_SIZE + candyMachine.itemsLoaded.toNumber(),
),
})
.setTransactionOptions({
blockhash: block.blockhash,
feePayer: this.metaplex.identity().publicKey,
lastValidBlockHeight: block.lastValidBlockHeight,
})
.setFeePayer(this.metaplex.identity())
.toTransaction(),
}),
);
// make the connected wallet sign both candyMachine + registry transactions
const signedTx = await this.metaplex.identity().signAllTransactions(txns);
// send the signed transactions
const signatures = await Promise.all(
signedTx.map((tx) =>
this.metaplex.connection.sendRawTransaction(tx.serialize()),
),
);
// wait for confirmations in parallel
const confirmations = await Promise.all(
signatures.map((sig) => {
return this.metaplex.rpc().confirmTransaction(sig);
}),
);
if (confirmations.length === 0) {
throw new Error("Transaction failed");
}
return signatures.map((signature) => ({ signature }));
return await sendMultipartTransaction(builders, this.metaplex);
}
/**
@@ -385,20 +418,25 @@ export class NFTDrop {
* console.log("Claimed NFT at address", claimedAddresses[0]);
* ```
*/
async claimTo(receiverAddress: string, quantity: Amount): Promise<string[]> {
async claimTo(receiverAddress: string, amount: Amount): Promise<string[]> {
const candyMachine = await this.getCandyMachine();
await this.claimConditions.assertCanClaimable(Number(quantity));
const results: MintCandyMachineOutput[] = [];
// has to claim sequentially
for (let i = 0; i < quantity; i++) {
results.push(
await this.metaplex
await this.claimConditions.assertCanClaimable(Number(amount));
const builders = await Promise.all(
[...Array(amount).keys()].map(async () => {
return await this.metaplex
.candyMachines()
.mint({ candyMachine, newOwner: new PublicKey(receiverAddress) })
.run(),
);
}
return results.map((result) => result.nft.address.toBase58());
.builders()
.mint({
candyMachine,
newOwner: new PublicKey(receiverAddress),
});
}),
);
const mintAddresses = builders.map((builder) =>
builder.getContext().mintSigner.publicKey.toBase58(),
);
await sendMultipartTransaction(builders, this.metaplex);
return mintAddresses;
}
/**
@@ -431,6 +469,40 @@ export class NFTDrop {
};
}
/**
* Update the creators of the collection
* @param creators - the creators to update
*/
async updateCreators(creators: CreatorInput[]) {
const tx = await this.metaplex
.candyMachines()
.update({
candyMachine: await this.getCandyMachine(),
creators: enforceCreator(creators, this.metaplex.identity().publicKey),
})
.run();
return {
signature: tx.response.signature,
};
}
/**
* Update the royalty basis points of the collection
* @param sellerFeeBasisPoints - the royalty basis points of the collection
*/
async updateRoyalty(sellerFeeBasisPoints: number) {
const tx = await this.metaplex
.candyMachines()
.update({
candyMachine: await this.getCandyMachine(),
sellerFeeBasisPoints,
})
.run();
return {
signature: tx.response.signature,
};
}
private async getCandyMachine() {
return this.metaplex
.candyMachines()
+2 -9
View File
@@ -1,6 +1,7 @@
import { CurrencyValue, TokenMetadata } from "../../core/schema/token";
import { Amount, AmountSchema, TransactionResult } from "../types/common";
import { toCurrencyValue } from "../utils/token";
import { getNework } from "../utils/urls";
import {
findMetadataPda,
Metaplex,
@@ -36,15 +37,7 @@ export class Token {
public accountType = "token" as const;
public publicKey: PublicKey;
public get network() {
const url = new URL(this.metaplex.connection.rpcEndpoint);
// try this first to avoid hitting `custom` network for alchemy urls
if (url.hostname.includes("devnet")) {
return "devnet";
}
if (url.hostname.includes("mainnet")) {
return "mainnet-beta";
}
return this.metaplex.cluster;
return getNework(this.metaplex);
}
constructor(
@@ -1,4 +1,5 @@
import { AmountSchema, FileOrBufferOrStringSchema } from "../common";
import { Creator } from "@metaplex-foundation/js";
import { z } from "zod";
/**
@@ -64,6 +65,13 @@ export type TokenMetadataInput = z.input<typeof TokenMetadataInputSchema>;
*/
export type CreatorInput = z.input<typeof CreatorInputSchema>;
/**
* @public
*/
export type CreatorOutput = Omit<Creator, "address"> & {
readonly address: string;
};
/**
* @internal
*/
@@ -0,0 +1,56 @@
import { TransactionResult } from "../types/common";
import {
getSignerHistogram,
Metaplex,
TransactionBuilder,
} from "@metaplex-foundation/js";
export async function sendMultipartTransaction(
builders: TransactionBuilder[],
metaplex: Metaplex,
): Promise<TransactionResult[]> {
const block = await metaplex.connection.getLatestBlockhash();
const txns = builders.map((builder) => {
const builderTx = builder
.setTransactionOptions({
blockhash: block.blockhash,
feePayer: metaplex.identity().publicKey,
lastValidBlockHeight: block.lastValidBlockHeight,
})
.setFeePayer(metaplex.identity());
const dropSigners = [metaplex.identity(), ...builderTx.getSigners()];
const { keypairs } = getSignerHistogram(dropSigners);
const tx = builderTx.toTransaction();
if (keypairs.length > 0) {
tx.partialSign(...keypairs);
}
return tx;
});
// make the connected wallet sign all transactions
const signedTx = await metaplex.identity().signAllTransactions(txns);
// send the signed transactions
let signatures = [];
for (const tx of signedTx) {
const signature = await metaplex.connection.sendRawTransaction(
tx.serialize(),
);
signatures.push(signature);
}
// wait for confirmations in parallel
const confirmations = await Promise.all(
signatures.map((sig) => {
return metaplex.rpc().confirmTransaction(sig);
}),
);
if (confirmations.length === 0) {
throw new Error("Transaction failed");
}
return signatures.map((signature) => ({ signature }));
}
+24
View File
@@ -1,4 +1,5 @@
import { Network } from "../types/index";
import { Metaplex } from "@metaplex-foundation/js";
import { Cluster, clusterApiUrl } from "@solana/web3.js";
/**
@@ -24,3 +25,26 @@ export function getUrlForNetwork(network: Network) {
return network;
}
}
export function getNework(metaplex: Metaplex) {
const url = new URL(metaplex.connection.rpcEndpoint);
// try this first to avoid hitting `custom` network for alchemy urls
if (url.hostname.includes("devnet")) {
return "devnet";
}
if (url.hostname.includes("mainnet")) {
return "mainnet-beta";
}
return metaplex.cluster;
}
export function getPublicRpc(metaplex: Metaplex) {
const url = new URL(metaplex.connection.rpcEndpoint);
if (url.hostname.includes("devnet")) {
return "https://api.devnet.solana.com";
}
if (url.hostname.includes("mainnet")) {
return "https://api.mainnet-beta.solana.com";
}
return url.toString();
}
+15
View File
@@ -90,6 +90,21 @@ describe("Events", async () => {
dropContract.events.removeAllListeners();
});
it("Should emit deploy events", async () => {
// eslint-disable-next-line no-unused-expressions
expect(sdk.deployer.events).to.not.be.undefined;
let txAddress = "";
sdk.deployer.events?.addDeployListener((event) => {
txAddress = event.contractAddress;
});
const address = await sdk.deployer.deployMarketplace({
name: "Marketplace",
});
// Wait for the async listener to get called
await new Promise((res) => setTimeout(res, 2000));
expect(txAddress).to.equal(address);
});
it("should emit Contract events", async () => {
const events: ContractEvent[] = [];
const remove = dropContract.events.addEventListener(
+40
View File
@@ -1,5 +1,6 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { ThirdwebSDK } from "../../src/evm";
import { assert, expect } from "chai";
import { ethers, Wallet } from "ethers";
const RPC_URL = "https://rpc-mumbai.maticvigil.com/";
@@ -47,4 +48,43 @@ describe("Gasless Forwarder", async () => {
await pack.open(0, 0);
});
it.skip("should mint batch via multicall tx with relayer", async () => {
const NFT_COLLECTION_ADDRESS = "0x24A5aB2878B63716B001aa9AbE816c2662192B12";
const provider = ethers.getDefaultProvider(RPC_URL);
// const wallet = Wallet.createRandom().connect(provider);
const wallet = new Wallet("<PRIVATE KEY>", provider);
const sdk = new ThirdwebSDK(wallet, {
gasless: {
openzeppelin: {
relayerUrl: "<RELAYER URL>",
},
},
});
const nftCollection = await sdk.getContract(
NFT_COLLECTION_ADDRESS,
"nft-collection",
);
const metas = [
{
name: "Test5",
},
{
name: "Test6",
},
];
const batch = await nftCollection.mintBatch(metas);
assert.lengthOf(batch, 2);
for (const meta of metas) {
const nft = batch.find(
async (n) => (await n.data()).metadata.name === meta.name,
);
assert.isDefined(nft);
}
});
});
@@ -42,6 +42,18 @@ describe("NFTCollection", async () => {
expect(balance).to.eq(1);
});
it("Should update supply", async () => {
const mint = await collection.mint({
name: "Test NFT",
description: "Test Description",
});
let nft = await collection.get(mint);
expect(nft.supply).to.eq(1);
await collection.mintAdditionalSupply(mint, 2);
nft = await collection.get(mint);
expect(nft.supply).to.eq(3);
});
it("should transfer NFTs", async () => {
const all = await collection.getAll();
const wallet = Keypair.generate();
@@ -66,11 +78,20 @@ describe("NFTCollection", async () => {
name: "Test NFT",
description: "Test Description",
});
const printed = await collection.mintAdditionalSupply(mint);
const [printed] = await collection.mintAdditionalSupply(mint, 1);
let balance = await collection.balance(mint);
expect(balance).to.equal(1);
balance = await collection.balance(printed);
expect(balance).to.equal(1);
const [nft1, nft2] = await collection.mintAdditionalSupply(mint, 2);
balance = await collection.balance(nft1);
expect(balance).to.equal(1);
balance = await collection.balance(nft2);
expect(balance).to.equal(1);
const supply = await collection.supplyOf(mint);
expect(supply).to.equal(4);
});
it("test supply of", async () => {
@@ -81,11 +102,25 @@ describe("NFTCollection", async () => {
const amount = 2;
for (let i = 0; i < amount; i++) {
await collection.mintAdditionalSupply(mint);
await collection.mintAdditionalSupply(mint, 1);
}
const supply = await collection.supplyOf(mint);
expect(supply).to.equal(BigInt(amount + 1));
expect(supply).to.equal(amount + 1);
});
it("test burn supply", async () => {
const mint = await collection.mint({
name: "Test NFT",
description: "Test Description",
});
let supply = await collection.supplyOf(mint);
expect(supply).to.equal(1);
await collection.burn(mint);
supply = await collection.supplyOf(mint);
expect(supply).to.equal(0);
});
it("should burn nfts", async () => {
@@ -93,10 +128,38 @@ describe("NFTCollection", async () => {
name: "Test NFT to burn",
description: "Test Description",
});
const all = await collection.getAll();
expect(all.length).to.eq(4);
await collection.burn(mint);
const all2 = await collection.getAll();
expect(all2.length).to.eq(3);
expect(all2.length).to.eq(all.length - 1);
});
it("should update creator settings", async () => {
let creators = await collection.getCreators();
expect(creators.length).to.equal(1);
expect(creators[0].address).to.equal(sdk.wallet.getAddress());
expect(creators[0].share).to.equal(100);
const newCreator = Keypair.generate().publicKey.toBase58();
await collection.updateCreators([
{ address: sdk.wallet.getAddress() as string, sharePercentage: 75 },
{ address: newCreator, sharePercentage: 25 },
]);
creators = await collection.getCreators();
expect(creators.length).to.equal(2);
expect(creators[0].address).to.equal(sdk.wallet.getAddress());
expect(creators[0].share).to.equal(75);
expect(creators[1].address).to.equal(newCreator);
expect(creators[1].share).to.equal(25);
});
it("Should update royalty", async () => {
let royalty = await collection.getRoyalty();
expect(royalty).to.equal(0);
await collection.updateRoyalty(100);
royalty = await collection.getRoyalty();
expect(royalty).to.equal(100);
});
});
+40 -3
View File
@@ -1,5 +1,6 @@
import { NFTDrop } from "../../src/solana";
import { sdk } from "./before-setup";
import { Keypair } from "@solana/web3.js";
import { expect } from "chai";
describe("NFTDrop", async () => {
@@ -54,15 +55,21 @@ describe("NFTDrop", async () => {
await drop.claimConditions.set({
maxClaimable: 1,
});
const address = await drop.claim(1);
const receiver = Keypair.generate();
const address = await drop.claimTo(receiver.publicKey.toBase58(), 1);
unclaimed = await drop.totalUnclaimedSupply();
claimed = await drop.totalClaimedSupply();
expect(unclaimed).to.equal(4);
expect(claimed).to.equal(1);
const balance = await drop.balance(address[0]);
const balance = await drop.balanceOf(
receiver.publicKey.toBase58(),
address[0],
);
expect(balance).to.equal(1);
const nftClaimed = await drop.get(address[0]);
expect(nftClaimed.owner).to.equal(receiver.publicKey.toBase58());
});
it("should update claim condition", async () => {
@@ -84,7 +91,9 @@ describe("NFTDrop", async () => {
});
it("should get all nfts", async () => {
await drop.claim(2);
const claimedAddresses = await drop.claim(2);
const nftClaimed = await drop.get(claimedAddresses[0]);
expect(nftClaimed.owner).to.equal(sdk.wallet.getAddress());
const all = await drop.getAll();
const claimed = await drop.getAllClaimed();
@@ -115,4 +124,32 @@ describe("NFTDrop", async () => {
const all2 = await burnDrop.getAllClaimed();
expect(all2.length).to.eq(1);
});
it("should update creator settings", async () => {
let creators = await drop.getCreators();
expect(creators.length).to.equal(1);
expect(creators[0].address).to.equal(sdk.wallet.getAddress());
expect(creators[0].share).to.equal(100);
const newCreator = Keypair.generate().publicKey.toBase58();
await drop.updateCreators([
{ address: sdk.wallet.getAddress() as string, sharePercentage: 75 },
{ address: newCreator, sharePercentage: 25 },
]);
creators = await drop.getCreators();
expect(creators.length).to.equal(2);
expect(creators[0].address).to.equal(sdk.wallet.getAddress());
expect(creators[0].share).to.equal(75);
expect(creators[1].address).to.equal(newCreator);
expect(creators[1].share).to.equal(25);
});
it("Should update royalty", async () => {
let royalty = await drop.getRoyalty();
expect(royalty).to.equal(0);
await drop.updateRoyalty(100);
royalty = await drop.getRoyalty();
expect(royalty).to.equal(100);
});
});
+1 -1
View File
@@ -2,7 +2,7 @@ import { NFTCollection } from "../../src/solana";
import { sdk } from "./before-setup";
import { expect } from "chai";
describe("NFTCollection", async () => {
describe("NFTCollection get all", async () => {
let collection: NFTCollection;
let addr: string;