npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mycelium-ip/react

v0.2.0-alpha.22

Published

React hooks for the Mycelium IP protocol

Readme

@mycelium-ip/react

React hooks for the Mycelium IP protocol. This package provides a wallet-agnostic React integration built on top of TanStack Query.

Features

  • Wallet Standard native — Uses the Wallet Standard (@wallet-standard/base) as its first-class wallet interface
  • TanStack Query powered — Built-in caching, loading states, and automatic cache invalidation
  • Next.js compatible — Works with client components out of the box
  • TypeScript first — Full type safety with comprehensive TypeScript definitions
  • Multi-sig support — Pass controllerSigners to any mutation for multi-controller entities
  • Minimal abstraction — Thin wrapper over @mycelium-ip/core-sdk

Installation

# npm
npm install @mycelium-ip/react @mycelium-ip/core-sdk

# yarn
yarn add @mycelium-ip/react @mycelium-ip/core-sdk

# pnpm
pnpm add @mycelium-ip/react @mycelium-ip/core-sdk

Peer Dependencies

Make sure you have the following peer dependencies installed:

npm install @solana/web3.js @tanstack/react-query react

Quick Start

import { Connection } from "@solana/web3.js";
import { MyceliumIpProvider, useCreateEntity } from "@mycelium-ip/react";
import type { Wallet } from "@wallet-standard/base";

// 1. Wrap your app with the provider
function App() {
  const connection = new Connection("https://api.devnet.solana.com");
  const wallet: Wallet = useYourWallet(); // Any Wallet Standard–compliant wallet

  return (
    <MyceliumIpProvider connection={connection} wallet={wallet}>
      <CreateEntityButton />
    </MyceliumIpProvider>
  );
}

// 2. Use hooks in your components
function CreateEntityButton() {
  const { mutate, isPending, isSuccess } = useCreateEntity();

  const handleCreate = () => {
    mutate({});
  };

  return (
    <button onClick={handleCreate} disabled={isPending}>
      {isPending ? "Creating..." : "Create Entity"}
    </button>
  );
}

Wallet Integration

The SDK uses the Wallet Standard (@wallet-standard/base) as its native wallet interface. Pass any Wallet that implements the standard directly to the provider — no adapter shim required.

Required wallet features

  • solana:signTransactionrequired; the SDK throws UnsupportedFeatureError if absent
  • solana:signAndSendTransaction — optional; preferred for transaction submission when available
  • solana:signMessage — optional

Solana Wallet Adapter

If you’re using @solana/wallet-adapter-react, the useWallet() hook already returns a Wallet Standard–compliant wallet via the wallet.adapter.wallet property when the user connects a Standard-compatible browser extension. Use the @solana/wallet-standard-wallet-adapter-base package to obtain it:

"use client";

import { useWallet } from "@solana/wallet-adapter-react";
import { useConnection } from "@solana/wallet-adapter-react";
import { MyceliumIpProvider } from "@mycelium-ip/react";
import { isStandardWallet } from "@mycelium-ip/core-sdk";

function MyceliumProvider({ children }: { children: React.ReactNode }) {
  const { connection } = useConnection();
  const { wallet } = useWallet();

  // The adapter’s underlying Wallet Standard wallet
  const standardWallet =
    wallet?.adapter && "wallet" in wallet.adapter
      ? (wallet.adapter as any).wallet
      : null;

  if (!standardWallet || !isStandardWallet(standardWallet)) {
    return <>{children}</>;
  }

  return (
    <MyceliumIpProvider connection={connection} wallet={standardWallet}>
      {children}
    </MyceliumIpProvider>
  );
}

Privy Embedded Wallet

If you’re using @privy-io/react-auth with Solana embedded wallets, Privy wallets already implement the Wallet Standard. Pass the wallet directly:

"use client";

import {
  useWallets,
  useSignAndSendTransaction,
} from "@privy-io/react-auth/solana";
import { usePrivy } from "@privy-io/react-auth";
import { Connection } from "@solana/web3.js";
import { MyceliumIpProvider } from "@mycelium-ip/react";
import { isStandardWallet } from "@mycelium-ip/core-sdk";
import { useMemo } from "react";

function MyceliumPrivyProvider({ children }: { children: React.ReactNode }) {
  const { ready, authenticated } = usePrivy();
  const { wallets } = useWallets();
  const { signAndSendTransaction } = useSignAndSendTransaction();

  const embeddedWallet = wallets[0];

  const connection = useMemo(
    () => new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!),
    [],
  );

  // If the Privy wallet exposes a Wallet Standard object, use it directly
  const standardWallet =
    embeddedWallet && "wallet" in embeddedWallet
      ? (embeddedWallet as any).wallet
      : null;

  if (
    !ready ||
    !authenticated ||
    !standardWallet ||
    !isStandardWallet(standardWallet)
  ) {
    return <>{children}</>;
  }

  return (
    <MyceliumIpProvider
      connection={connection}
      wallet={standardWallet}
      executeTransaction={async (tx) => {
        const { hash } = await signAndSendTransaction({
          transaction: tx,
          options: { sponsor: true },
        });
        return { signature: hash };
      }}
    >
      {children}
    </MyceliumIpProvider>
  );
}

export default MyceliumPrivyProvider;

Complete Privy Setup Example

Here’s a complete example showing how to set up Privy with Mycelium in a Next.js App Router application:

// app/providers.tsx
"use client";

import { PrivyProvider } from "@privy-io/react-auth";
import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
import MyceliumPrivyProvider from "./MyceliumPrivyProvider";

const solanaConnectors = toSolanaWalletConnectors({
  shouldAutoConnect: true,
});

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <PrivyProvider
      appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}
      config={{
        appearance: {
          theme: "dark",
        },
        embeddedWallets: {
          solana: {
            createOnLogin: "users-without-wallets",
          },
        },
        externalWallets: {
          solana: {
            connectors: solanaConnectors,
          },
        },
      }}
    >
      <MyceliumPrivyProvider>{children}</MyceliumPrivyProvider>
    </PrivyProvider>
  );
}
// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

## Provider

### MyceliumIpProvider

The provider initializes the SDK and must wrap your application (or the part that uses Mycelium hooks).

```tsx
<MyceliumIpProvider
  connection={connection}
  wallet={wallet}
  queryClient={queryClient} // optional
  options={{
    confirmation: "confirmed",
  }}
>
  <App />
</MyceliumIpProvider>

Props

| Prop | Type | Required | Description | | ------------- | --------------------------- | -------- | ------------------------------------------------------------------------------------- | | connection | Connection | Yes | Solana RPC connection | | wallet | Wallet \| null | No | Wallet Standard–compliant wallet (@wallet-standard/base). Mutations throw when null | | queryClient | QueryClient | No | Existing TanStack Query client. If omitted, a default client is created | | options | MyceliumIpProviderOptions | No | Provider configuration options |

Options

| Option | Type | Default | Description | | -------------- | ------------ | ------------- | ---------------------------------------------------------------------------- | | confirmation | Commitment | "confirmed" | Transaction confirmation level ("processed", "confirmed", "finalized") |

Wallet Switching

When the wallet prop changes but connection and cluster stay the same, the provider efficiently swaps the wallet in-place via client.setWallet() — the SDK client, AnchorProvider, and Program instances are reused rather than reconstructed. This means wallet switching in your app "just works":

function App() {
  const wallet = useActiveWallet(); // changes when user switches wallets

  return (
    <MyceliumIpProvider connection={connection} wallet={wallet}>
      <YourApp />
    </MyceliumIpProvider>
  );
}

If connection or cluster changes, the provider creates a fresh client automatically.

Adding React Query Devtools

To enable React Query devtools for debugging, install the devtools package and add it to your app:

pnpm add @tanstack/react-query-devtools
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

function App() {
  return (
    <MyceliumIpProvider connection={connection} wallet={wallet}>
      <YourApp />
      <ReactQueryDevtools initialIsOpen={false} />
    </MyceliumIpProvider>
  );
}

Hooks Reference

All mutation hooks return TanStack Query's UseMutationResult, providing:

  • mutate / mutateAsync — Execute the mutation
  • isPending — Loading state
  • isSuccess / isError — Result states
  • data — Transaction result (signature)
  • error — Error object if failed
  • reset — Reset mutation state

Entity Hooks

useCreateEntity

Creates a new entity in the protocol. Entity index is auto-assigned from the on-chain counter.

import { useCreateEntity } from "@mycelium-ip/react";

function CreateEntity() {
  const { mutate, isPending } = useCreateEntity();

  const handleCreate = () => {
    mutate({});
  };

  return <button onClick={handleCreate}>Create Entity</button>;
}

useTransferEntityControl

Transfers control of an entity to a new controller.

import { useTransferEntityControl } from "@mycelium-ip/react";

function TransferControl({ entityPubkey }: { entityPubkey: PublicKey }) {
  const { mutate } = useTransferEntityControl();

  const handleTransfer = () => {
    mutate({
      entity: entityPubkey,
      newController: newControllerPubkey,
    });
  };

  return <button onClick={handleTransfer}>Transfer Control</button>;
}

IP Hooks

useCreateIp

Creates a new IP (Intellectual Property) asset.

import { useCreateIp } from "@mycelium-ip/react";

function CreateIp({ entityPubkey }: { entityPubkey: PublicKey }) {
  const { mutate, isPending } = useCreateIp();

  const handleCreate = () => {
    mutate({
      registrantEntity: entityPubkey,
      content: new TextEncoder().encode("ipfs://QmXxx..."), // Content identifier
      treasuryTokenAccount: treasuryAccount, // Protocol treasury token account
      payerTokenAccount: payerAccount, // Payer's token account for fees
    });
  };

  return <button onClick={handleCreate}>Register IP</button>;
}

useTransferIp

Transfers ownership of an IP to another entity.

import { useTransferIp } from "@mycelium-ip/react";

function TransferIp({ ipPubkey, currentOwner, newOwner }: Props) {
  const { mutate } = useTransferIp();

  const handleTransfer = () => {
    mutate({
      ip: ipPubkey,
      currentOwnerEntity: currentOwner,
      newOwnerEntity: newOwner,
      controllerSigners: [controller1], // required when entity has multiple controllers
    });
  };

  return <button onClick={handleTransfer}>Transfer IP</button>;
}

License Hooks

useCreateLicense

Creates a license for an IP.

import { useCreateLicense } from "@mycelium-ip/react";

function CreateLicense({ ipPubkey, entityPubkey }: Props) {
  const { mutate } = useCreateLicense();

  const handleCreate = () => {
    mutate({
      originIp: ipPubkey,
      ownerEntity: entityPubkey,
      derivativesAllowed: true, // Whether derivatives can be created under this license
    });
  };

  return <button onClick={handleCreate}>Create License</button>;
}

useUpdateLicense

Updates an existing license.

import { useUpdateLicense } from "@mycelium-ip/react";

function UpdateLicense({ ipPubkey, entityPubkey }: Props) {
  const { mutate } = useUpdateLicense();

  const handleUpdate = () => {
    mutate({
      originIp: ipPubkey,
      authorityEntity: entityPubkey,
      derivativesAllowed: false,
    });
  };

  return <button onClick={handleUpdate}>Update License</button>;
}

useRevokeLicense

Revokes a license.

import { useRevokeLicense } from "@mycelium-ip/react";

function RevokeLicense({ ipPubkey, entityPubkey }: Props) {
  const { mutate } = useRevokeLicense();

  const handleRevoke = () => {
    mutate({
      originIp: ipPubkey,
      authorityEntity: entityPubkey,
    });
  };

  return <button onClick={handleRevoke}>Revoke License</button>;
}

Grant Hooks

useCreateLicenseGrant

Creates a license grant for another entity.

import { useCreateLicenseGrant } from "@mycelium-ip/react";

function CreateGrant({ ipPubkey, authorityEntity, granteeEntity }: Props) {
  const { mutate } = useCreateLicenseGrant();

  const handleGrant = () => {
    mutate({
      originIp: ipPubkey,
      authorityEntity: authorityEntity,
      granteeEntity: granteeEntity,
      expiration: Math.floor(Date.now() / 1000) + 86400 * 365, // 1 year from now
    });
  };

  return <button onClick={handleGrant}>Grant License</button>;
}

useRevokeLicenseGrant

Revokes a license grant.

import { useRevokeLicenseGrant } from "@mycelium-ip/react";

function RevokeGrant({ ipPubkey, authorityEntity, granteeEntity }: Props) {
  const { mutate } = useRevokeLicenseGrant();

  const handleRevoke = () => {
    mutate({
      originIp: ipPubkey,
      authorityEntity: authorityEntity,
      granteeEntity: granteeEntity,
    });
  };

  return <button onClick={handleRevoke}>Revoke Grant</button>;
}

Derivative Hooks

useCreateDerivativeLink

Creates a derivative link between two IPs.

import { useCreateDerivativeLink } from "@mycelium-ip/react";

function CreateDerivative({ parentIp, childIp, childOwner }: Props) {
  const { mutate } = useCreateDerivativeLink();

  const handleCreate = () => {
    mutate({
      parentIp: parentIp,
      childIp: childIp,
      childOwnerEntity: childOwner,
      licenseGrant: grantPubkey,
      license: licensePubkey,
    });
  };

  return <button onClick={handleCreate}>Link Derivative</button>;
}

useUpdateDerivativeLicense

Updates the license on a derivative link.

import { useUpdateDerivativeLicense } from "@mycelium-ip/react";

function UpdateDerivativeLicense({ parentIp, childIp, childOwner }: Props) {
  const { mutate } = useUpdateDerivativeLicense();

  const handleUpdate = () => {
    mutate({
      parentIp: parentIp,
      childIp: childIp,
      childOwnerEntity: childOwner,
      newLicenseGrant: newGrantPubkey,
      newLicense: newLicensePubkey,
    });
  };

  return <button onClick={handleUpdate}>Update Derivative License</button>;
}

Metadata Hooks

useCreateMetadataSchema

Creates a new metadata schema.

import { useCreateMetadataSchema } from "@mycelium-ip/react";

function CreateSchema() {
  const { mutate } = useCreateMetadataSchema();

  const handleCreate = () => {
    mutate({
      id: new TextEncoder().encode("artwork-schema"),
      version: 1,
      data: new TextEncoder().encode(
        JSON.stringify({
          type: "object",
          properties: {
            title: { type: "string" },
            artist: { type: "string" },
          },
        }),
      ),
      cid: new TextEncoder().encode("ipfs://QmSchema..."),
    });
  };

  return <button onClick={handleCreate}>Create Schema</button>;
}

useCreateEntityMetadata

Creates metadata for an entity.

import { useCreateEntityMetadata } from "@mycelium-ip/react";

function CreateEntityMetadata({ entityPubkey, schemaPubkey }: Props) {
  const { mutate } = useCreateEntityMetadata();

  const handleCreate = () => {
    mutate({
      entity: entityPubkey,
      schema: schemaPubkey,
      revision: 1,
      data: new TextEncoder().encode(JSON.stringify({ name: "My Org" })),
      cid: new TextEncoder().encode("ipfs://QmMetadata..."),
    });
  };

  return <button onClick={handleCreate}>Add Metadata</button>;
}

useCreateIpMetadata

Creates metadata for an IP.

import { useCreateIpMetadata } from "@mycelium-ip/react";

function CreateIpMetadata({ ipPubkey, entityPubkey, schemaPubkey }: Props) {
  const { mutate } = useCreateIpMetadata();

  const handleCreate = () => {
    mutate({
      ip: ipPubkey,
      ownerEntity: entityPubkey,
      schema: schemaPubkey,
      revision: 1,
      data: new TextEncoder().encode(
        JSON.stringify({
          title: "My Artwork",
          artist: "Anonymous",
        }),
      ),
      cid: new TextEncoder().encode("ipfs://QmIpMetadata..."),
    });
  };

  return <button onClick={handleCreate}>Add IP Metadata</button>;
}

Composite Hooks

These hooks bundle multiple on-chain instructions into a single atomic transaction, reducing network round-trips and guaranteeing that both operations succeed or fail together.

useCreateEntityWithMetadata

Creates a new entity and attaches metadata in one transaction. The entity PDA is derived automatically from the caller's wallet public key and the provided handle.

import { useCreateEntityWithMetadata } from "@mycelium-ip/react";

function CreateEntityWithMetadata({
  schemaPubkey,
}: {
  schemaPubkey: PublicKey;
}) {
  const { mutate, isPending, isSuccess, data } = useCreateEntityWithMetadata();

  const handleCreate = () => {
    mutate({
      entity: {
        handle: "my-organization",
        additionalControllers: [],
        signatureThreshold: 1,
      },
      metadata: {
        schema: schemaPubkey,
        revision: 1n,
        data: new TextEncoder().encode(JSON.stringify({ name: "My Org" })),
        cid: "ipfs://QmMetadata...",
      },
    });
  };

  return (
    <div>
      <button onClick={handleCreate} disabled={isPending}>
        {isPending ? "Creating..." : "Create Entity with Metadata"}
      </button>
      {isSuccess && <p>Created entity! Signature: {data.signature}</p>}
    </div>
  );
}

The mutation resolves with a CreateEntityWithMetadataResult:

interface CreateEntityWithMetadataResult {
  signature: string; // Transaction signature
  entityCreated?: EntityCreated; // Decoded EntityCreated event
  entityMetadataCreated?: EntityMetadataCreated; // Decoded EntityMetadataCreated event
}

useCreateIpWithMetadata

Creates a new IP asset and attaches metadata in one transaction. The IP PDA is derived automatically from registrantEntity and the content hash. The ownerEntity for the metadata instruction is also inferred from the IP params.

import { useCreateIpWithMetadata } from "@mycelium-ip/react";

function CreateIpWithMetadata({
  entityPubkey,
  schemaPubkey,
  treasuryAccount,
  payerAccount,
}: Props) {
  const { mutate, isPending, isSuccess, data } = useCreateIpWithMetadata();

  const handleCreate = () => {
    mutate({
      ip: {
        registrantEntity: entityPubkey,
        content: new TextEncoder().encode("ipfs://QmXxx..."),
        treasuryTokenAccount: treasuryAccount,
        payerTokenAccount: payerAccount,
      },
      metadata: {
        schema: schemaPubkey,
        revision: 1n,
        data: new TextEncoder().encode(
          JSON.stringify({ title: "My Artwork", artist: "Anonymous" }),
        ),
        cid: "ipfs://QmIpMetadata...",
      },
    });
  };

  return (
    <div>
      <button onClick={handleCreate} disabled={isPending}>
        {isPending ? "Registering..." : "Register IP with Metadata"}
      </button>
      {isSuccess && <p>Registered IP! Signature: {data.signature}</p>}
    </div>
  );
}

The mutation resolves with a CreateIpWithMetadataResult:

interface CreateIpWithMetadataResult {
  signature: string; // Transaction signature
  ipCreated?: IpCreated; // Decoded IpCreated event
  ipMetadataCreated?: IpMetadataCreated; // Decoded IpMetadataCreated event
}

Accessor Hooks

These hooks provide access to the underlying SDK and context values.

import {
  useMyceliumClient,
  useMyceliumConnection,
  useMyceliumWallet,
  useMyceliumContext,
} from "@mycelium-ip/react";

function MyComponent() {
  // Get the core SDK client for direct access
  const client = useMyceliumClient();

  // Get the Solana connection
  const connection = useMyceliumConnection();

  // Get the wallet (StandardWalletWrapper | null)
  const wallet = useMyceliumWallet();

  // Get everything at once
  const { client, connection, wallet, confirmation } = useMyceliumContext();
}

Multi-sig / Controller Signers

Entities with multiple controllers require additional signers to meet the signature threshold. All mutation hooks accept an optional controllerSigners field — an array of PublicKeys for the entity controllers that must co-sign the transaction.

The controllerSigners field is available on:

  • useUpdateEntityControllers
  • useCreateIp, useTransferIp
  • useCreateEntityMetadata, useCreateIpMetadata
  • useCreateDerivativeLink, useUpdateDerivativeLicense
  • useCreateLicense, useUpdateLicense, useRevokeLicense
  • useCreateLicenseGrant, useRevokeLicenseGrant
  • useCreateEntityWithMetadata (both entity and metadata params)
  • useCreateIpWithMetadata (both ip and metadata params)
const { mutate } = useCreateIp();

mutate({
  registrantEntity: entityPubkey,
  contentHash: hash,
  controllerSigners: [controller1, controller2], // additional signers
});

For composite hooks, pass controllerSigners in each sub-params object:

const { mutate } = useCreateEntityWithMetadata();

mutate({
  entity: {
    handle: "my-org",
    controllerSigners: [controller1],
  },
  metadata: {
    schema: schemaPubkey,
    dataHash: hash,
    cid: "ipfs://Qm...",
    controllerSigners: [controller1],
  },
});

Transaction Results

All mutation hooks return a TransactionResult on success:

interface TransactionResult {
  signature: string; // The transaction signature
}

Handling Results

function CreateEntityWithFeedback() {
  const { mutate, isPending, isSuccess, isError, data, error } =
    useCreateEntity();

  const handleCreate = () => {
    mutate(
      {
        handle: new TextEncoder().encode("my-entity"),
        additionalControllers: [],
        signatureThreshold: 1,
      },
      {
        onSuccess: (result) => {
          console.log("Transaction signature:", result.signature);
          // Show success toast, redirect, etc.
        },
        onError: (error) => {
          console.error("Transaction failed:", error.message);
          // Show error toast
        },
      },
    );
  };

  return (
    <div>
      <button onClick={handleCreate} disabled={isPending}>
        {isPending ? "Creating..." : "Create Entity"}
      </button>
      {isSuccess && <p>Success! Signature: {data.signature}</p>}
      {isError && <p>Error: {error.message}</p>}
    </div>
  );
}

Async/Await Pattern

async function handleCreateEntity() {
  const { mutateAsync } = useCreateEntity();

  try {
    const result = await mutateAsync({
      handle: new TextEncoder().encode("my-entity"),
      additionalControllers: [],
      signatureThreshold: 1,
    });
    console.log("Created with signature:", result.signature);
  } catch (error) {
    console.error("Failed to create entity:", error);
  }
}

Query Keys

The SDK exports queryKeys for custom cache invalidation or building custom queries:

import { queryKeys, useMyceliumClient } from "@mycelium-ip/react";
import { useQuery, useQueryClient } from "@tanstack/react-query";

// Available query keys
queryKeys.all; // ["mycelium"]
queryKeys.entities(); // ["mycelium", "entities"]
queryKeys.entity(id); // ["mycelium", "entities", id]
queryKeys.ips(); // ["mycelium", "ips"]
queryKeys.ip(id); // ["mycelium", "ips", id]
queryKeys.licenses(); // ["mycelium", "licenses"]
queryKeys.license(id); // ["mycelium", "licenses", id]
queryKeys.grants(); // ["mycelium", "grants"]
queryKeys.grant(id); // ["mycelium", "grants", id]
queryKeys.metadata(); // ["mycelium", "metadata"]
queryKeys.derivatives(); // ["mycelium", "derivatives"]
queryKeys.derivative(id); // ["mycelium", "derivatives", id]

// Manual cache invalidation
function RefreshButton() {
  const queryClient = useQueryClient();

  return (
    <button
      onClick={() =>
        queryClient.invalidateQueries({ queryKey: queryKeys.ips() })
      }
    >
      Refresh IPs
    </button>
  );
}

Advanced Usage

Custom QueryClient

Provide your own QueryClient for custom caching behavior:

import { QueryClient } from "@tanstack/react-query";
import { MyceliumIpProvider } from "@mycelium-ip/react";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 10, // 10 minutes
      gcTime: 1000 * 60 * 60, // 1 hour
      retry: 3,
      retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
    },
    mutations: {
      retry: 1,
    },
  },
});

function App() {
  return (
    <MyceliumIpProvider
      connection={connection}
      wallet={wallet}
      queryClient={queryClient}
    >
      <YourApp />
    </MyceliumIpProvider>
  );
}

TypeScript Types

All parameter types are re-exported from @mycelium-ip/core-sdk:

import type {
  CreateEntityParams,
  CreateIpParams,
  CreateLicenseParams,
  CreateLicenseGrantParams,
  // ... etc
} from "@mycelium-ip/core-sdk";

Next.js App Router

The provider is already marked with "use client". In Next.js App Router, create a client component for the provider:

// app/providers.tsx
"use client";

import { MyceliumIpProvider } from "@mycelium-ip/react";
// ... wallet setup

export function Providers({ children }: { children: React.ReactNode }) {
  // ... setup logic
  return (
    <MyceliumIpProvider connection={connection} wallet={wallet}>
      {children}
    </MyceliumIpProvider>
  );
}
// app/layout.tsx
import { Providers } from "./providers";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

API Reference

Exports

// Provider
export { MyceliumIpProvider } from "@mycelium-ip/react";
export type {
  MyceliumIpProviderProps,
  MyceliumIpProviderOptions,
  MyceliumContextValue,
} from "@mycelium-ip/react";

// Wallet types (re-exported from @mycelium-ip/core-sdk)
export type {
  StandardWalletWrapper,
  UnsupportedFeatureError,
} from "@mycelium-ip/react";

// Transaction utilities
export {
  clusterToChain,
  executeTransaction,
  executeTransactionWithInstructions,
} from "@mycelium-ip/react";
export type { TransactionResult } from "@mycelium-ip/react";

// Query keys
export { queryKeys } from "@mycelium-ip/react";

// Accessor hooks
export {
  useMyceliumClient,
  useMyceliumConnection,
  useMyceliumWallet,
  useMyceliumContext,
} from "@mycelium-ip/react";

// Entity hooks
export {
  useCreateEntity,
  useUpdateEntityControllers,
  useCreateEntityWithMetadata,
} from "@mycelium-ip/react";
export type {
  CreateEntityWithMetadataParams,
  CreateEntityWithMetadataResult,
} from "@mycelium-ip/react";

// IP hooks
export {
  useCreateIp,
  useTransferIp,
  useCreateIpWithMetadata,
} from "@mycelium-ip/react";
export type {
  CreateIpWithMetadataParams,
  CreateIpWithMetadataResult,
} from "@mycelium-ip/react";

// License hooks
export {
  useCreateLicense,
  useUpdateLicense,
  useRevokeLicense,
} from "@mycelium-ip/react";

// Grant hooks
export {
  useCreateLicenseGrant,
  useRevokeLicenseGrant,
} from "@mycelium-ip/react";

// Derivative hooks
export {
  useCreateDerivativeLink,
  useUpdateDerivativeLicense,
} from "@mycelium-ip/react";

// Metadata hooks
export {
  useCreateMetadataSchema,
  useCreateEntityMetadata,
  useCreateIpMetadata,
} from "@mycelium-ip/react";

License

MIT