@mycelium-ip/react 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/README.md +952 -0
  2. package/dist/hooks/derivative/useCreateDerivativeLink.d.ts +30 -0
  3. package/dist/hooks/derivative/useCreateDerivativeLink.d.ts.map +1 -0
  4. package/dist/hooks/derivative/useCreateDerivativeLink.js +45 -0
  5. package/dist/hooks/derivative/useUpdateDerivativeLicense.d.ts +30 -0
  6. package/dist/hooks/derivative/useUpdateDerivativeLicense.d.ts.map +1 -0
  7. package/dist/hooks/derivative/useUpdateDerivativeLicense.js +44 -0
  8. package/dist/hooks/entity/useCreateEntity.d.ts +28 -0
  9. package/dist/hooks/entity/useCreateEntity.d.ts.map +1 -0
  10. package/dist/hooks/entity/useCreateEntity.js +42 -0
  11. package/dist/hooks/entity/useUpdateEntityControllers.d.ts +28 -0
  12. package/dist/hooks/entity/useUpdateEntityControllers.d.ts.map +1 -0
  13. package/dist/hooks/entity/useUpdateEntityControllers.js +42 -0
  14. package/dist/hooks/grant/useCreateLicenseGrant.d.ts +29 -0
  15. package/dist/hooks/grant/useCreateLicenseGrant.d.ts.map +1 -0
  16. package/dist/hooks/grant/useCreateLicenseGrant.js +43 -0
  17. package/dist/hooks/grant/useRevokeLicenseGrant.d.ts +28 -0
  18. package/dist/hooks/grant/useRevokeLicenseGrant.d.ts.map +1 -0
  19. package/dist/hooks/grant/useRevokeLicenseGrant.js +42 -0
  20. package/dist/hooks/internal/useMyceliumClient.d.ts +15 -0
  21. package/dist/hooks/internal/useMyceliumClient.d.ts.map +1 -0
  22. package/dist/hooks/internal/useMyceliumClient.js +18 -0
  23. package/dist/hooks/internal/useMyceliumConnection.d.ts +14 -0
  24. package/dist/hooks/internal/useMyceliumConnection.d.ts.map +1 -0
  25. package/dist/hooks/internal/useMyceliumConnection.js +17 -0
  26. package/dist/hooks/internal/useMyceliumContext.d.ts +14 -0
  27. package/dist/hooks/internal/useMyceliumContext.d.ts.map +1 -0
  28. package/dist/hooks/internal/useMyceliumContext.js +22 -0
  29. package/dist/hooks/internal/useMyceliumWallet.d.ts +14 -0
  30. package/dist/hooks/internal/useMyceliumWallet.d.ts.map +1 -0
  31. package/dist/hooks/internal/useMyceliumWallet.js +17 -0
  32. package/dist/hooks/ip/useCreateIp.d.ts +29 -0
  33. package/dist/hooks/ip/useCreateIp.d.ts.map +1 -0
  34. package/dist/hooks/ip/useCreateIp.js +43 -0
  35. package/dist/hooks/ip/useTransferIp.d.ts +28 -0
  36. package/dist/hooks/ip/useTransferIp.d.ts.map +1 -0
  37. package/dist/hooks/ip/useTransferIp.js +42 -0
  38. package/dist/hooks/license/useCreateLicense.d.ts +28 -0
  39. package/dist/hooks/license/useCreateLicense.d.ts.map +1 -0
  40. package/dist/hooks/license/useCreateLicense.js +42 -0
  41. package/dist/hooks/license/useRevokeLicense.d.ts +27 -0
  42. package/dist/hooks/license/useRevokeLicense.d.ts.map +1 -0
  43. package/dist/hooks/license/useRevokeLicense.js +41 -0
  44. package/dist/hooks/license/useUpdateLicense.d.ts +28 -0
  45. package/dist/hooks/license/useUpdateLicense.d.ts.map +1 -0
  46. package/dist/hooks/license/useUpdateLicense.js +42 -0
  47. package/dist/hooks/metadata/useCreateEntityMetadata.d.ts +30 -0
  48. package/dist/hooks/metadata/useCreateEntityMetadata.d.ts.map +1 -0
  49. package/dist/hooks/metadata/useCreateEntityMetadata.js +45 -0
  50. package/dist/hooks/metadata/useCreateIpMetadata.d.ts +31 -0
  51. package/dist/hooks/metadata/useCreateIpMetadata.d.ts.map +1 -0
  52. package/dist/hooks/metadata/useCreateIpMetadata.js +46 -0
  53. package/dist/hooks/metadata/useCreateMetadataSchema.d.ts +29 -0
  54. package/dist/hooks/metadata/useCreateMetadataSchema.d.ts.map +1 -0
  55. package/dist/hooks/metadata/useCreateMetadataSchema.js +43 -0
  56. package/dist/hooks/queries/queryKeys.d.ts +45 -0
  57. package/dist/hooks/queries/queryKeys.d.ts.map +1 -0
  58. package/dist/hooks/queries/queryKeys.js +44 -0
  59. package/dist/index.d.ts +24 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +33 -0
  62. package/dist/provider/MyceliumIpProvider.d.ts +56 -0
  63. package/dist/provider/MyceliumIpProvider.d.ts.map +1 -0
  64. package/dist/provider/MyceliumIpProvider.js +76 -0
  65. package/dist/provider/context.d.ts +22 -0
  66. package/dist/provider/context.d.ts.map +1 -0
  67. package/dist/provider/context.js +6 -0
  68. package/dist/types/index.d.ts +2 -0
  69. package/dist/types/index.d.ts.map +1 -0
  70. package/dist/types/index.js +1 -0
  71. package/dist/types/wallet.d.ts +54 -0
  72. package/dist/types/wallet.d.ts.map +1 -0
  73. package/dist/types/wallet.js +1 -0
  74. package/dist/utils/transaction.d.ts +40 -0
  75. package/dist/utils/transaction.d.ts.map +1 -0
  76. package/dist/utils/transaction.js +82 -0
  77. package/package.json +59 -0
package/README.md ADDED
@@ -0,0 +1,952 @@
1
+ # @mycelium-ip/react
2
+
3
+ React hooks for the Mycelium IP protocol. This package provides a wallet-agnostic React integration built on top of TanStack Query.
4
+
5
+ ## Features
6
+
7
+ - **Wallet-agnostic** — Works with any wallet (Solana Wallet Adapter, Privy, embedded wallets)
8
+ - **TanStack Query powered** — Built-in caching, loading states, and automatic cache invalidation
9
+ - **Next.js compatible** — Works with client components out of the box
10
+ - **TypeScript first** — Full type safety with comprehensive TypeScript definitions
11
+ - **Minimal abstraction** — Thin wrapper over `@mycelium-ip/core-sdk`
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ # npm
17
+ npm install @mycelium-ip/react @mycelium-ip/core-sdk
18
+
19
+ # yarn
20
+ yarn add @mycelium-ip/react @mycelium-ip/core-sdk
21
+
22
+ # pnpm
23
+ pnpm add @mycelium-ip/react @mycelium-ip/core-sdk
24
+ ```
25
+
26
+ ### Peer Dependencies
27
+
28
+ Make sure you have the following peer dependencies installed:
29
+
30
+ ```bash
31
+ npm install @solana/web3.js @tanstack/react-query react
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```tsx
37
+ import { Connection } from "@solana/web3.js";
38
+ import { MyceliumIpProvider, useCreateEntity } from "@mycelium-ip/react";
39
+
40
+ // 1. Wrap your app with the provider
41
+ function App() {
42
+ const connection = new Connection("https://api.devnet.solana.com");
43
+ const wallet = useYourWalletAdapter(); // See Wallet Integration section
44
+
45
+ return (
46
+ <MyceliumIpProvider connection={connection} wallet={wallet}>
47
+ <CreateEntityButton />
48
+ </MyceliumIpProvider>
49
+ );
50
+ }
51
+
52
+ // 2. Use hooks in your components
53
+ function CreateEntityButton() {
54
+ const { mutate, isPending, isSuccess } = useCreateEntity();
55
+
56
+ const handleCreate = () => {
57
+ mutate({
58
+ handle: new TextEncoder().encode("my-entity"),
59
+ additionalControllers: [],
60
+ signatureThreshold: 1,
61
+ });
62
+ };
63
+
64
+ return (
65
+ <button onClick={handleCreate} disabled={isPending}>
66
+ {isPending ? "Creating..." : "Create Entity"}
67
+ </button>
68
+ );
69
+ }
70
+ ```
71
+
72
+ ## Wallet Integration
73
+
74
+ The SDK is completely wallet-agnostic. You provide a wallet object implementing the `MyceliumWallet` interface, allowing integration with any wallet provider.
75
+
76
+ ### MyceliumWallet Interface
77
+
78
+ ```typescript
79
+ interface MyceliumWallet {
80
+ /** The public key of the connected wallet (null if not connected) */
81
+ publicKey: PublicKey | null;
82
+
83
+ /** Signs a single transaction */
84
+ signTransaction<T extends Transaction | VersionedTransaction>(
85
+ transaction: T,
86
+ ): Promise<T>;
87
+
88
+ /** Signs multiple transactions (optional) */
89
+ signAllTransactions?<T extends Transaction | VersionedTransaction>(
90
+ transactions: T[],
91
+ ): Promise<T[]>;
92
+
93
+ /** Signs an arbitrary message (optional) */
94
+ signMessage?(message: Uint8Array): Promise<Uint8Array>;
95
+ }
96
+ ```
97
+
98
+ ### Solana Wallet Adapter
99
+
100
+ If you're using [`@solana/wallet-adapter-react`](https://github.com/solana-labs/wallet-adapter):
101
+
102
+ ```tsx
103
+ "use client";
104
+
105
+ import { useWallet } from "@solana/wallet-adapter-react";
106
+ import { useConnection } from "@solana/wallet-adapter-react";
107
+ import { MyceliumIpProvider, type MyceliumWallet } from "@mycelium-ip/react";
108
+ import { useMemo } from "react";
109
+
110
+ function MyceliumProvider({ children }: { children: React.ReactNode }) {
111
+ const { connection } = useConnection();
112
+ const { publicKey, signTransaction, signAllTransactions, signMessage } =
113
+ useWallet();
114
+
115
+ // Adapt Wallet Adapter to MyceliumWallet interface
116
+ const wallet = useMemo<MyceliumWallet>(
117
+ () => ({
118
+ publicKey,
119
+ signTransaction: signTransaction!,
120
+ signAllTransactions,
121
+ signMessage,
122
+ }),
123
+ [publicKey, signTransaction, signAllTransactions, signMessage],
124
+ );
125
+
126
+ // Don't render provider until wallet is connected
127
+ if (!publicKey || !signTransaction) {
128
+ return <>{children}</>;
129
+ }
130
+
131
+ return (
132
+ <MyceliumIpProvider connection={connection} wallet={wallet}>
133
+ {children}
134
+ </MyceliumIpProvider>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ### Privy Embedded Wallet
140
+
141
+ If you're using [`@privy-io/react-auth`](https://docs.privy.io/) with Solana embedded wallets:
142
+
143
+ ```tsx
144
+ "use client";
145
+
146
+ import { useSolanaWallets } from "@privy-io/react-auth/solana";
147
+ import { usePrivy } from "@privy-io/react-auth";
148
+ import { Connection, PublicKey } from "@solana/web3.js";
149
+ import { MyceliumIpProvider, type MyceliumWallet } from "@mycelium-ip/react";
150
+ import { useMemo } from "react";
151
+
152
+ function MyceliumPrivyProvider({ children }: { children: React.ReactNode }) {
153
+ const { ready, authenticated } = usePrivy();
154
+ const { wallets } = useSolanaWallets();
155
+
156
+ // Get the embedded wallet (or first available wallet)
157
+ const embeddedWallet =
158
+ wallets.find((w) => w.walletClientType === "privy") ?? wallets[0];
159
+
160
+ const connection = useMemo(
161
+ () => new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!),
162
+ [],
163
+ );
164
+
165
+ // Adapt Privy wallet to MyceliumWallet interface
166
+ const wallet = useMemo<MyceliumWallet | null>(() => {
167
+ if (!embeddedWallet?.address) return null;
168
+
169
+ return {
170
+ publicKey: new PublicKey(embeddedWallet.address),
171
+ signTransaction: async (transaction) => {
172
+ return embeddedWallet.signTransaction(transaction);
173
+ },
174
+ signAllTransactions: async (transactions) => {
175
+ // Privy supports batch signing
176
+ const signed = [];
177
+ for (const tx of transactions) {
178
+ signed.push(await embeddedWallet.signTransaction(tx));
179
+ }
180
+ return signed;
181
+ },
182
+ signMessage: async (message) => {
183
+ const signature = await embeddedWallet.signMessage(message);
184
+ return signature;
185
+ },
186
+ };
187
+ }, [embeddedWallet]);
188
+
189
+ // Wait for Privy to be ready and user to be authenticated
190
+ if (!ready || !authenticated || !wallet) {
191
+ return <>{children}</>;
192
+ }
193
+
194
+ return (
195
+ <MyceliumIpProvider connection={connection} wallet={wallet}>
196
+ {children}
197
+ </MyceliumIpProvider>
198
+ );
199
+ }
200
+
201
+ export default MyceliumPrivyProvider;
202
+ ```
203
+
204
+ #### Complete Privy Setup Example
205
+
206
+ Here's a complete example showing how to set up Privy with Mycelium in a Next.js App Router application:
207
+
208
+ ```tsx
209
+ // app/providers.tsx
210
+ "use client";
211
+
212
+ import { PrivyProvider } from "@privy-io/react-auth";
213
+ import { toSolanaWalletConnectors } from "@privy-io/react-auth/solana";
214
+ import MyceliumPrivyProvider from "./MyceliumPrivyProvider";
215
+
216
+ const solanaConnectors = toSolanaWalletConnectors({
217
+ shouldAutoConnect: true,
218
+ });
219
+
220
+ export function Providers({ children }: { children: React.ReactNode }) {
221
+ return (
222
+ <PrivyProvider
223
+ appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!}
224
+ config={{
225
+ appearance: {
226
+ theme: "dark",
227
+ },
228
+ embeddedWallets: {
229
+ solana: {
230
+ createOnLogin: "users-without-wallets",
231
+ },
232
+ },
233
+ externalWallets: {
234
+ solana: {
235
+ connectors: solanaConnectors,
236
+ },
237
+ },
238
+ }}
239
+ >
240
+ <MyceliumPrivyProvider>{children}</MyceliumPrivyProvider>
241
+ </PrivyProvider>
242
+ );
243
+ }
244
+ ```
245
+
246
+ ```tsx
247
+ // app/layout.tsx
248
+ import { Providers } from "./providers";
249
+
250
+ export default function RootLayout({
251
+ children,
252
+ }: {
253
+ children: React.ReactNode;
254
+ }) {
255
+ return (
256
+ <html lang="en">
257
+ <body>
258
+ <Providers>{children}</Providers>
259
+ </body>
260
+ </html>
261
+ );
262
+ }
263
+ ```
264
+
265
+ ## Provider
266
+
267
+ ### MyceliumIpProvider
268
+
269
+ The provider initializes the SDK and must wrap your application (or the part that uses Mycelium hooks).
270
+
271
+ ```tsx
272
+ <MyceliumIpProvider
273
+ connection={connection}
274
+ wallet={wallet}
275
+ queryClient={queryClient} // optional
276
+ options={{
277
+ confirmation: "confirmed",
278
+ devtools: true,
279
+ }}
280
+ >
281
+ <App />
282
+ </MyceliumIpProvider>
283
+ ```
284
+
285
+ ### Props
286
+
287
+ | Prop | Type | Required | Description |
288
+ | ------------- | --------------------------- | -------- | ----------------------------------------------------------------------- |
289
+ | `connection` | `Connection` | Yes | Solana RPC connection |
290
+ | `wallet` | `MyceliumWallet` | Yes | Wallet implementing the MyceliumWallet interface |
291
+ | `queryClient` | `QueryClient` | No | Existing TanStack Query client. If omitted, a default client is created |
292
+ | `options` | `MyceliumIpProviderOptions` | No | Provider configuration options |
293
+
294
+ ### Options
295
+
296
+ | Option | Type | Default | Description |
297
+ | -------------- | ------------ | ------------- | ---------------------------------------------------------------------------- |
298
+ | `confirmation` | `Commitment` | `"confirmed"` | Transaction confirmation level (`"processed"`, `"confirmed"`, `"finalized"`) |
299
+ | `devtools` | `boolean` | `false` | Enable TanStack Query devtools |
300
+
301
+ ## Hooks Reference
302
+
303
+ All mutation hooks return TanStack Query's `UseMutationResult`, providing:
304
+
305
+ - `mutate` / `mutateAsync` — Execute the mutation
306
+ - `isPending` — Loading state
307
+ - `isSuccess` / `isError` — Result states
308
+ - `data` — Transaction result (signature)
309
+ - `error` — Error object if failed
310
+ - `reset` — Reset mutation state
311
+
312
+ ### Entity Hooks
313
+
314
+ #### useCreateEntity
315
+
316
+ Creates a new entity in the protocol.
317
+
318
+ ```tsx
319
+ import { useCreateEntity } from "@mycelium-ip/react";
320
+
321
+ function CreateEntity() {
322
+ const { mutate, isPending } = useCreateEntity();
323
+
324
+ const handleCreate = () => {
325
+ mutate({
326
+ handle: new TextEncoder().encode("my-organization"),
327
+ additionalControllers: [], // Additional PublicKeys that can control this entity
328
+ signatureThreshold: 1, // Required signatures for multi-sig
329
+ });
330
+ };
331
+
332
+ return <button onClick={handleCreate}>Create Entity</button>;
333
+ }
334
+ ```
335
+
336
+ #### useUpdateEntityControllers
337
+
338
+ Updates the controllers and signature threshold of an entity.
339
+
340
+ ```tsx
341
+ import { useUpdateEntityControllers } from "@mycelium-ip/react";
342
+
343
+ function UpdateControllers({ entityPubkey }: { entityPubkey: PublicKey }) {
344
+ const { mutate } = useUpdateEntityControllers();
345
+
346
+ const handleUpdate = () => {
347
+ mutate({
348
+ entity: entityPubkey,
349
+ newControllers: [controller1, controller2],
350
+ newThreshold: 2,
351
+ });
352
+ };
353
+
354
+ return <button onClick={handleUpdate}>Update Controllers</button>;
355
+ }
356
+ ```
357
+
358
+ ### IP Hooks
359
+
360
+ #### useCreateIp
361
+
362
+ Creates a new IP (Intellectual Property) asset.
363
+
364
+ ```tsx
365
+ import { useCreateIp } from "@mycelium-ip/react";
366
+
367
+ function CreateIp({ entityPubkey }: { entityPubkey: PublicKey }) {
368
+ const { mutate, isPending } = useCreateIp();
369
+
370
+ const handleCreate = () => {
371
+ mutate({
372
+ registrantEntity: entityPubkey,
373
+ content: new TextEncoder().encode("ipfs://QmXxx..."), // Content identifier
374
+ treasuryTokenAccount: treasuryAccount, // Protocol treasury token account
375
+ payerTokenAccount: payerAccount, // Payer's token account for fees
376
+ });
377
+ };
378
+
379
+ return <button onClick={handleCreate}>Register IP</button>;
380
+ }
381
+ ```
382
+
383
+ #### useTransferIp
384
+
385
+ Transfers ownership of an IP to another entity.
386
+
387
+ ```tsx
388
+ import { useTransferIp } from "@mycelium-ip/react";
389
+
390
+ function TransferIp({ ipPubkey, currentOwner, newOwner }: Props) {
391
+ const { mutate } = useTransferIp();
392
+
393
+ const handleTransfer = () => {
394
+ mutate({
395
+ ip: ipPubkey,
396
+ currentOwnerEntity: currentOwner,
397
+ newOwnerEntity: newOwner,
398
+ });
399
+ };
400
+
401
+ return <button onClick={handleTransfer}>Transfer IP</button>;
402
+ }
403
+ ```
404
+
405
+ ### License Hooks
406
+
407
+ #### useCreateLicense
408
+
409
+ Creates a license for an IP.
410
+
411
+ ```tsx
412
+ import { useCreateLicense } from "@mycelium-ip/react";
413
+
414
+ function CreateLicense({ ipPubkey, entityPubkey }: Props) {
415
+ const { mutate } = useCreateLicense();
416
+
417
+ const handleCreate = () => {
418
+ mutate({
419
+ originIp: ipPubkey,
420
+ ownerEntity: entityPubkey,
421
+ derivativesAllowed: true, // Whether derivatives can be created under this license
422
+ });
423
+ };
424
+
425
+ return <button onClick={handleCreate}>Create License</button>;
426
+ }
427
+ ```
428
+
429
+ #### useUpdateLicense
430
+
431
+ Updates an existing license.
432
+
433
+ ```tsx
434
+ import { useUpdateLicense } from "@mycelium-ip/react";
435
+
436
+ function UpdateLicense({ ipPubkey, entityPubkey }: Props) {
437
+ const { mutate } = useUpdateLicense();
438
+
439
+ const handleUpdate = () => {
440
+ mutate({
441
+ originIp: ipPubkey,
442
+ authorityEntity: entityPubkey,
443
+ derivativesAllowed: false,
444
+ });
445
+ };
446
+
447
+ return <button onClick={handleUpdate}>Update License</button>;
448
+ }
449
+ ```
450
+
451
+ #### useRevokeLicense
452
+
453
+ Revokes a license.
454
+
455
+ ```tsx
456
+ import { useRevokeLicense } from "@mycelium-ip/react";
457
+
458
+ function RevokeLicense({ ipPubkey, entityPubkey }: Props) {
459
+ const { mutate } = useRevokeLicense();
460
+
461
+ const handleRevoke = () => {
462
+ mutate({
463
+ originIp: ipPubkey,
464
+ authorityEntity: entityPubkey,
465
+ });
466
+ };
467
+
468
+ return <button onClick={handleRevoke}>Revoke License</button>;
469
+ }
470
+ ```
471
+
472
+ ### Grant Hooks
473
+
474
+ #### useCreateLicenseGrant
475
+
476
+ Creates a license grant for another entity.
477
+
478
+ ```tsx
479
+ import { useCreateLicenseGrant } from "@mycelium-ip/react";
480
+
481
+ function CreateGrant({ ipPubkey, authorityEntity, granteeEntity }: Props) {
482
+ const { mutate } = useCreateLicenseGrant();
483
+
484
+ const handleGrant = () => {
485
+ mutate({
486
+ originIp: ipPubkey,
487
+ authorityEntity: authorityEntity,
488
+ granteeEntity: granteeEntity,
489
+ expiration: Math.floor(Date.now() / 1000) + 86400 * 365, // 1 year from now
490
+ });
491
+ };
492
+
493
+ return <button onClick={handleGrant}>Grant License</button>;
494
+ }
495
+ ```
496
+
497
+ #### useRevokeLicenseGrant
498
+
499
+ Revokes a license grant.
500
+
501
+ ```tsx
502
+ import { useRevokeLicenseGrant } from "@mycelium-ip/react";
503
+
504
+ function RevokeGrant({ ipPubkey, authorityEntity, granteeEntity }: Props) {
505
+ const { mutate } = useRevokeLicenseGrant();
506
+
507
+ const handleRevoke = () => {
508
+ mutate({
509
+ originIp: ipPubkey,
510
+ authorityEntity: authorityEntity,
511
+ granteeEntity: granteeEntity,
512
+ });
513
+ };
514
+
515
+ return <button onClick={handleRevoke}>Revoke Grant</button>;
516
+ }
517
+ ```
518
+
519
+ ### Derivative Hooks
520
+
521
+ #### useCreateDerivativeLink
522
+
523
+ Creates a derivative link between two IPs.
524
+
525
+ ```tsx
526
+ import { useCreateDerivativeLink } from "@mycelium-ip/react";
527
+
528
+ function CreateDerivative({ parentIp, childIp, childOwner }: Props) {
529
+ const { mutate } = useCreateDerivativeLink();
530
+
531
+ const handleCreate = () => {
532
+ mutate({
533
+ parentIp: parentIp,
534
+ childIp: childIp,
535
+ childOwnerEntity: childOwner,
536
+ licenseGrant: grantPubkey,
537
+ license: licensePubkey,
538
+ });
539
+ };
540
+
541
+ return <button onClick={handleCreate}>Link Derivative</button>;
542
+ }
543
+ ```
544
+
545
+ #### useUpdateDerivativeLicense
546
+
547
+ Updates the license on a derivative link.
548
+
549
+ ```tsx
550
+ import { useUpdateDerivativeLicense } from "@mycelium-ip/react";
551
+
552
+ function UpdateDerivativeLicense({ parentIp, childIp, childOwner }: Props) {
553
+ const { mutate } = useUpdateDerivativeLicense();
554
+
555
+ const handleUpdate = () => {
556
+ mutate({
557
+ parentIp: parentIp,
558
+ childIp: childIp,
559
+ childOwnerEntity: childOwner,
560
+ newLicenseGrant: newGrantPubkey,
561
+ newLicense: newLicensePubkey,
562
+ });
563
+ };
564
+
565
+ return <button onClick={handleUpdate}>Update Derivative License</button>;
566
+ }
567
+ ```
568
+
569
+ ### Metadata Hooks
570
+
571
+ #### useCreateMetadataSchema
572
+
573
+ Creates a new metadata schema.
574
+
575
+ ```tsx
576
+ import { useCreateMetadataSchema } from "@mycelium-ip/react";
577
+
578
+ function CreateSchema() {
579
+ const { mutate } = useCreateMetadataSchema();
580
+
581
+ const handleCreate = () => {
582
+ mutate({
583
+ id: new TextEncoder().encode("artwork-schema"),
584
+ version: 1,
585
+ data: new TextEncoder().encode(
586
+ JSON.stringify({
587
+ type: "object",
588
+ properties: {
589
+ title: { type: "string" },
590
+ artist: { type: "string" },
591
+ },
592
+ }),
593
+ ),
594
+ cid: new TextEncoder().encode("ipfs://QmSchema..."),
595
+ });
596
+ };
597
+
598
+ return <button onClick={handleCreate}>Create Schema</button>;
599
+ }
600
+ ```
601
+
602
+ #### useCreateEntityMetadata
603
+
604
+ Creates metadata for an entity.
605
+
606
+ ```tsx
607
+ import { useCreateEntityMetadata } from "@mycelium-ip/react";
608
+
609
+ function CreateEntityMetadata({ entityPubkey, schemaPubkey }: Props) {
610
+ const { mutate } = useCreateEntityMetadata();
611
+
612
+ const handleCreate = () => {
613
+ mutate({
614
+ entity: entityPubkey,
615
+ schema: schemaPubkey,
616
+ revision: 1,
617
+ data: new TextEncoder().encode(JSON.stringify({ name: "My Org" })),
618
+ cid: new TextEncoder().encode("ipfs://QmMetadata..."),
619
+ });
620
+ };
621
+
622
+ return <button onClick={handleCreate}>Add Metadata</button>;
623
+ }
624
+ ```
625
+
626
+ #### useCreateIpMetadata
627
+
628
+ Creates metadata for an IP.
629
+
630
+ ```tsx
631
+ import { useCreateIpMetadata } from "@mycelium-ip/react";
632
+
633
+ function CreateIpMetadata({ ipPubkey, entityPubkey, schemaPubkey }: Props) {
634
+ const { mutate } = useCreateIpMetadata();
635
+
636
+ const handleCreate = () => {
637
+ mutate({
638
+ ip: ipPubkey,
639
+ ownerEntity: entityPubkey,
640
+ schema: schemaPubkey,
641
+ revision: 1,
642
+ data: new TextEncoder().encode(
643
+ JSON.stringify({
644
+ title: "My Artwork",
645
+ artist: "Anonymous",
646
+ }),
647
+ ),
648
+ cid: new TextEncoder().encode("ipfs://QmIpMetadata..."),
649
+ });
650
+ };
651
+
652
+ return <button onClick={handleCreate}>Add IP Metadata</button>;
653
+ }
654
+ ```
655
+
656
+ ### Accessor Hooks
657
+
658
+ These hooks provide access to the underlying SDK and context values.
659
+
660
+ ```tsx
661
+ import {
662
+ useMyceliumClient,
663
+ useMyceliumConnection,
664
+ useMyceliumWallet,
665
+ useMyceliumContext,
666
+ } from "@mycelium-ip/react";
667
+
668
+ function MyComponent() {
669
+ // Get the core SDK client for direct access
670
+ const client = useMyceliumClient();
671
+
672
+ // Get the Solana connection
673
+ const connection = useMyceliumConnection();
674
+
675
+ // Get the wallet
676
+ const wallet = useMyceliumWallet();
677
+
678
+ // Get everything at once
679
+ const { client, connection, wallet, confirmation } = useMyceliumContext();
680
+ }
681
+ ```
682
+
683
+ ## Transaction Results
684
+
685
+ All mutation hooks return a `TransactionResult` on success:
686
+
687
+ ```typescript
688
+ interface TransactionResult {
689
+ signature: string; // The transaction signature
690
+ }
691
+ ```
692
+
693
+ ### Handling Results
694
+
695
+ ```tsx
696
+ function CreateEntityWithFeedback() {
697
+ const { mutate, isPending, isSuccess, isError, data, error } =
698
+ useCreateEntity();
699
+
700
+ const handleCreate = () => {
701
+ mutate(
702
+ {
703
+ handle: new TextEncoder().encode("my-entity"),
704
+ additionalControllers: [],
705
+ signatureThreshold: 1,
706
+ },
707
+ {
708
+ onSuccess: (result) => {
709
+ console.log("Transaction signature:", result.signature);
710
+ // Show success toast, redirect, etc.
711
+ },
712
+ onError: (error) => {
713
+ console.error("Transaction failed:", error.message);
714
+ // Show error toast
715
+ },
716
+ },
717
+ );
718
+ };
719
+
720
+ return (
721
+ <div>
722
+ <button onClick={handleCreate} disabled={isPending}>
723
+ {isPending ? "Creating..." : "Create Entity"}
724
+ </button>
725
+ {isSuccess && <p>Success! Signature: {data.signature}</p>}
726
+ {isError && <p>Error: {error.message}</p>}
727
+ </div>
728
+ );
729
+ }
730
+ ```
731
+
732
+ ### Async/Await Pattern
733
+
734
+ ```tsx
735
+ async function handleCreateEntity() {
736
+ const { mutateAsync } = useCreateEntity();
737
+
738
+ try {
739
+ const result = await mutateAsync({
740
+ handle: new TextEncoder().encode("my-entity"),
741
+ additionalControllers: [],
742
+ signatureThreshold: 1,
743
+ });
744
+ console.log("Created with signature:", result.signature);
745
+ } catch (error) {
746
+ console.error("Failed to create entity:", error);
747
+ }
748
+ }
749
+ ```
750
+
751
+ ## Query Keys
752
+
753
+ The SDK exports `queryKeys` for custom cache invalidation or building custom queries:
754
+
755
+ ```tsx
756
+ import { queryKeys, useMyceliumClient } from "@mycelium-ip/react";
757
+ import { useQuery, useQueryClient } from "@tanstack/react-query";
758
+
759
+ // Available query keys
760
+ queryKeys.all; // ["mycelium"]
761
+ queryKeys.entities(); // ["mycelium", "entities"]
762
+ queryKeys.entity(id); // ["mycelium", "entities", id]
763
+ queryKeys.ips(); // ["mycelium", "ips"]
764
+ queryKeys.ip(id); // ["mycelium", "ips", id]
765
+ queryKeys.licenses(); // ["mycelium", "licenses"]
766
+ queryKeys.license(id); // ["mycelium", "licenses", id]
767
+ queryKeys.grants(); // ["mycelium", "grants"]
768
+ queryKeys.grant(id); // ["mycelium", "grants", id]
769
+ queryKeys.metadata(); // ["mycelium", "metadata"]
770
+ queryKeys.derivatives(); // ["mycelium", "derivatives"]
771
+ queryKeys.derivative(id); // ["mycelium", "derivatives", id]
772
+
773
+ // Manual cache invalidation
774
+ function RefreshButton() {
775
+ const queryClient = useQueryClient();
776
+
777
+ return (
778
+ <button
779
+ onClick={() =>
780
+ queryClient.invalidateQueries({ queryKey: queryKeys.ips() })
781
+ }
782
+ >
783
+ Refresh IPs
784
+ </button>
785
+ );
786
+ }
787
+ ```
788
+
789
+ ## Advanced Usage
790
+
791
+ ### Custom QueryClient
792
+
793
+ Provide your own QueryClient for custom caching behavior:
794
+
795
+ ```tsx
796
+ import { QueryClient } from "@tanstack/react-query";
797
+ import { MyceliumIpProvider } from "@mycelium-ip/react";
798
+
799
+ const queryClient = new QueryClient({
800
+ defaultOptions: {
801
+ queries: {
802
+ staleTime: 1000 * 60 * 10, // 10 minutes
803
+ gcTime: 1000 * 60 * 60, // 1 hour
804
+ retry: 3,
805
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
806
+ },
807
+ mutations: {
808
+ retry: 1,
809
+ },
810
+ },
811
+ });
812
+
813
+ function App() {
814
+ return (
815
+ <MyceliumIpProvider
816
+ connection={connection}
817
+ wallet={wallet}
818
+ queryClient={queryClient}
819
+ >
820
+ <YourApp />
821
+ </MyceliumIpProvider>
822
+ );
823
+ }
824
+ ```
825
+
826
+ ### TypeScript Types
827
+
828
+ All parameter types are re-exported from `@mycelium-ip/core-sdk`:
829
+
830
+ ```typescript
831
+ import type {
832
+ CreateEntityParams,
833
+ CreateIpParams,
834
+ CreateLicenseParams,
835
+ CreateLicenseGrantParams,
836
+ // ... etc
837
+ } from "@mycelium-ip/core-sdk";
838
+ ```
839
+
840
+ ### Next.js App Router
841
+
842
+ The provider is already marked with `"use client"`. In Next.js App Router, create a client component for the provider:
843
+
844
+ ```tsx
845
+ // app/providers.tsx
846
+ "use client";
847
+
848
+ import { MyceliumIpProvider } from "@mycelium-ip/react";
849
+ // ... wallet setup
850
+
851
+ export function Providers({ children }: { children: React.ReactNode }) {
852
+ // ... setup logic
853
+ return (
854
+ <MyceliumIpProvider connection={connection} wallet={wallet}>
855
+ {children}
856
+ </MyceliumIpProvider>
857
+ );
858
+ }
859
+ ```
860
+
861
+ ```tsx
862
+ // app/layout.tsx
863
+ import { Providers } from "./providers";
864
+
865
+ export default function RootLayout({
866
+ children,
867
+ }: {
868
+ children: React.ReactNode;
869
+ }) {
870
+ return (
871
+ <html>
872
+ <body>
873
+ <Providers>{children}</Providers>
874
+ </body>
875
+ </html>
876
+ );
877
+ }
878
+ ```
879
+
880
+ ## API Reference
881
+
882
+ ### Exports
883
+
884
+ ```typescript
885
+ // Provider
886
+ export { MyceliumIpProvider } from "@mycelium-ip/react";
887
+ export type {
888
+ MyceliumIpProviderProps,
889
+ MyceliumIpProviderOptions,
890
+ MyceliumContextValue,
891
+ } from "@mycelium-ip/react";
892
+
893
+ // Wallet type
894
+ export type { MyceliumWallet } from "@mycelium-ip/react";
895
+
896
+ // Transaction utilities
897
+ export {
898
+ executeTransaction,
899
+ executeTransactionWithInstructions,
900
+ } from "@mycelium-ip/react";
901
+ export type { TransactionResult } from "@mycelium-ip/react";
902
+
903
+ // Query keys
904
+ export { queryKeys } from "@mycelium-ip/react";
905
+
906
+ // Accessor hooks
907
+ export {
908
+ useMyceliumClient,
909
+ useMyceliumConnection,
910
+ useMyceliumWallet,
911
+ useMyceliumContext,
912
+ } from "@mycelium-ip/react";
913
+
914
+ // Entity hooks
915
+ export {
916
+ useCreateEntity,
917
+ useUpdateEntityControllers,
918
+ } from "@mycelium-ip/react";
919
+
920
+ // IP hooks
921
+ export { useCreateIp, useTransferIp } from "@mycelium-ip/react";
922
+
923
+ // License hooks
924
+ export {
925
+ useCreateLicense,
926
+ useUpdateLicense,
927
+ useRevokeLicense,
928
+ } from "@mycelium-ip/react";
929
+
930
+ // Grant hooks
931
+ export {
932
+ useCreateLicenseGrant,
933
+ useRevokeLicenseGrant,
934
+ } from "@mycelium-ip/react";
935
+
936
+ // Derivative hooks
937
+ export {
938
+ useCreateDerivativeLink,
939
+ useUpdateDerivativeLicense,
940
+ } from "@mycelium-ip/react";
941
+
942
+ // Metadata hooks
943
+ export {
944
+ useCreateMetadataSchema,
945
+ useCreateEntityMetadata,
946
+ useCreateIpMetadata,
947
+ } from "@mycelium-ip/react";
948
+ ```
949
+
950
+ ## License
951
+
952
+ MIT