@campnetwork/origin 1.2.0-2 → 1.2.0-4

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.
package/README.md CHANGED
@@ -1,1300 +1,1329 @@
1
- <p align="center">
2
- <img src="https://imgur.com/7nLZezD.png" height="200px"/>
3
- </p>
4
- <br/>
5
-
6
- <p align="center">
7
- <a href="https://www.npmjs.com/package/@campnetwork/origin"><img src="https://img.shields.io/npm/v/@campnetwork/origin?style=for-the-badge" alt="npm version"/></a>
8
- <img alt="GitHub License" src="https://img.shields.io/github/license/campaign-layer/camp-sdk?style=for-the-badge">
9
- <img src="https://img.shields.io/npm/last-update/%40campnetwork%2Forigin?style=for-the-badge" alt="npm last update"/>
10
- <img alt="NPM Downloads" src="https://img.shields.io/npm/d18m/%40campnetwork%2Forigin?style=for-the-badge">
11
- </p>
12
-
13
- # Origin SDK
14
-
15
- The Origin SDK currently exposes the following modules:
16
-
17
- - `"@campnetwork/origin"` - The main entry point for the SDK, exposes the following classes:
18
- - `TwitterAPI` - For fetching user Twitter data from Origin
19
- - `SpotifyAPI` - For fetching user Spotify data from Origin
20
- - `Auth` - For authenticating users with the Origin SDK (browser and Node.js)
21
- - Signer adapters and utilities for Node.js support (ethers, viem, custom signers)
22
- - Camp Network chain configurations (`campMainnet`, `campTestnet`)
23
- - Origin utilities (`createLicenseTerms`, `LicenseTerms`, `DataStatus`)
24
- - `"@campnetwork/origin/react"` - Exposes the CampProvider and CampContext, as well as React components and hooks for authentication and fetching user data via Origin
25
-
26
- ## Features
27
-
28
- - 🌐 **Browser & Node.js Support** - Use in client-side and server-side applications
29
- - 🔐 **Multiple Signer Types** - Works with ethers, viem, or custom signers
30
- - 🔗 **Social Account Linking** - Connect Twitter, Spotify, Discord, TikTok, and Telegram
31
- - ⚛️ **React Components** - Pre-built UI components and hooks
32
- - 📦 **TypeScript Support** - Full type definitions included
33
- - 🛠️ **Flexible Storage** - Custom storage adapters for session persistence
34
-
35
- # Installation
36
-
37
- ```bash
38
- npm install @campnetwork/origin
39
- ```
40
-
41
- # Core
42
-
43
- The core modules can be imported either as a CommonJS module or as an ES6 module.
44
-
45
- ### CommonJS
46
-
47
- ```js
48
- const { TwitterAPI, SpotifyAPI, Auth } = require("@campnetwork/origin");
49
- ```
50
-
51
- ### ES6
52
-
53
- ```js
54
- import { TwitterAPI, SpotifyAPI, Auth } from "@campnetwork/origin";
55
- ```
56
-
57
- ## Socials
58
-
59
- ### TwitterAPI
60
-
61
- The TwitterAPI class is the entry point for fetching user Twitter data from Origin. It requires an API key to be instantiated.
62
-
63
- **Note: The methods for fetching data will only return data for users who have authenticated to your app via the Origin SDK.**
64
-
65
- #### Constructor
66
-
67
- `apiKey` - The API key of your app.
68
-
69
- ```js
70
- const twitter = new TwitterAPI({
71
- apiKey: string,
72
- });
73
- ```
74
-
75
- #### Methods
76
-
77
- ##### fetchUserByUsername
78
-
79
- `fetchUserByUsername(twitterUserName: string)`
80
-
81
- ```js
82
- const user = await twitter.fetchUserByUsername("jack");
83
- ```
84
-
85
- ##### fetchTweetsByUsername
86
-
87
- `fetchTweetsByUsername(twitterUserName: string, page: number, limit: number)`
88
-
89
- ```js
90
- const tweets = await twitter.fetchTweetsByUsername("jack", 1, 10);
91
- ```
92
-
93
- ##### fetchFollowersByUsername
94
-
95
- `fetchFollowersByUsername(twitterUserName: string, page: number, limit: number)`
96
-
97
- ```js
98
- const followers = await twitter.fetchFollowersByUsername("jack", 1, 10);
99
- ```
100
-
101
- ##### fetchFollowingByUsername
102
-
103
- `fetchFollowingByUsername(twitterUserName: string, page: number, limit: number)`
104
-
105
- ```js
106
- const following = await twitter.fetchFollowingByUsername("jack", 1, 10);
107
- ```
108
-
109
- ##### fetchTweetById
110
-
111
- `fetchTweetById(tweetId: string)`
112
-
113
- ```js
114
- const tweet = await twitter.fetchTweetById("1234567890");
115
- ```
116
-
117
- ##### fetchUserByWalletAddress
118
-
119
- `fetchUserByWalletAddress(walletAddress: string, page: number, limit: number)`
120
-
121
- ```js
122
- const user = await twitter.fetchUserByWalletAddress("0x1234567890", 1, 10);
123
- ```
124
-
125
- ##### fetchRepostedByUsername
126
-
127
- `fetchRepostedByUsername(twitterUserName: string, page: number, limit: number)`
128
-
129
- ```js
130
- const reposts = await twitter.fetchRepostedByUsername("jack", 1, 10);
131
- ```
132
-
133
- ##### fetchRepliesByUsername
134
-
135
- `fetchRepliesByUsername(twitterUserName: string, page: number, limit: number)`
136
-
137
- ```js
138
- const replies = await twitter.fetchRepliesByUsername("jack", 1, 10);
139
- ```
140
-
141
- ##### fetchLikesByUsername
142
-
143
- `fetchLikesByUsername(twitterUserName: string, page: number, limit: number)`
144
-
145
- ```js
146
- const likes = await twitter.fetchLikesByUsername("jack", 1, 10);
147
- ```
148
-
149
- ##### fetchFollowsByUsername
150
-
151
- `fetchFollowsByUsername(twitterUserName: string, page: number, limit: number)`
152
-
153
- ```js
154
- const follows = await twitter.fetchFollowsByUsername("jack", 1, 10);
155
- ```
156
-
157
- ##### fetchViewedTweetsByUsername
158
-
159
- `fetchViewedTweetsByUsername(twitterUserName: string, page: number, limit: number)`
160
-
161
- ```js
162
- const viewedTweets = await twitter.fetchViewedTweetsByUsername("jack", 1, 10);
163
- ```
164
-
165
- ### SpotifyAPI
166
-
167
- The SpotifyAPI class is the entry point for fetching user Spotify data from Origin. It requires an API key to be instantiated.
168
-
169
- **Note: The methods for fetching data will only return data for users who have authenticated to your app via the Origin SDK.**
170
-
171
- #### Constructor
172
-
173
- `apiKey` - The API key of your app.
174
-
175
- ```js
176
- const spotify = new SpotifyAPI({
177
- apiKey: string,
178
- });
179
- ```
180
-
181
- #### Methods
182
-
183
- ##### fetchSavedTracksById
184
-
185
- `fetchSavedTracksById(spotifyId: string)`
186
-
187
- ```js
188
- const savedTracks = await spotify.fetchSavedTracksById("1234567890");
189
- ```
190
-
191
- ##### fetchPlayedTracksById
192
-
193
- `fetchPlayedTracksById(spotifyId: string)`
194
-
195
- ```js
196
- const playedTracks = await spotify.fetchPlayedTracksById("1234567890");
197
- ```
198
-
199
- ##### fetchSavedAlbumsById
200
-
201
- `fetchSavedAlbumsById(spotifyId: string)`
202
-
203
- ```js
204
- const savedAlbums = await spotify.fetchSavedAlbumsById("1234567890");
205
- ```
206
-
207
- ##### fetchSavedPlaylistsById
208
-
209
- `fetchSavedPlaylistsById(spotifyId: string)`
210
-
211
- ```js
212
- const savedPlaylists = await spotify.fetchSavedPlaylistsById("1234567890");
213
- ```
214
-
215
- ##### fetchTracksInAlbum
216
-
217
- `fetchTracksInAlbum(spotifyId: string, albumId: string)`
218
-
219
- ```js
220
- const tracks = await spotify.fetchTracksInAlbum("1234567890", "1234567890");
221
- ```
222
-
223
- ##### fetchTracksInPlaylist
224
-
225
- `fetchTracksInPlaylist(spotifyId: string, playlistId: string)`
226
-
227
- ```js
228
- const tracks = await spotify.fetchTracksInPlaylist("1234567890", "1234567890");
229
- ```
230
-
231
- ##### fetchUserByWalletAddress
232
-
233
- `fetchUserByWalletAddress(walletAddress: string)`
234
-
235
- ```js
236
- const user = await spotify.fetchUserByWalletAddress("0x1234567890");
237
- ```
238
-
239
- ### TikTokAPI
240
-
241
- The TikTokAPI class is the entry point for fetching user TikTok data from Origin. It requires an API key to be instantiated.
242
-
243
- **Note: The methods for fetching data will only return data for users who have authenticated to your app via the Origin SDK.**
244
-
245
- #### Constructor
246
-
247
- `apiKey` - The API key of your app.
248
-
249
- ```js
250
- const tiktok = new TikTokAPI({
251
- apiKey: string,
252
- });
253
- ```
254
-
255
- #### Methods
256
-
257
- ##### fetchUserByUsername
258
-
259
- `fetchUserByUsername(tiktokUserName: string)`
260
-
261
- ```js
262
- const user = await tiktok.fetchUserByUsername("jack");
263
- ```
264
-
265
- ##### fetchVideoById
266
-
267
- `fetchVideoById(userHandle: string, videoId: string)`
268
-
269
- ```js
270
- const video = await tiktok.fetchVideo("jack", "1234567890");
271
- ```
272
-
273
- ## Auth
274
-
275
- The Auth class is the entry point for authenticating users with the Origin SDK. It requires a clientId to be instantiated.
276
-
277
- **Note: The Auth class is only to be used on the client side.**
278
-
279
- ### Constructor
280
-
281
- - `clientId` - The client ID of your app. This is required to authenticate users with the Origin SDK.
282
- - `redirectUri` - The URI to redirect to after the user completes oauth for any of the socials. Defaults to `window.location.href`.
283
- The `redirectUri` can also be an object with the following optional properties:
284
- - `twitter` - The URI to redirect to after the user completes oauth for Twitter.
285
- - `spotify` - The URI to redirect to after the user completes oauth for Spotify.
286
-
287
- You may use the `redirectUri` object to redirect the user to different pages based on the social they are linking.
288
- You may only define the URIs for the socials you are using, the rest will default to `window.location.href`.
289
-
290
- ```js
291
- import { Auth } from "@campnetwork/origin";
292
-
293
- const auth = new Auth({
294
- clientId: string,
295
- redirectUri: string | object,
296
- allowAnalytics: boolean,
297
- });
298
- ```
299
-
300
- ```js
301
- const auth = new Auth({
302
- clientId: "your-client-id",
303
- redirectUri: {
304
- twitter: "https://your-website.com/twitter",
305
- spotify: "https://your-website.com/spotify",
306
- },
307
- });
308
- ```
309
-
310
- ### Methods
311
-
312
- #### connect
313
-
314
- `connect() => void`
315
-
316
- The `connect` method prompts the user to sign a message with their wallet in order to authenticate with the Origin SDK.
317
- The wallet provider can be set by calling the `setProvider` method on the Auth instance beforehand. The default provider used is `window.ethereum`.
318
-
319
- ```js
320
- auth.connect();
321
- ```
322
-
323
- #### disconnect
324
-
325
- `disconnect() => void`
326
-
327
- The `disconnect` method logs the user out of the Origin SDK on the client side.
328
-
329
- ```js
330
- auth.disconnect();
331
- ```
332
-
333
- #### setProvider
334
-
335
- `setProvider(provider: { provider: EIP1193Provider, info: EIP6963ProviderInfo, address?: string }) => void`
336
-
337
- _Read more about the [EIP1193Provider](https://eips.ethereum.org/EIPS/eip-1193) and [EIP6963ProviderInfo](https://eips.ethereum.org/EIPS/eip-6963) interfaces._
338
-
339
- The `setProvider` method sets the wallet provider to be used for authentication.
340
-
341
- ```js
342
- auth.setProvider({
343
- provider: window.ethereum,
344
- info: { name: "MetaMask", icon: "https://..." },
345
- });
346
- ```
347
-
348
- #### setWalletAddress
349
-
350
- `setWalletAddress(walletAddress: string) => void`
351
-
352
- The `setWalletAddress` method sets the wallet address to be used for authentication (via the `connect` method).
353
-
354
- **This is only needed if the provider does not support the `eth_requestAccounts` method. Only use this method if you are sure you need to set the wallet address manually.**
355
-
356
- ```js
357
- auth.setWalletAddress("0x1234567890");
358
- ```
359
-
360
- #### on
361
-
362
- `on(event: string, callback: (data: any) => void) => void`
363
-
364
- The `on` method listens for events emitted by the Auth module of the Origin SDK.
365
-
366
- The following events are emitted:
367
-
368
- ##### "state"
369
-
370
- Possible states:
371
-
372
- - `authenticated` - The user has successfully authenticated.
373
- - `unauthenticated` - The user has been logged out.
374
- - `loading` - The user is in the process of authenticating.
375
-
376
- ```js
377
- auth.on("state", (data) => {
378
- console.log(data); // "authenticated" | "unauthenticated" | "loading"
379
- });
380
- ```
381
-
382
- ##### "provider"
383
-
384
- Returns the provider that has been set via the `setProvider` method.
385
- If using the Origin SDK React components, this event is emitted when the user selects a provider in the Auth modal.
386
-
387
- ```js
388
- auth.on("provider", (data) => {
389
- console.log(data); // { provider: EIP1193Provider, info: EIP6963ProviderInfo }
390
- });
391
- ```
392
-
393
- ##### "providers"
394
-
395
- Returns the list of providers that have been injected via EIP6963 and that the user can select from.
396
-
397
- ```js
398
- auth.on("providers", (data) => {
399
- console.log(data); // [{ provider: EIP1193Provider, info: EIP6963ProviderInfo }]
400
- });
401
- ```
402
-
403
- You may use this event to update the UI with the available providers. The user can then select a provider to authenticate with, and the `setProvider` method can be called with the selected provider. The `connect` method can then be called to authenticate the user.
404
-
405
- ```js
406
- auth.on("providers", (data) => {
407
- // Update UI with providers
408
- // User selects a provider
409
- const selectedProvider = data[0];
410
-
411
- auth.setProvider(selectedProvider);
412
-
413
- auth.connect();
414
- });
415
- ```
416
-
417
- #### off
418
-
419
- `off(event: string, callback: (data: any) => void) => void`
420
-
421
- The `off` method unsubscribes from events emitted by the Auth module of the Origin SDK.
422
-
423
- ```js
424
- auth.off("state", callback);
425
- ```
426
-
427
- #### getLinkedSocials
428
-
429
- `getLinkedSocials() => Promise<{ twitter: boolean, tiktok: boolean, spotify: boolean }>`
430
-
431
- The `getLinkedSocials` method returns a promise that resolves to an object containing the possible socials that the user can link and whether they are linked or not.
432
-
433
- ```js
434
- const linkedSocials = await auth.getLinkedSocials();
435
-
436
- console.log(linkedSocials); // { twitter: true, tiktok: false, spotify: true }
437
- ```
438
-
439
- ---
440
-
441
- After the user has authenticated, the following methods can be used to link and unlink social accounts.
442
- When linking a social account, the user will be redirected to the OAuth flow for that social platform.
443
- Afterwards, the user will be redirected back to the `redirectUri` specified in the Auth constructor.
444
-
445
- #### linkTwitter
446
-
447
- `linkTwitter() => void`
448
-
449
- The `linkTwitter` method redirects the user to the Twitter OAuth flow to link their Twitter account to Origin.
450
-
451
- ```js
452
- auth.linkTwitter();
453
- ```
454
-
455
- #### linkSpotify
456
-
457
- `linkSpotify() => void`
458
-
459
- The `linkSpotify` method redirects the user to the Spotify OAuth flow to link their Spotify account to Origin.
460
-
461
- ```js
462
- auth.linkSpotify();
463
- ```
464
-
465
- #### linkTikTok
466
-
467
- `linkTikTok(handle: string) => Promise<void>`
468
-
469
- The `linkTikTok` method links the provided TikTok handle to Origin.
470
-
471
- ```js
472
- auth.linkTikTok("tiktokhandle");
473
- ```
474
-
475
- ---
476
-
477
- #### unlinkTwitter
478
-
479
- `unlinkTwitter() => Promise<void>`
480
-
481
- The `unlinkTwitter` method unlinks the user's Twitter account from Origin.
482
-
483
- ```js
484
- await auth.unlinkTwitter();
485
- ```
486
-
487
- #### unlinkSpotify
488
-
489
- `unlinkSpotify() => Promise<void>`
490
-
491
- The `unlinkSpotify` method unlinks the user's Spotify account from Origin.
492
-
493
- ```js
494
- await auth.unlinkSpotify();
495
- ```
496
-
497
- #### unlinkTikTok
498
-
499
- `unlinkTikTok() => Promise<void>`
500
-
501
- The `unlinkTikTok` method unlinks the user's TikTok account from Origin.
502
-
503
- ```js
504
- await auth.unlinkTikTok();
505
- ```
506
-
507
- ## Node.js Support
508
-
509
- The Origin SDK supports Node.js environments, allowing you to authenticate and interact with Origin using server-side signers like ethers or viem.
510
-
511
- ### Installation for Node.js
512
-
513
- ```bash
514
- # With ethers
515
- npm install @campnetwork/origin ethers
516
-
517
- # With viem
518
- npm install @campnetwork/origin viem
519
- ```
520
-
521
- ### Key Differences from Browser Usage
522
-
523
- 1. **No Browser Provider Detection**: In Node.js, you explicitly provide a signer instead of detecting browser wallets
524
- 2. **Storage**: By default, Node.js uses in-memory storage (not persisted). You can provide a custom storage adapter
525
- 3. **OAuth Social Linking**: Social account linking requires browser environment for OAuth flow
526
- 4. **SIWE Domain/URI**: You must provide domain and URI for SIWE messages
527
-
528
- ### Using with ethers
529
-
530
- ```js
531
- import { Auth, campMainnet } from "@campnetwork/origin";
532
- import { ethers } from "ethers";
533
-
534
- // Setup ethers provider and signer
535
- const provider = new ethers.JsonRpcProvider(
536
- process.env.RPC_URL || campMainnet.rpcUrls.default.http[0]
537
- );
538
- const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
539
-
540
- // Create Auth instance
541
- const auth = new Auth({
542
- clientId: process.env.CLIENT_ID,
543
- redirectUri: "https://myapp.com/callback",
544
- environment: "PRODUCTION",
545
- });
546
-
547
- // Connect using ethers signer
548
- const result = await auth.connectWithSigner(signer, {
549
- domain: "myapp.com",
550
- uri: "https://myapp.com",
551
- });
552
-
553
- console.log("Connected!", result.walletAddress);
554
-
555
- // Use origin methods
556
- if (auth.origin) {
557
- const terms = await auth.origin.getTerms(tokenId);
558
- console.log("Terms:", terms);
559
- }
560
- ```
561
-
562
- ### Using with viem
563
-
564
- ```js
565
- import { Auth, createNodeWalletClient, campMainnet } from "@campnetwork/origin";
566
- import { privateKeyToAccount } from "viem/accounts";
567
-
568
- // Create viem account from private key
569
- const account = privateKeyToAccount(process.env.PRIVATE_KEY);
570
-
571
- // Create wallet client for Node.js using Camp Network chain
572
- const client = createNodeWalletClient(
573
- account,
574
- campMainnet,
575
- process.env.RPC_URL || campMainnet.rpcUrls.default.http[0]
576
- );
577
-
578
- // Create Auth instance
579
- const auth = new Auth({
580
- clientId: process.env.CLIENT_ID,
581
- redirectUri: "https://myapp.com/callback",
582
- environment: "PRODUCTION",
583
- });
584
-
585
- // Connect using viem client
586
- await auth.connectWithSigner(client, {
587
- domain: "myapp.com",
588
- uri: "https://myapp.com",
589
- });
590
-
591
- console.log("Authenticated:", auth.isAuthenticated);
592
- ```
593
-
594
- ### Exported Chain Configurations
595
-
596
- The SDK exports Camp Network chain configurations for easy use:
597
-
598
- ```js
599
- import { campMainnet, campTestnet } from "@campnetwork/origin";
600
-
601
- // campMainnet - Chain ID: 484 (Production)
602
- // campTestnet - Chain ID: 123420001114 (Basecamp testnet)
603
-
604
- console.log(campMainnet.rpcUrls.default.http[0]); // RPC URL
605
- console.log(campMainnet.blockExplorers.default.url); // Block explorer
606
- ```
607
-
608
- ### Custom Storage Adapter
609
-
610
- By default, Node.js uses in-memory storage. You can provide a custom storage adapter for persistence:
611
-
612
- ```js
613
- import { Auth, MemoryStorage } from "@campnetwork/origin";
614
-
615
- // Custom file-based storage
616
- class FileStorage {
617
- async getItem(key) {
618
- /* read from file */
619
- }
620
- async setItem(key, value) {
621
- /* write to file */
622
- }
623
- async removeItem(key) {
624
- /* delete from file */
625
- }
626
- }
627
-
628
- const auth = new Auth({
629
- clientId: process.env.CLIENT_ID,
630
- redirectUri: "https://myapp.com/callback",
631
- environment: "PRODUCTION",
632
- storage: new FileStorage(), // Custom storage
633
- });
634
- ```
635
-
636
- ### Methods
637
-
638
- #### connectWithSigner
639
-
640
- `connectWithSigner(signer: any, options?: { domain?: string, uri?: string }) => Promise<{ success: boolean, message: string, walletAddress: string }>`
641
-
642
- Connect with a custom signer (viem WalletClient, ethers Signer, or custom signer implementation).
643
-
644
- ```js
645
- await auth.connectWithSigner(signer, {
646
- domain: "myapp.com", // Required: Your application domain
647
- uri: "https://myapp.com", // Required: Your application URI
648
- });
649
- ```
650
-
651
- **Supported Signer Types:**
652
-
653
- - **viem WalletClient** - Automatically detected and used
654
- - **ethers Signer** (v5 or v6) - Works with both versions
655
- - **Custom Signer** - Must implement `getAddress()`, `signMessage()`, and `getChainId()` methods
656
-
657
- ### Exported Types and Utilities
658
-
659
- ```js
660
- import {
661
- // Auth class
662
- Auth,
663
-
664
- // Signer adapters
665
- ViemSignerAdapter,
666
- EthersSignerAdapter,
667
- CustomSignerAdapter,
668
- createSignerAdapter,
669
-
670
- // Storage adapters
671
- BrowserStorage,
672
- MemoryStorage,
673
-
674
- // Viem helpers
675
- createNodeWalletClient,
676
-
677
- // Chain configs
678
- campMainnet,
679
- campTestnet,
680
- } from "@campnetwork/origin";
681
- ```
682
-
683
- ### Examples
684
-
685
- See the [examples/server-side](./examples/server-side) directory for complete Node.js examples including:
686
-
687
- - `connect-with-ethers.js` - Using ethers v6 Signer
688
- - `connect-with-viem.js` - Using viem WalletClient
689
- - `connect-with-custom-signer.js` - Custom signer implementation
690
- - `query-origin-data.js` - Querying blockchain data
691
-
692
- # React
693
-
694
- The React components and hooks can be imported as ES6 modules. The example below shows how to set up the `CampProvider` component and subsequently use the provided hooks and components.
695
-
696
- ```js
697
- // main.jsx
698
- import { StrictMode } from "react";
699
- import { createRoot } from "react-dom/client";
700
- import { CampProvider } from "@campnetwork/origin/react";
701
- import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
702
- import App from "./App.jsx";
703
-
704
- const queryClient = new QueryClient();
705
-
706
- createRoot(document.getElementById("root")).render(
707
- <StrictMode>
708
- <QueryClientProvider client={queryClient}>
709
- <CampProvider clientId="your-client-id">
710
- <App />
711
- </CampProvider>
712
- </QueryClientProvider>
713
- </StrictMode>
714
- );
715
- ```
716
-
717
- ## CampProvider
718
-
719
- The `CampProvider` component requires a `clientId` prop to be passed in order link the users to your app.
720
- It can also take the following optional props:
721
-
722
- - `redirectUri` - `string | object` - Either a string that will be used as the redirect URI for all socials, or an object with the following optional properties: `twitter`, `spotify`. This is used to redirect the user to different pages after they have completed the OAuth flow for a social.
723
- - `environment` - `string` - The environment to use. Can be either `DEVELOPMENT` or `PRODUCTION`. Defaults to `DEVELOPMENT`.
724
- - - the `DEVELOPMENT` environment uses the Camp Testnet while the `PRODUCTION` environment uses the Camp Mainnet.
725
-
726
- ```jsx
727
- import { CampProvider } from "@campnetwork/origin/react";
728
- // ...
729
- function App() {
730
- return (
731
- <CampProvider
732
- clientId="your-client-id"
733
- redirectUri="https://your-website.com"
734
- environment="DEVELOPMENT"
735
- >
736
- <div>Your app</div>
737
- </CampProvider>
738
- );
739
- }
740
- ```
741
-
742
- Or, with an object for the `redirectUri`:
743
-
744
- ```jsx
745
- import { CampProvider } from "@campnetwork/origin/react";
746
- // ...
747
- function App() {
748
- return (
749
- <CampProvider
750
- clientId="your-client-id"
751
- redirectUri={{
752
- twitter: "https://your-website.com/twitter",
753
- spotify: "https://your-website.com/spotify",
754
- }}
755
- environment="DEVELOPMENT"
756
- >
757
- <div>Your app</div>
758
- </CampProvider>
759
- );
760
- }
761
- ```
762
-
763
- The `CampProvider` component sets up the context for the Origin SDK and provides the Auth instance to the rest of the app.
764
-
765
- ## CampModal
766
-
767
- ![@campnetwork/origin](https://imgur.com/AFmorL4.png)
768
-
769
- The **CampModal** is a one-line\* solution for authenticating users with the Origin SDK. It can be used to connect users to Origin, link and unlink social accounts, mint IPNFTs, and view the user's Origin stats.
770
-
771
- It works as follows:
772
-
773
- The **CampModal** component displays a button with the text "**Connect**" that the user can click on in order to summon the modal. The modal shows a list of available providers that the user can select from. After a provider has been selected, the `connect` method is called on the Auth instance to authenticate the user.
774
-
775
- If the user is already authenticated, the button will instead say "**My Origin**" and the modal will display the user's Origin profile information and allow them to link and unlink social accounts.
776
-
777
- The **CampModal** can take the following props:
778
-
779
- - `wcProjectId` - `string` - The WalletConnect project ID to use for authentication. Allows the users to authenticate via WalletConnect.
780
- - `injectButton` - `boolean` - Whether to inject the button into the DOM or not. Defaults to `true`. If set to `false`, the button will not be rendered and the modal can be opened programmatically via the `openModal` function returned by the `useModal` hook.
781
- - `onlyWagmi` - `boolean` - Whether to only show the provider that the user is currently authenticated with. Defaults to `false`.
782
- - `defaultProvider` - `{ provider: EIP1193Provider, info: EIP6963ProviderInfo, exclusive: boolean }` - Custom provider to set as the highlighted provider in the modal. If not set, the wagmi provider will be highlighted if it is available. The `exclusive` property can be set to `true` to only show this provider in the modal.
783
-
784
- ### Usage
785
-
786
- Basic usage of the **CampModal** component:
787
-
788
- ```jsx
789
- import { CampModal } from "@campnetwork/origin/react";
790
-
791
- function App() {
792
- return (
793
- <div>
794
- <CampModal />
795
- </div>
796
- );
797
- }
798
- ```
799
-
800
- With custom props:
801
-
802
- ```jsx
803
- import { CampModal } from "@campnetwork/origin/react";
804
-
805
- function App() {
806
- return (
807
- <div>
808
- <CampModal
809
- wcProjectId="your-wc-project-id"
810
- defaultProvider={{
811
- provider: window.ethereum,
812
- info: { name: "MetaMask", icon: "https://..." },
813
- exclusive: false,
814
- }}
815
- />
816
- </div>
817
- );
818
- }
819
- ```
820
-
821
- You can find more [examples here](./examples/client-side/react/providers-configuration).
822
-
823
- Only show the provider that the user is currently authenticated with (if using wagmi):
824
-
825
- ```jsx
826
- import { CampModal } from "@campnetwork/origin/react";
827
-
828
- function App() {
829
- return (
830
- <div>
831
- <CampModal onlyWagmi />
832
- </div>
833
- );
834
- }
835
- ```
836
-
837
- Users can be authenticated either via the Camp Modal as outlined above or programmatically by calling the `connect` method on the Auth instance.
838
-
839
- ### Usage with third party providers (Privy, Appkit, Magic, etc.)
840
-
841
- The Camp Modal can be used in conjunction with providers such as Privy and Appkit to create a seamless authentication experience for users. When using wagmi, it will automatically detect if the user is authenticated via a third party provider and give them the option to connect to Origin using that provider. Otherwise, you can set up the default provider to be whatever provider you are using.
842
-
843
- [Example usage with Privy](./examples/client-side/react/privy-connector/)
844
-
845
- [Example usage with Appkit](./examples/client-side/react/appkit-connector/)
846
-
847
- [Example usage with magic.link](./examples/client-side/react/magic-link-connector/)
848
-
849
- After the user has authenticated, you can use the provided hooks to fetch user data and listen for events.
850
-
851
- ## LinkButton
852
-
853
- The **LinkButton** component is a button that can be used to link and unlink social accounts. Under the hood it uses the `useLinkModal` hook to open the Link Socials modal.
854
-
855
- The **LinkButton** can take the following props:
856
-
857
- - `social` - `string` - The social account to link or unlink. Can be one of: `twitter`, `tiktok`, `spotify`.
858
- - `variant` - `string` - The variant of the button. Can be one of: `default`, `icon`. Defaults to `default`.
859
- - `theme` - `string` - The theme of the button. Can be one of: `default`, `camp`. Defaults to `default`.
860
-
861
- **Note: The `<CampModal/>` component must be rendered in the component tree for the buttons to work.**
862
-
863
- ### Usage
864
-
865
- Basic usage of the **LinkButton** component:
866
-
867
- ```jsx
868
- import { LinkButton, CampModal } from "@campnetwork/origin/react";
869
-
870
- function App() {
871
- return (
872
- <div>
873
- <CampModal />
874
- <LinkButton social="twitter" />
875
- <LinkButton social="spotify" theme="camp" />
876
- <LinkButton social="tiktok" variant="icon" theme="camp" />
877
- </div>
878
- );
879
- }
880
- ```
881
-
882
- ## CampButton
883
-
884
- The **CampButton** component allows you to render a button that opens the Auth or My Origin modal when clicked. It can be used as an alternative to the button that is injected by the **CampModal** component. It allows you to have multiple buttons in your app that open the modal, or to have the button in a different location than where the **CampModal** component is rendered.
885
-
886
- ```jsx
887
- import { CampButton, CampModal } from "@campnetwork/origin/react";
888
-
889
- function App() {
890
- return (
891
- <div>
892
- <CampModal injectButton={false} />
893
- <CampButton />
894
- </div>
895
- );
896
- }
897
- ```
898
-
899
- ## Hooks
900
-
901
- ### useAuth
902
-
903
- The `useAuth` hook returns the instance of the Auth class that is provided by the CampProvider.
904
- It can be used as outlined in the Core section in order to build custom authentication flows, listen for events, and fetch user data.
905
-
906
- ```jsx
907
- import { useAuth } from "@campnetwork/origin/react";
908
-
909
- function App() {
910
- const auth = useAuth();
911
-
912
- return (
913
- <div>
914
- <button onClick={auth.connect}>Connect</button>
915
- </div>
916
- );
917
- }
918
- ```
919
-
920
- ### useAuthState
921
-
922
- The `useAuthState` hook returns the current authentication state of the user.
923
-
924
- ```jsx
925
- import { useAuthState } from "@campnetwork/origin/react";
926
-
927
- function App() {
928
- const { authenticated, loading } = useAuthState();
929
-
930
- return (
931
- <div>
932
- {loading && <div>Loading...</div>}
933
- {authenticated && <div>Authenticated</div>}
934
- </div>
935
- );
936
- }
937
- ```
938
-
939
- ### useProvider
940
-
941
- The `useProvider` hook returns the provider that has been set via the `setProvider` method, as well as a `setProvider` function that can be used to update the provider.
942
-
943
- ```jsx
944
- import { useProvider } from "@campnetwork/origin/react";
945
-
946
- function App() {
947
- const { provider, setProvider } = useProvider();
948
-
949
- return (
950
- <div>
951
- <div>Current provider: {provider.info.name}</div>
952
- <button
953
- onClick={() =>
954
- setProvider({ provider: window.ethereum, info: { name: "Metamask" } })
955
- }
956
- >
957
- Set Provider
958
- </button>
959
- </div>
960
- );
961
- }
962
- ```
963
-
964
- ### useProviders
965
-
966
- The `useProviders` hook returns the list of providers that have been injected via EIP6963 and that the user or app can select from.
967
-
968
- ```jsx
969
- import { useProviders, useProvider } from "@campnetwork/origin/react";
970
-
971
- function App() {
972
- const providers = useProviders();
973
- const { setProvider } = useProvider();
974
-
975
- return (
976
- <div>
977
- {providers.map((provider) => (
978
- <button key={provider.info.name} onClick={() => setProvider(provider)}>
979
- {provider.info.name}
980
- </button>
981
- ))}
982
- </div>
983
- );
984
- }
985
- ```
986
-
987
- ### useConnect
988
-
989
- The `useConnect` hook returns functions that can be used to connect and disconnect the user.
990
-
991
- ```jsx
992
- import { useConnect, useAuthState } from "@campnetwork/origin/react";
993
-
994
- function App() {
995
- const { connect, disconnect } = useConnect();
996
- const { authenticated } = useAuthState();
997
-
998
- return (
999
- <div>
1000
- {authenticated ? (
1001
- <button onClick={disconnect}>Disconnect</button>
1002
- ) : (
1003
- <button onClick={connect}>Connect</button>
1004
- )}
1005
- </div>
1006
- );
1007
- }
1008
- ```
1009
-
1010
- ### useSocials
1011
-
1012
- The `useSocials` hook returns the state of the user's linked social accounts.
1013
-
1014
- ```jsx
1015
- import { useSocials } from "@campnetwork/origin/react";
1016
-
1017
- function App() {
1018
- const { data, error, isLoading } = useSocials();
1019
-
1020
- if (loading) return <div>Loading...</div>;
1021
- if (error) return <div>Error: {error.message}</div>;
1022
-
1023
- return (
1024
- <div>
1025
- <div>Twitter: {data.twitter ? "Linked" : "Not linked"}</div>
1026
- <div>Tiktok: {data.tiktok ? "Linked" : "Not linked"}</div>
1027
- <div>Spotify: {data.spotify ? "Linked" : "Not linked"}</div>
1028
- </div>
1029
- );
1030
- }
1031
- ```
1032
-
1033
- ### useLinkSocials
1034
-
1035
- The `useLinkSocials` hook returns functions that can be used to link and unlink social accounts.
1036
-
1037
- ```jsx
1038
- import { useLinkSocials } from "@campnetwork/origin/react";
1039
-
1040
- function App() {
1041
- const {
1042
- linkTwitter,
1043
- linkSpotify,
1044
- linkTiktok,
1045
- unlinkTwitter,
1046
- unlinkSpotify,
1047
- unlinkTiktok,
1048
- } = useLinkSocials();
1049
-
1050
- return (
1051
- <div>
1052
- <button onClick={linkTwitter}>Link Twitter</button>
1053
- <button onClick={linkSpotify}>Link Spotify</button>
1054
- <button onClick={() => linkTiktok("tiktokhandle")}>Link TikTok</button>
1055
- </button>
1056
- <button onClick={unlinkTwitter}>Unlink Twitter</button>
1057
- <button onClick={unlinkTiktok}>Unlink TikTok</button>
1058
- <button onClick={unlinkSpotify}>Unlink Spotify</button>
1059
- </div>
1060
- );
1061
- }
1062
- ```
1063
-
1064
- ### useModal
1065
-
1066
- The `useModal` hook returns the state of the Auth and My Origin modals, as well as functions to show and hide them.
1067
-
1068
- **Note: The `<CampModal/>` component must be rendered in the component tree for the modals to be displayed.**
1069
-
1070
- ```jsx
1071
- import { useModal, CampModal } from "@campnetwork/origin/react";
1072
-
1073
- function App() {
1074
- const { isOpen, openModal, closeModal } = useModal();
1075
-
1076
- return (
1077
- <div>
1078
- <button onClick={openModal}>Open Modal</button>
1079
- <button onClick={closeModal}>Close Modal</button>
1080
- <CampModal injectButton={false} />
1081
- </div>
1082
- );
1083
- }
1084
- ```
1085
-
1086
- The state and functions returned by the `useModal` hook can be used to show and hide the Auth and My Origin modals, as well as to check if they are currently open. The modal being controlled is dictated by the user's authentication state.
1087
-
1088
- ### useLinkModal
1089
-
1090
- The `useLinkModal` hook returns the state of the Link Socials modal, as well as functions to show and hide it.
1091
-
1092
- **Note: The `<CampModal/>` component must be rendered in the component tree for the modal to be displayed.**
1093
-
1094
- ```jsx
1095
- import { useLinkModal, CampModal } from "@campnetwork/origin/react";
1096
-
1097
- function App() {
1098
- const { isLinkingOpen, openTwitterModal } = useLinkModal();
1099
-
1100
- return (
1101
- <div>
1102
- <CampModal />
1103
- <button onClick={openTwitterModal}>Link Twitter</button>
1104
- </div>
1105
- );
1106
- }
1107
- ```
1108
-
1109
- It returns the following properties and functions:
1110
-
1111
- - `isLinkingOpen` - `boolean` - Whether the Link Socials modal is open or not.
1112
- - `openTwitterModal` - `() => void`
1113
- - `openSpotifyModal` - `() => void`
1114
- - `openTiktokModal` - `() => void`
1115
- - `linkTwitter` - `() => void`
1116
- - `linkSpotify` - `() => void`
1117
- - `linkTiktok` - `() => void`
1118
- - `linkTelegram` - `() => void`
1119
- - `unlinkTwitter` - `() => void`
1120
- - `unlinkSpotify` - `() => void`
1121
- - `unlinkTiktok` - `() => void`
1122
- - `closeModal` - `() => void`
1123
-
1124
- The difference between the `openXModal` functions and the `linkX / unlinkX` functions is that the former opens the modal regardless of the user's linking state, allowing them to either link or unlink their account, while the latter only opens the specified modal if the user's linking state allows for it.
1125
-
1126
- For example, if the user is linked to Twitter, calling `openTwitterModal` will open the modal to _unlink_ their Twitter account, while calling `linkTwitter` will not do anything, and calling `unlinkTwitter` will open the modal to unlink their Twitter account.
1127
-
1128
- ## Origin Methods (`auth.origin`)
1129
-
1130
- The `Origin` class provides blockchain and API methods for interacting with Origin IpNFTs, uploading files, managing user stats, and more. Access these via `auth.origin` after authentication.
1131
-
1132
- ### Types
1133
-
1134
- #### `LicenseTerms`
1135
-
1136
- The license terms object used in minting and updating methods:
1137
-
1138
- ```typescript
1139
- type LicenseTerms = {
1140
- price: bigint; // Price in wei
1141
- duration: number; // Duration in seconds
1142
- royaltyBps: number; // Royalty in basis points (1-10000)
1143
- paymentToken: Address; // Payment token address (address(0) for native currency)
1144
- };
1145
- ```
1146
-
1147
- ### Minting Constraints
1148
-
1149
- When minting or updating an IpNFT, the following constraints apply to the `LicenseTerms`:
1150
-
1151
- - The price must be at least `1000000000000000` wei (0.001 $CAMP).
1152
- - The royaltyBps must be between `1` and `10000` (0.01% to 100%).
1153
- - The duration must be between `86400` seconds and `2628000` seconds (1 day to 30 days).
1154
-
1155
- ### `createLicenseTerms(price, duration, royaltyBps, paymentToken)`
1156
-
1157
- A utility function to create properly validated license terms for minting and updating IpNFTs.
1158
-
1159
- - `price`: Price in wei (bigint) - must be at least `1000000000000000` wei
1160
- - `duration`: Duration in seconds (number) - must be between `86400` and `2628000` seconds
1161
- - `royaltyBps`: Royalty in basis points (number) - must be between `1` and `10000`
1162
- - `paymentToken`: Payment token address (Address) - use `zeroAddress` from viem for native currency
1163
- - **Returns:** A validated `LicenseTerms` object
1164
- - **Throws:** Error if any parameter violates the constraints
1165
-
1166
- **Example:**
1167
-
1168
- ```typescript
1169
- import { createLicenseTerms } from "@campnetwork/origin";
1170
- import { zeroAddress } from "viem";
1171
-
1172
- // Create license terms with validation
1173
- const license = createLicenseTerms(
1174
- BigInt("1000000000000000"), // 0.001 CAMP in wei
1175
- 86400, // 1 day in seconds
1176
- 1000, // 10% royalty (1000 basis points)
1177
- zeroAddress // Native currency (CAMP)
1178
- );
1179
-
1180
- // Use with minting functions
1181
- await auth.origin.mintFile(file, metadata, license);
1182
- await auth.origin.mintSocial("twitter", metadata, license);
1183
- ```
1184
-
1185
- ### File Upload & Minting
1186
-
1187
- #### `mintFile(file, metadata, license, parents?, options?)`
1188
-
1189
- Uploads a file and mints an IpNFT for it.
1190
-
1191
- - `file`: File to upload and mint
1192
- - `metadata`: Additional metadata for the IpNFT
1193
- - `name`: Name of the IpNFT
1194
- - `description`: Description of the IpNFT
1195
- - `image`: Optional image URL for the IpNFT
1196
- - `attributes`: Optional array of attributes
1197
- - `license`: LicenseTerms object
1198
- - `parents`: Optional array of parent token IDs for derivatives
1199
- - `options.progressCallback`: Optional progress callback
1200
- - **Returns:** Minted token ID as a string, or throws on failure
1201
-
1202
- #### `mintSocial(source, metadata, license)`
1203
-
1204
- Mints an IpNFT for a connected social account.
1205
-
1206
- - `source`: Social platform (`"spotify" | "twitter" | "tiktok"`)
1207
- - `metadata`: Additional metadata for the IpNFT
1208
- - `license`: LicenseTerms object
1209
- - **Returns:** Minted token ID as a string, or throws on failure
1210
-
1211
- ### IpNFT & Marketplace Methods
1212
-
1213
- Most methods mirror smart contract functions and require appropriate permissions.
1214
-
1215
- #### Core IpNFT Methods
1216
-
1217
- - `mintWithSignature(account, tokenId, parents, creatorContentHash, uri, license, deadline, signature)`
1218
- - `registerIpNFT(source, deadline, license, metadata, fileKey?, parents?)`
1219
- - `updateTerms(tokenId, license)`
1220
- - `finalizeDelete(tokenId)`
1221
- - `getOrCreateRoyaltyVault(tokenId)`
1222
- - `getTerms(tokenId)`
1223
- - `ownerOf(tokenId)`
1224
- - `balanceOf(owner)`
1225
- - `tokenURI(tokenId)`
1226
- - `dataStatus(tokenId)`
1227
- - `isApprovedForAll(owner, operator)`
1228
- - `transferFrom(from, to, tokenId)`
1229
- - `safeTransferFrom(from, to, tokenId)`
1230
- - `approve(to, tokenId)`
1231
- - `setApprovalForAll(operator, approved)`
1232
-
1233
- #### Marketplace Methods
1234
-
1235
- - `buyAccess(buyer, tokenId, expectedPrice, expectedDuration, expectedPaymentToken, value?)`
1236
- - `hasAccess(tokenId, user)`
1237
- - `subscriptionExpiry(tokenId, user)`
1238
-
1239
- #### Utility & Royalty Methods
1240
-
1241
- - `getRoyalties(token?, owner?)` — Get royalty vault and balance
1242
- - `claimRoyalties(token?, owner?)` Claim royalties
1243
-
1244
- #### Smart Access & Data
1245
-
1246
- - `buyAccessSmart(tokenId)` — Buys access, handles payment approval and license details - **Recommended in place of buyAccess**
1247
- - `getData(tokenId)` Fetches the underlying IP for a given IPNFT if the user has purchased access to it
1248
-
1249
- ### User Data & Stats
1250
-
1251
- - `getOriginUploads()` — Fetch user's Origin file uploads (returns array or null)
1252
- - `getOriginUsage()` Fetch user's Origin stats (returns object with multiplier, points, active, teams, dataSources)
1253
- - `setOriginConsent(consent: boolean)` — Set user's consent for Origin usage
1254
-
1255
- ### Utility Methods
1256
-
1257
- - `getJwt()` — Get current JWT token
1258
- - `setViemClient(client)` — Set viem wallet client for blockchain interactions
1259
-
1260
- ---
1261
-
1262
- Call these methods as `await auth.origin.methodName(...)` after authenticating. See inline code documentation for full details and parameter types.
1263
-
1264
- ---
1265
-
1266
- # Contributing
1267
-
1268
- Install the dependencies.
1269
-
1270
- ```bash
1271
- npm install
1272
- ```
1273
-
1274
- Build the SDK.
1275
-
1276
- ```bash
1277
- npm run build
1278
- ```
1279
-
1280
- This will generate the SDK in the `dist` folder.
1281
-
1282
- You can also run the following command to watch for changes and rebuild the SDK automatically:
1283
-
1284
- ```bash
1285
- npm run dev
1286
- ```
1287
-
1288
- In order to use the sdk in a local project, you can link the sdk to the project.
1289
-
1290
- ```bash
1291
- npm link .
1292
- ```
1293
-
1294
- Then, in the project you want to use the sdk in, run:
1295
-
1296
- ```bash
1297
- npm link @campnetwork/origin
1298
- ```
1299
-
1300
- This will link the local sdk to the project.
1
+ <p align="center">
2
+ <img src="https://imgur.com/7nLZezD.png" height="200px"/>
3
+ </p>
4
+ <br/>
5
+
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/@campnetwork/origin"><img src="https://img.shields.io/npm/v/@campnetwork/origin?style=for-the-badge" alt="npm version"/></a>
8
+ <img alt="GitHub License" src="https://img.shields.io/github/license/campaign-layer/camp-sdk?style=for-the-badge">
9
+ <img src="https://img.shields.io/npm/last-update/%40campnetwork%2Forigin?style=for-the-badge" alt="npm last update"/>
10
+ <img alt="NPM Downloads" src="https://img.shields.io/npm/d18m/%40campnetwork%2Forigin?style=for-the-badge">
11
+ </p>
12
+
13
+ # Origin SDK
14
+
15
+ The Origin SDK currently exposes the following modules:
16
+
17
+ - `"@campnetwork/origin"` - The main entry point for the SDK, exposes the following classes:
18
+ - `TwitterAPI` - For fetching user Twitter data from Origin
19
+ - `SpotifyAPI` - For fetching user Spotify data from Origin
20
+ - `Auth` - For authenticating users with the Origin SDK (browser and Node.js)
21
+ - Signer adapters and utilities for Node.js support (ethers, viem, custom signers)
22
+ - Camp Network chain configurations (`campMainnet`, `campTestnet`)
23
+ - Origin utilities (`createLicenseTerms`, `LicenseTerms`, `DataStatus`)
24
+ - `"@campnetwork/origin/react"` - Exposes the CampProvider and CampContext, as well as React components and hooks for authentication and fetching user data via Origin
25
+
26
+ ## Features
27
+
28
+ - **Browser & Node.js Support** - Use in client-side and server-side applications
29
+ - **Multiple Signer Types** - Works with ethers, viem, or custom signers
30
+ - **Social Account Linking** - Connect Twitter, Spotify, and TikTok
31
+ - **React Components** - Pre-built UI components and hooks
32
+ - **TypeScript Support** - Full type definitions included
33
+ - **Flexible Storage** - Custom storage adapters for session persistence
34
+
35
+ # Installation
36
+
37
+ ```bash
38
+ npm install @campnetwork/origin
39
+ ```
40
+
41
+ # Core
42
+
43
+ The core modules can be imported either as a CommonJS module or as an ES6 module.
44
+
45
+ ### CommonJS
46
+
47
+ ```js
48
+ const { TwitterAPI, SpotifyAPI, Auth } = require("@campnetwork/origin");
49
+ ```
50
+
51
+ ### ES6
52
+
53
+ ```js
54
+ import { TwitterAPI, SpotifyAPI, Auth } from "@campnetwork/origin";
55
+ ```
56
+
57
+ ## Socials
58
+
59
+ ### TwitterAPI
60
+
61
+ The TwitterAPI class is the entry point for fetching user Twitter data from Origin. It requires an API key to be instantiated.
62
+
63
+ **Note: The methods for fetching data will only return data for users who have authenticated to your app via the Origin SDK.**
64
+
65
+ #### Constructor
66
+
67
+ `apiKey` - The API key of your app.
68
+
69
+ ```js
70
+ const twitter = new TwitterAPI({
71
+ apiKey: string,
72
+ });
73
+ ```
74
+
75
+ #### Methods
76
+
77
+ ##### fetchUserByUsername
78
+
79
+ `fetchUserByUsername(twitterUserName: string)`
80
+
81
+ ```js
82
+ const user = await twitter.fetchUserByUsername("jack");
83
+ ```
84
+
85
+ ##### fetchTweetsByUsername
86
+
87
+ `fetchTweetsByUsername(twitterUserName: string, page: number, limit: number)`
88
+
89
+ ```js
90
+ const tweets = await twitter.fetchTweetsByUsername("jack", 1, 10);
91
+ ```
92
+
93
+ ##### fetchFollowersByUsername
94
+
95
+ `fetchFollowersByUsername(twitterUserName: string, page: number, limit: number)`
96
+
97
+ ```js
98
+ const followers = await twitter.fetchFollowersByUsername("jack", 1, 10);
99
+ ```
100
+
101
+ ##### fetchFollowingByUsername
102
+
103
+ `fetchFollowingByUsername(twitterUserName: string, page: number, limit: number)`
104
+
105
+ ```js
106
+ const following = await twitter.fetchFollowingByUsername("jack", 1, 10);
107
+ ```
108
+
109
+ ##### fetchTweetById
110
+
111
+ `fetchTweetById(tweetId: string)`
112
+
113
+ ```js
114
+ const tweet = await twitter.fetchTweetById("1234567890");
115
+ ```
116
+
117
+ ##### fetchUserByWalletAddress
118
+
119
+ `fetchUserByWalletAddress(walletAddress: string, page: number, limit: number)`
120
+
121
+ ```js
122
+ const user = await twitter.fetchUserByWalletAddress("0x1234567890", 1, 10);
123
+ ```
124
+
125
+ ##### fetchRepostedByUsername
126
+
127
+ `fetchRepostedByUsername(twitterUserName: string, page: number, limit: number)`
128
+
129
+ ```js
130
+ const reposts = await twitter.fetchRepostedByUsername("jack", 1, 10);
131
+ ```
132
+
133
+ ##### fetchRepliesByUsername
134
+
135
+ `fetchRepliesByUsername(twitterUserName: string, page: number, limit: number)`
136
+
137
+ ```js
138
+ const replies = await twitter.fetchRepliesByUsername("jack", 1, 10);
139
+ ```
140
+
141
+ ##### fetchLikesByUsername
142
+
143
+ `fetchLikesByUsername(twitterUserName: string, page: number, limit: number)`
144
+
145
+ ```js
146
+ const likes = await twitter.fetchLikesByUsername("jack", 1, 10);
147
+ ```
148
+
149
+ ##### fetchFollowsByUsername
150
+
151
+ `fetchFollowsByUsername(twitterUserName: string, page: number, limit: number)`
152
+
153
+ ```js
154
+ const follows = await twitter.fetchFollowsByUsername("jack", 1, 10);
155
+ ```
156
+
157
+ ##### fetchViewedTweetsByUsername
158
+
159
+ `fetchViewedTweetsByUsername(twitterUserName: string, page: number, limit: number)`
160
+
161
+ ```js
162
+ const viewedTweets = await twitter.fetchViewedTweetsByUsername("jack", 1, 10);
163
+ ```
164
+
165
+ ### SpotifyAPI
166
+
167
+ The SpotifyAPI class is the entry point for fetching user Spotify data from Origin. It requires an API key to be instantiated.
168
+
169
+ **Note: The methods for fetching data will only return data for users who have authenticated to your app via the Origin SDK.**
170
+
171
+ #### Constructor
172
+
173
+ `apiKey` - The API key of your app.
174
+
175
+ ```js
176
+ const spotify = new SpotifyAPI({
177
+ apiKey: string,
178
+ });
179
+ ```
180
+
181
+ #### Methods
182
+
183
+ ##### fetchSavedTracksById
184
+
185
+ `fetchSavedTracksById(spotifyId: string)`
186
+
187
+ ```js
188
+ const savedTracks = await spotify.fetchSavedTracksById("1234567890");
189
+ ```
190
+
191
+ ##### fetchPlayedTracksById
192
+
193
+ `fetchPlayedTracksById(spotifyId: string)`
194
+
195
+ ```js
196
+ const playedTracks = await spotify.fetchPlayedTracksById("1234567890");
197
+ ```
198
+
199
+ ##### fetchSavedAlbumsById
200
+
201
+ `fetchSavedAlbumsById(spotifyId: string)`
202
+
203
+ ```js
204
+ const savedAlbums = await spotify.fetchSavedAlbumsById("1234567890");
205
+ ```
206
+
207
+ ##### fetchSavedPlaylistsById
208
+
209
+ `fetchSavedPlaylistsById(spotifyId: string)`
210
+
211
+ ```js
212
+ const savedPlaylists = await spotify.fetchSavedPlaylistsById("1234567890");
213
+ ```
214
+
215
+ ##### fetchTracksInAlbum
216
+
217
+ `fetchTracksInAlbum(spotifyId: string, albumId: string)`
218
+
219
+ ```js
220
+ const tracks = await spotify.fetchTracksInAlbum("1234567890", "1234567890");
221
+ ```
222
+
223
+ ##### fetchTracksInPlaylist
224
+
225
+ `fetchTracksInPlaylist(spotifyId: string, playlistId: string)`
226
+
227
+ ```js
228
+ const tracks = await spotify.fetchTracksInPlaylist("1234567890", "1234567890");
229
+ ```
230
+
231
+ ##### fetchUserByWalletAddress
232
+
233
+ `fetchUserByWalletAddress(walletAddress: string)`
234
+
235
+ ```js
236
+ const user = await spotify.fetchUserByWalletAddress("0x1234567890");
237
+ ```
238
+
239
+ ### TikTokAPI
240
+
241
+ The TikTokAPI class is the entry point for fetching user TikTok data from Origin. It requires an API key to be instantiated.
242
+
243
+ **Note: The methods for fetching data will only return data for users who have authenticated to your app via the Origin SDK.**
244
+
245
+ #### Constructor
246
+
247
+ `apiKey` - The API key of your app.
248
+
249
+ ```js
250
+ const tiktok = new TikTokAPI({
251
+ apiKey: string,
252
+ });
253
+ ```
254
+
255
+ #### Methods
256
+
257
+ ##### fetchUserByUsername
258
+
259
+ `fetchUserByUsername(tiktokUserName: string)`
260
+
261
+ ```js
262
+ const user = await tiktok.fetchUserByUsername("jack");
263
+ ```
264
+
265
+ ##### fetchVideoById
266
+
267
+ `fetchVideoById(userHandle: string, videoId: string)`
268
+
269
+ ```js
270
+ const video = await tiktok.fetchVideo("jack", "1234567890");
271
+ ```
272
+
273
+ ## Auth
274
+
275
+ The Auth class is the entry point for authenticating users with the Origin SDK. It requires a clientId to be instantiated.
276
+
277
+ ### Constructor
278
+
279
+ - `clientId` - The client ID of your app. This is required to authenticate users with the Origin SDK.
280
+ - `redirectUri` - The URI to redirect to after the user completes oauth for any of the socials. Defaults to `window.location.href`.
281
+ The `redirectUri` can also be an object with the following optional properties:
282
+ - `twitter` - The URI to redirect to after the user completes oauth for Twitter.
283
+ - `spotify` - The URI to redirect to after the user completes oauth for Spotify.
284
+ - `environment` - The environment to use. Can be either `DEVELOPMENT` or `PRODUCTION`. Defaults to `DEVELOPMENT`.
285
+ - `baseParentId` - A valid tokenID to be used as the parent of all IPNFTs minted on your platform, making them all derivatives of your base asset.
286
+
287
+ You may use the `redirectUri` object to redirect the user to different pages based on the social they are linking.
288
+ You may only define the URIs for the socials you are using, the rest will default to `window.location.href`.
289
+
290
+ ```js
291
+ import { Auth } from "@campnetwork/origin";
292
+
293
+ const auth = new Auth({
294
+ clientId: string,
295
+ redirectUri: string | object,
296
+ allowAnalytics: boolean,
297
+ });
298
+ ```
299
+
300
+ ```js
301
+ const auth = new Auth({
302
+ clientId: "your-client-id",
303
+ redirectUri: {
304
+ twitter: "https://your-website.com/twitter",
305
+ spotify: "https://your-website.com/spotify",
306
+ },
307
+ });
308
+ ```
309
+
310
+ ### Methods
311
+
312
+ #### connect
313
+
314
+ `connect() => void`
315
+
316
+ The `connect` method prompts the user to sign a message with their wallet in order to authenticate with the Origin SDK.
317
+ The wallet provider can be set by calling the `setProvider` method on the Auth instance beforehand. The default provider used is `window.ethereum`.
318
+
319
+ ```js
320
+ auth.connect();
321
+ ```
322
+
323
+ #### disconnect
324
+
325
+ `disconnect() => void`
326
+
327
+ The `disconnect` method logs the user out of the Origin SDK on the client side.
328
+
329
+ ```js
330
+ auth.disconnect();
331
+ ```
332
+
333
+ #### setProvider
334
+
335
+ `setProvider(provider: { provider: EIP1193Provider, info: EIP6963ProviderInfo, address?: string }) => void`
336
+
337
+ _Read more about the [EIP1193Provider](https://eips.ethereum.org/EIPS/eip-1193) and [EIP6963ProviderInfo](https://eips.ethereum.org/EIPS/eip-6963) interfaces._
338
+
339
+ The `setProvider` method sets the wallet provider to be used for authentication.
340
+
341
+ ```js
342
+ auth.setProvider({
343
+ provider: window.ethereum,
344
+ info: { name: "MetaMask", icon: "https://..." },
345
+ });
346
+ ```
347
+
348
+ #### setWalletAddress
349
+
350
+ `setWalletAddress(walletAddress: string) => void`
351
+
352
+ The `setWalletAddress` method sets the wallet address to be used for authentication (via the `connect` method).
353
+
354
+ **This is only needed if the provider does not support the `eth_requestAccounts` method. Only use this method if you are sure you need to set the wallet address manually.**
355
+
356
+ ```js
357
+ auth.setWalletAddress("0x1234567890");
358
+ ```
359
+
360
+ #### on
361
+
362
+ `on(event: string, callback: (data: any) => void) => void`
363
+
364
+ The `on` method listens for events emitted by the Auth module of the Origin SDK.
365
+
366
+ The following events are emitted:
367
+
368
+ ##### "state"
369
+
370
+ Possible states:
371
+
372
+ - `authenticated` - The user has successfully authenticated.
373
+ - `unauthenticated` - The user has been logged out.
374
+ - `loading` - The user is in the process of authenticating.
375
+
376
+ ```js
377
+ auth.on("state", (data) => {
378
+ console.log(data); // "authenticated" | "unauthenticated" | "loading"
379
+ });
380
+ ```
381
+
382
+ ##### "provider"
383
+
384
+ Returns the provider that has been set via the `setProvider` method.
385
+ If using the Origin SDK React components, this event is emitted when the user selects a provider in the Auth modal.
386
+
387
+ ```js
388
+ auth.on("provider", (data) => {
389
+ console.log(data); // { provider: EIP1193Provider, info: EIP6963ProviderInfo }
390
+ });
391
+ ```
392
+
393
+ ##### "providers"
394
+
395
+ Returns the list of providers that have been injected via EIP6963 and that the user can select from.
396
+
397
+ ```js
398
+ auth.on("providers", (data) => {
399
+ console.log(data); // [{ provider: EIP1193Provider, info: EIP6963ProviderInfo }]
400
+ });
401
+ ```
402
+
403
+ You may use this event to update the UI with the available providers. The user can then select a provider to authenticate with, and the `setProvider` method can be called with the selected provider. The `connect` method can then be called to authenticate the user.
404
+
405
+ ```js
406
+ auth.on("providers", (data) => {
407
+ // Update UI with providers
408
+ // User selects a provider
409
+ const selectedProvider = data[0];
410
+
411
+ auth.setProvider(selectedProvider);
412
+
413
+ auth.connect();
414
+ });
415
+ ```
416
+
417
+ #### off
418
+
419
+ `off(event: string, callback: (data: any) => void) => void`
420
+
421
+ The `off` method unsubscribes from events emitted by the Auth module of the Origin SDK.
422
+
423
+ ```js
424
+ auth.off("state", callback);
425
+ ```
426
+
427
+ #### getLinkedSocials
428
+
429
+ `getLinkedSocials() => Promise<{ twitter: boolean, tiktok: boolean, spotify: boolean }>`
430
+
431
+ The `getLinkedSocials` method returns a promise that resolves to an object containing the possible socials that the user can link and whether they are linked or not.
432
+
433
+ ```js
434
+ const linkedSocials = await auth.getLinkedSocials();
435
+
436
+ console.log(linkedSocials); // { twitter: true, tiktok: false, spotify: true }
437
+ ```
438
+
439
+ ---
440
+
441
+ After the user has authenticated, the following methods can be used to link and unlink social accounts.
442
+ When linking a social account, the user will be redirected to the OAuth flow for that social platform.
443
+ Afterwards, the user will be redirected back to the `redirectUri` specified in the Auth constructor.
444
+
445
+ **Note: Linking socials is only available in a browser environment**
446
+
447
+ #### linkTwitter
448
+
449
+ `linkTwitter() => void`
450
+
451
+ The `linkTwitter` method redirects the user to the Twitter OAuth flow to link their Twitter account to Origin.
452
+
453
+ ```js
454
+ auth.linkTwitter();
455
+ ```
456
+
457
+ #### linkSpotify
458
+
459
+ `linkSpotify() => void`
460
+
461
+ The `linkSpotify` method redirects the user to the Spotify OAuth flow to link their Spotify account to Origin.
462
+
463
+ ```js
464
+ auth.linkSpotify();
465
+ ```
466
+
467
+ #### linkTikTok
468
+
469
+ `linkTikTok(handle: string) => Promise<void>`
470
+
471
+ The `linkTikTok` method links the provided TikTok handle to Origin.
472
+
473
+ ```js
474
+ auth.linkTikTok("tiktokhandle");
475
+ ```
476
+
477
+ ---
478
+
479
+ #### unlinkTwitter
480
+
481
+ `unlinkTwitter() => Promise<void>`
482
+
483
+ The `unlinkTwitter` method unlinks the user's Twitter account from Origin.
484
+
485
+ ```js
486
+ await auth.unlinkTwitter();
487
+ ```
488
+
489
+ #### unlinkSpotify
490
+
491
+ `unlinkSpotify() => Promise<void>`
492
+
493
+ The `unlinkSpotify` method unlinks the user's Spotify account from Origin.
494
+
495
+ ```js
496
+ await auth.unlinkSpotify();
497
+ ```
498
+
499
+ #### unlinkTikTok
500
+
501
+ `unlinkTikTok() => Promise<void>`
502
+
503
+ The `unlinkTikTok` method unlinks the user's TikTok account from Origin.
504
+
505
+ ```js
506
+ await auth.unlinkTikTok();
507
+ ```
508
+
509
+ ## Node.js Support
510
+
511
+ The Origin SDK supports Node.js environments, allowing you to authenticate and interact with Origin using server-side signers like ethers or viem.
512
+
513
+ ### Installation for Node.js
514
+
515
+ ```bash
516
+ # With ethers
517
+ npm install @campnetwork/origin ethers
518
+
519
+ # With viem
520
+ npm install @campnetwork/origin viem
521
+ ```
522
+
523
+ ### Key Differences from Browser Usage
524
+
525
+ 1. **No Browser Provider Detection**: In Node.js, you explicitly provide a signer instead of detecting browser wallets
526
+ 2. **Storage**: By default, Node.js uses in-memory storage (not persisted). You can provide a custom storage adapter
527
+ 3. **OAuth Social Linking**: Social account linking requires browser environment for OAuth flow
528
+ 4. **SIWE Domain/URI**: You must provide domain and URI for SIWE messages
529
+
530
+ ### Using with ethers
531
+
532
+ ```js
533
+ import { Auth, campMainnet } from "@campnetwork/origin";
534
+ import { ethers } from "ethers";
535
+
536
+ // Setup ethers provider and signer
537
+ const provider = new ethers.JsonRpcProvider(
538
+ process.env.RPC_URL || campMainnet.rpcUrls.default.http[0]
539
+ );
540
+ const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
541
+
542
+ // Create Auth instance
543
+ const auth = new Auth({
544
+ clientId: process.env.CLIENT_ID,
545
+ redirectUri: "https://myapp.com/callback",
546
+ environment: "PRODUCTION",
547
+ });
548
+
549
+ // Connect using ethers signer
550
+ const result = await auth.connectWithSigner(signer, {
551
+ domain: "myapp.com",
552
+ uri: "https://myapp.com",
553
+ });
554
+
555
+ console.log("Connected!", result.walletAddress);
556
+
557
+ // Use origin methods
558
+ if (auth.origin) {
559
+ const terms = await auth.origin.getTerms(tokenId);
560
+ console.log("Terms:", terms);
561
+ }
562
+ ```
563
+
564
+ ### Using with viem
565
+
566
+ ```js
567
+ import { Auth, createNodeWalletClient, campMainnet } from "@campnetwork/origin";
568
+ import { privateKeyToAccount } from "viem/accounts";
569
+
570
+ // Create viem account from private key
571
+ const account = privateKeyToAccount(process.env.PRIVATE_KEY);
572
+
573
+ // Create wallet client for Node.js using Camp Network chain
574
+ const client = createNodeWalletClient(
575
+ account,
576
+ campMainnet,
577
+ process.env.RPC_URL || campMainnet.rpcUrls.default.http[0]
578
+ );
579
+
580
+ // Create Auth instance
581
+ const auth = new Auth({
582
+ clientId: process.env.CLIENT_ID,
583
+ redirectUri: "https://myapp.com/callback",
584
+ environment: "PRODUCTION",
585
+ });
586
+
587
+ // Connect using viem client
588
+ await auth.connectWithSigner(client, {
589
+ domain: "myapp.com",
590
+ uri: "https://myapp.com",
591
+ });
592
+
593
+ console.log("Authenticated:", auth.isAuthenticated);
594
+ ```
595
+
596
+ ### Exported Chain Configurations
597
+
598
+ The SDK exports Camp Network chain configurations for easy use:
599
+
600
+ ```js
601
+ import { campMainnet, campTestnet } from "@campnetwork/origin";
602
+
603
+ // campMainnet - Chain ID: 484 (Production)
604
+ // campTestnet - Chain ID: 123420001114 (Basecamp testnet)
605
+
606
+ console.log(campMainnet.rpcUrls.default.http[0]); // RPC URL
607
+ console.log(campMainnet.blockExplorers.default.url); // Block explorer
608
+ ```
609
+
610
+ ### Custom Storage Adapter
611
+
612
+ By default, Node.js uses in-memory storage. You can provide a custom storage adapter for persistence:
613
+
614
+ ```js
615
+ import { Auth, MemoryStorage } from "@campnetwork/origin";
616
+
617
+ // Custom file-based storage
618
+ class FileStorage {
619
+ async getItem(key) {
620
+ /* read from file */
621
+ }
622
+ async setItem(key, value) {
623
+ /* write to file */
624
+ }
625
+ async removeItem(key) {
626
+ /* delete from file */
627
+ }
628
+ }
629
+
630
+ const auth = new Auth({
631
+ clientId: process.env.CLIENT_ID,
632
+ redirectUri: "https://myapp.com/callback",
633
+ environment: "PRODUCTION",
634
+ storage: new FileStorage(), // Custom storage
635
+ });
636
+ ```
637
+
638
+ ### Methods
639
+
640
+ #### connectWithSigner
641
+
642
+ `connectWithSigner(signer: any, options?: { domain?: string, uri?: string }) => Promise<{ success: boolean, message: string, walletAddress: string }>`
643
+
644
+ Connect with a custom signer (viem WalletClient, ethers Signer, or custom signer implementation).
645
+
646
+ ```js
647
+ await auth.connectWithSigner(signer, {
648
+ domain: "myapp.com", // Required: Your application domain
649
+ uri: "https://myapp.com", // Required: Your application URI
650
+ });
651
+ ```
652
+
653
+ **Supported Signer Types:**
654
+
655
+ - **viem WalletClient** - Automatically detected and used
656
+ - **ethers Signer** (v5 or v6) - Works with both versions
657
+ - **Custom Signer** - Must implement `getAddress()`, `signMessage()`, and `getChainId()` methods
658
+
659
+ ### Exported Types and Utilities
660
+
661
+ ```js
662
+ import {
663
+ // Auth class
664
+ Auth,
665
+
666
+ // Signer adapters
667
+ ViemSignerAdapter,
668
+ EthersSignerAdapter,
669
+ CustomSignerAdapter,
670
+ createSignerAdapter,
671
+
672
+ // Storage adapters
673
+ BrowserStorage,
674
+ MemoryStorage,
675
+
676
+ // Viem helpers
677
+ createNodeWalletClient,
678
+
679
+ // Chain configs
680
+ campMainnet,
681
+ campTestnet,
682
+ } from "@campnetwork/origin";
683
+ ```
684
+
685
+ ### Examples
686
+
687
+ See the [examples/server-side](./examples/server-side) directory for complete Node.js examples including:
688
+
689
+ - `connect-with-ethers.js` - Using ethers v6 Signer
690
+ - `connect-with-viem.js` - Using viem WalletClient
691
+ - `connect-with-custom-signer.js` - Custom signer implementation
692
+ - `query-origin-data.js` - Querying blockchain data
693
+
694
+ # React
695
+
696
+ The React components and hooks can be imported as ES6 modules. The example below shows how to set up the `CampProvider` component and subsequently use the provided hooks and components.
697
+
698
+ ```js
699
+ // main.jsx
700
+ import { StrictMode } from "react";
701
+ import { createRoot } from "react-dom/client";
702
+ import { CampProvider } from "@campnetwork/origin/react";
703
+ import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
704
+ import App from "./App.jsx";
705
+
706
+ const queryClient = new QueryClient();
707
+
708
+ createRoot(document.getElementById("root")).render(
709
+ <StrictMode>
710
+ <QueryClientProvider client={queryClient}>
711
+ <CampProvider clientId="your-client-id">
712
+ <App />
713
+ </CampProvider>
714
+ </QueryClientProvider>
715
+ </StrictMode>
716
+ );
717
+ ```
718
+
719
+ ## CampProvider
720
+
721
+ The `CampProvider` component requires a `clientId` prop to be passed in order to link the users to your app.
722
+ It can also take the following optional props:
723
+
724
+ - `redirectUri` - `string | object` - Either a string that will be used as the redirect URI for all socials, or an object with the following optional properties: `twitter`, `spotify`. This is used to redirect the user to different pages after they have completed the OAuth flow for a social.
725
+ - `environment` - `string` - The environment to use. Can be either `DEVELOPMENT` or `PRODUCTION`. Defaults to `DEVELOPMENT`.
726
+ - - the `DEVELOPMENT` environment uses the Camp Testnet while the `PRODUCTION` environment uses the Camp Mainnet.
727
+ - `baseParentId` - `string | bigint` - A valid tokenID to be used as the parent of all IPNFTs minted on your platform, making them all derivatives of your base asset.
728
+
729
+ ```jsx
730
+ import { CampProvider } from "@campnetwork/origin/react";
731
+ // ...
732
+ function App() {
733
+ return (
734
+ <CampProvider
735
+ clientId="your-client-id"
736
+ redirectUri="https://your-website.com"
737
+ environment="DEVELOPMENT"
738
+ >
739
+ <div>Your app</div>
740
+ </CampProvider>
741
+ );
742
+ }
743
+ ```
744
+
745
+ Or, with an object for the `redirectUri`:
746
+
747
+ ```jsx
748
+ import { CampProvider } from "@campnetwork/origin/react";
749
+ // ...
750
+ function App() {
751
+ return (
752
+ <CampProvider
753
+ clientId="your-client-id"
754
+ redirectUri={{
755
+ twitter: "https://your-website.com/twitter",
756
+ spotify: "https://your-website.com/spotify",
757
+ }}
758
+ environment="DEVELOPMENT"
759
+ >
760
+ <div>Your app</div>
761
+ </CampProvider>
762
+ );
763
+ }
764
+ ```
765
+
766
+ The `CampProvider` component sets up the context for the Origin SDK and provides the Auth instance to the rest of the app.
767
+
768
+ ## CampModal
769
+
770
+ ![@campnetwork/origin](https://imgur.com/AFmorL4.png)
771
+
772
+ The **CampModal** is a one-line\* solution for authenticating users with the Origin SDK. It can be used to connect users to Origin, link and unlink social accounts, mint IPNFTs, and view the user's Origin stats.
773
+
774
+ It works as follows:
775
+
776
+ The **CampModal** component displays a button with the text "**Connect**" that the user can click on in order to summon the modal. The modal shows a list of available providers that the user can select from. After a provider has been selected, the `connect` method is called on the Auth instance to authenticate the user.
777
+
778
+ If the user is already authenticated, the button will instead say "**My Origin**" and the modal will display the user's Origin profile information and allow them to link and unlink social accounts.
779
+
780
+ The **CampModal** can take the following props:
781
+
782
+ - `wcProjectId` - `string` - The WalletConnect project ID to use for authentication. Allows the users to authenticate via WalletConnect.
783
+ - `injectButton` - `boolean` - Whether to inject the button into the DOM or not. Defaults to `true`. If set to `false`, the button will not be rendered and the modal can be opened programmatically via the `openModal` function returned by the `useModal` hook.
784
+ - `onlyWagmi` - `boolean` - Whether to only show the provider that the user is currently authenticated with. Defaults to `false`.
785
+ - `defaultProvider` - `{ provider: EIP1193Provider, info: EIP6963ProviderInfo, exclusive: boolean }` - Custom provider to set as the highlighted provider in the modal. If not set, the wagmi provider will be highlighted if it is available. The `exclusive` property can be set to `true` to only show this provider in the modal.
786
+
787
+ ### Usage
788
+
789
+ Basic usage of the **CampModal** component:
790
+
791
+ ```jsx
792
+ import { CampModal } from "@campnetwork/origin/react";
793
+
794
+ function App() {
795
+ return (
796
+ <div>
797
+ <CampModal />
798
+ </div>
799
+ );
800
+ }
801
+ ```
802
+
803
+ With custom props:
804
+
805
+ ```jsx
806
+ import { CampModal } from "@campnetwork/origin/react";
807
+
808
+ function App() {
809
+ return (
810
+ <div>
811
+ <CampModal
812
+ wcProjectId="your-wc-project-id"
813
+ defaultProvider={{
814
+ provider: window.ethereum,
815
+ info: { name: "MetaMask", icon: "https://..." },
816
+ exclusive: false,
817
+ }}
818
+ />
819
+ </div>
820
+ );
821
+ }
822
+ ```
823
+
824
+ You can find more [examples here](./examples/client-side/react/providers-configuration).
825
+
826
+ Only show the provider that the user is currently authenticated with (if using wagmi):
827
+
828
+ ```jsx
829
+ import { CampModal } from "@campnetwork/origin/react";
830
+
831
+ function App() {
832
+ return (
833
+ <div>
834
+ <CampModal onlyWagmi />
835
+ </div>
836
+ );
837
+ }
838
+ ```
839
+
840
+ Users can be authenticated either via the Camp Modal as outlined above or programmatically by calling the `connect` method on the Auth instance.
841
+
842
+ ### Usage with third party providers (Privy, Appkit, Magic, etc.)
843
+
844
+ The Camp Modal can be used in conjunction with providers such as Privy and Appkit to create a seamless authentication experience for users. When using wagmi, it will automatically detect if the user is authenticated via a third party provider and give them the option to connect to Origin using that provider. Otherwise, you can set up the default provider to be whatever provider you are using.
845
+
846
+ [Example usage with Privy](./examples/client-side/react/privy-connector/)
847
+
848
+ [Example usage with Appkit](./examples/client-side/react/appkit-connector/)
849
+
850
+ [Example usage with magic.link](./examples/client-side/react/magic-link-connector/)
851
+
852
+ After the user has authenticated, you can use the provided hooks to fetch user data and listen for events.
853
+
854
+ ## LinkButton
855
+
856
+ The **LinkButton** component is a button that can be used to link and unlink social accounts. Under the hood it uses the `useLinkModal` hook to open the Link Socials modal.
857
+
858
+ The **LinkButton** can take the following props:
859
+
860
+ - `social` - `string` - The social account to link or unlink. Can be one of: `twitter`, `tiktok`, `spotify`.
861
+ - `variant` - `string` - The variant of the button. Can be one of: `default`, `icon`. Defaults to `default`.
862
+ - `theme` - `string` - The theme of the button. Can be one of: `default`, `camp`. Defaults to `default`.
863
+
864
+ **Note: The `<CampModal/>` component must be rendered in the component tree for the buttons to work.**
865
+
866
+ ### Usage
867
+
868
+ Basic usage of the **LinkButton** component:
869
+
870
+ ```jsx
871
+ import { LinkButton, CampModal } from "@campnetwork/origin/react";
872
+
873
+ function App() {
874
+ return (
875
+ <div>
876
+ <CampModal />
877
+ <LinkButton social="twitter" />
878
+ <LinkButton social="spotify" theme="camp" />
879
+ <LinkButton social="tiktok" variant="icon" theme="camp" />
880
+ </div>
881
+ );
882
+ }
883
+ ```
884
+
885
+ ## CampButton
886
+
887
+ The **CampButton** component allows you to render a button that opens the Auth or My Origin modal when clicked. It can be used as an alternative to the button that is injected by the **CampModal** component. It allows you to have multiple buttons in your app that open the modal, or to have the button in a different location than where the **CampModal** component is rendered.
888
+
889
+ ```jsx
890
+ import { CampButton, CampModal } from "@campnetwork/origin/react";
891
+
892
+ function App() {
893
+ return (
894
+ <div>
895
+ <CampModal injectButton={false} />
896
+ <CampButton />
897
+ </div>
898
+ );
899
+ }
900
+ ```
901
+
902
+ ## Hooks
903
+
904
+ ### useAuth
905
+
906
+ The `useAuth` hook returns the instance of the Auth class that is provided by the CampProvider.
907
+ It can be used as outlined in the Core section in order to build custom authentication flows, listen for events, and fetch user data.
908
+
909
+ ```jsx
910
+ import { useAuth } from "@campnetwork/origin/react";
911
+
912
+ function App() {
913
+ const auth = useAuth();
914
+
915
+ return (
916
+ <div>
917
+ <button onClick={auth.connect}>Connect</button>
918
+ </div>
919
+ );
920
+ }
921
+ ```
922
+
923
+ ### useAuthState
924
+
925
+ The `useAuthState` hook returns the current authentication state of the user.
926
+
927
+ ```jsx
928
+ import { useAuthState } from "@campnetwork/origin/react";
929
+
930
+ function App() {
931
+ const { authenticated, loading } = useAuthState();
932
+
933
+ return (
934
+ <div>
935
+ {loading && <div>Loading...</div>}
936
+ {authenticated && <div>Authenticated</div>}
937
+ </div>
938
+ );
939
+ }
940
+ ```
941
+
942
+ ### useProvider
943
+
944
+ The `useProvider` hook returns the provider that has been set via the `setProvider` method, as well as a `setProvider` function that can be used to update the provider.
945
+
946
+ ```jsx
947
+ import { useProvider } from "@campnetwork/origin/react";
948
+
949
+ function App() {
950
+ const { provider, setProvider } = useProvider();
951
+
952
+ return (
953
+ <div>
954
+ <div>Current provider: {provider.info.name}</div>
955
+ <button
956
+ onClick={() =>
957
+ setProvider({ provider: window.ethereum, info: { name: "Metamask" } })
958
+ }
959
+ >
960
+ Set Provider
961
+ </button>
962
+ </div>
963
+ );
964
+ }
965
+ ```
966
+
967
+ ### useProviders
968
+
969
+ The `useProviders` hook returns the list of providers that have been injected via EIP6963 and that the user or app can select from.
970
+
971
+ ```jsx
972
+ import { useProviders, useProvider } from "@campnetwork/origin/react";
973
+
974
+ function App() {
975
+ const providers = useProviders();
976
+ const { setProvider } = useProvider();
977
+
978
+ return (
979
+ <div>
980
+ {providers.map((provider) => (
981
+ <button key={provider.info.name} onClick={() => setProvider(provider)}>
982
+ {provider.info.name}
983
+ </button>
984
+ ))}
985
+ </div>
986
+ );
987
+ }
988
+ ```
989
+
990
+ ### useConnect
991
+
992
+ The `useConnect` hook returns functions that can be used to connect and disconnect the user.
993
+
994
+ ```jsx
995
+ import { useConnect, useAuthState } from "@campnetwork/origin/react";
996
+
997
+ function App() {
998
+ const { connect, disconnect } = useConnect();
999
+ const { authenticated } = useAuthState();
1000
+
1001
+ return (
1002
+ <div>
1003
+ {authenticated ? (
1004
+ <button onClick={disconnect}>Disconnect</button>
1005
+ ) : (
1006
+ <button onClick={connect}>Connect</button>
1007
+ )}
1008
+ </div>
1009
+ );
1010
+ }
1011
+ ```
1012
+
1013
+ ### useSocials
1014
+
1015
+ The `useSocials` hook returns the state of the user's linked social accounts.
1016
+
1017
+ ```jsx
1018
+ import { useSocials } from "@campnetwork/origin/react";
1019
+
1020
+ function App() {
1021
+ const { data, error, isLoading } = useSocials();
1022
+
1023
+ if (loading) return <div>Loading...</div>;
1024
+ if (error) return <div>Error: {error.message}</div>;
1025
+
1026
+ return (
1027
+ <div>
1028
+ <div>Twitter: {data.twitter ? "Linked" : "Not linked"}</div>
1029
+ <div>Tiktok: {data.tiktok ? "Linked" : "Not linked"}</div>
1030
+ <div>Spotify: {data.spotify ? "Linked" : "Not linked"}</div>
1031
+ </div>
1032
+ );
1033
+ }
1034
+ ```
1035
+
1036
+ ### useLinkSocials
1037
+
1038
+ The `useLinkSocials` hook returns functions that can be used to link and unlink social accounts.
1039
+
1040
+ ```jsx
1041
+ import { useLinkSocials } from "@campnetwork/origin/react";
1042
+
1043
+ function App() {
1044
+ const {
1045
+ linkTwitter,
1046
+ linkSpotify,
1047
+ linkTiktok,
1048
+ unlinkTwitter,
1049
+ unlinkSpotify,
1050
+ unlinkTiktok,
1051
+ } = useLinkSocials();
1052
+
1053
+ return (
1054
+ <div>
1055
+ <button onClick={linkTwitter}>Link Twitter</button>
1056
+ <button onClick={linkSpotify}>Link Spotify</button>
1057
+ <button onClick={() => linkTiktok("tiktokhandle")}>Link TikTok</button>
1058
+ </button>
1059
+ <button onClick={unlinkTwitter}>Unlink Twitter</button>
1060
+ <button onClick={unlinkTiktok}>Unlink TikTok</button>
1061
+ <button onClick={unlinkSpotify}>Unlink Spotify</button>
1062
+ </div>
1063
+ );
1064
+ }
1065
+ ```
1066
+
1067
+ ### useModal
1068
+
1069
+ The `useModal` hook returns the state of the Auth and My Origin modals, as well as functions to show and hide them.
1070
+
1071
+ **Note: The `<CampModal/>` component must be rendered in the component tree for the modals to be displayed.**
1072
+
1073
+ ```jsx
1074
+ import { useModal, CampModal } from "@campnetwork/origin/react";
1075
+
1076
+ function App() {
1077
+ const { isOpen, openModal, closeModal } = useModal();
1078
+
1079
+ return (
1080
+ <div>
1081
+ <button onClick={openModal}>Open Modal</button>
1082
+ <button onClick={closeModal}>Close Modal</button>
1083
+ <CampModal injectButton={false} />
1084
+ </div>
1085
+ );
1086
+ }
1087
+ ```
1088
+
1089
+ The state and functions returned by the `useModal` hook can be used to show and hide the Auth and My Origin modals, as well as to check if they are currently open. The modal being controlled is dictated by the user's authentication state.
1090
+
1091
+ ### useLinkModal
1092
+
1093
+ The `useLinkModal` hook returns the state of the Link Socials modal, as well as functions to show and hide it.
1094
+
1095
+ **Note: The `<CampModal/>` component must be rendered in the component tree for the modal to be displayed.**
1096
+
1097
+ ```jsx
1098
+ import { useLinkModal, CampModal } from "@campnetwork/origin/react";
1099
+
1100
+ function App() {
1101
+ const { isLinkingOpen, openTwitterModal } = useLinkModal();
1102
+
1103
+ return (
1104
+ <div>
1105
+ <CampModal />
1106
+ <button onClick={openTwitterModal}>Link Twitter</button>
1107
+ </div>
1108
+ );
1109
+ }
1110
+ ```
1111
+
1112
+ It returns the following properties and functions:
1113
+
1114
+ - `isLinkingOpen` - `boolean` - Whether the Link Socials modal is open or not.
1115
+ - `openTwitterModal` - `() => void`
1116
+ - `openSpotifyModal` - `() => void`
1117
+ - `openTiktokModal` - `() => void`
1118
+ - `linkTwitter` - `() => void`
1119
+ - `linkSpotify` - `() => void`
1120
+ - `linkTiktok` - `() => void`
1121
+ - `linkTelegram` - `() => void`
1122
+ - `unlinkTwitter` - `() => void`
1123
+ - `unlinkSpotify` - `() => void`
1124
+ - `unlinkTiktok` - `() => void`
1125
+ - `closeModal` - `() => void`
1126
+
1127
+ The difference between the `openXModal` functions and the `linkX / unlinkX` functions is that the former opens the modal regardless of the user's linking state, allowing them to either link or unlink their account, while the latter only opens the specified modal if the user's linking state allows for it.
1128
+
1129
+ For example, if the user is linked to Twitter, calling `openTwitterModal` will open the modal to _unlink_ their Twitter account, while calling `linkTwitter` will not do anything, and calling `unlinkTwitter` will open the modal to unlink their Twitter account.
1130
+
1131
+ ## Origin Methods (`auth.origin`)
1132
+
1133
+ The `Origin` class provides blockchain and API methods for interacting with Origin IpNFTs, uploading files, managing user stats, and more. Access these via `auth.origin` after authentication.
1134
+
1135
+ ### Types
1136
+
1137
+ #### `LicenseTerms`
1138
+
1139
+ The license terms object used in minting and updating methods:
1140
+
1141
+ ```typescript
1142
+ type LicenseTerms = {
1143
+ price: bigint; // Price in wei
1144
+ duration: number; // Duration in seconds
1145
+ royaltyBps: number; // Royalty in basis points (1-10000)
1146
+ paymentToken: Address; // Payment token address (address(0) for native currency)
1147
+ };
1148
+ ```
1149
+
1150
+ ### Minting Constraints
1151
+
1152
+ When minting or updating an IpNFT, the following constraints apply to the `LicenseTerms`:
1153
+
1154
+ - The price must be at least `1000000000000000` wei (0.001 $CAMP).
1155
+ - The royaltyBps must be between `1` and `10000` (0.01% to 100%).
1156
+ - The duration must be between `86400` seconds and `2628000` seconds (1 day to 30 days).
1157
+
1158
+ ### `createLicenseTerms(price, duration, royaltyBps, paymentToken)`
1159
+
1160
+ A utility function to create properly validated license terms for minting and updating IpNFTs.
1161
+
1162
+ - `price`: Price in wei (bigint)
1163
+ - `duration`: Duration in seconds (number)
1164
+ - `royaltyBps`: Royalty in basis points (number)
1165
+ - `paymentToken`: Payment token address (Address) - use `zeroAddress` from viem for native currency
1166
+ - **Returns:** A validated `LicenseTerms` object
1167
+ - **Throws:** Error if any parameter violates the constraints
1168
+
1169
+ **Example:**
1170
+
1171
+ ```typescript
1172
+ import { createLicenseTerms } from "@campnetwork/origin";
1173
+ import { zeroAddress } from "viem";
1174
+
1175
+ // Create license terms with validation
1176
+ const license = createLicenseTerms(
1177
+ BigInt("1000000000000000"), // 0.001 CAMP in wei
1178
+ 86400, // 1 day in seconds
1179
+ 1000, // 10% royalty (1000 basis points)
1180
+ zeroAddress // Native currency (CAMP)
1181
+ );
1182
+
1183
+ // Use with minting functions
1184
+ await auth.origin.mintFile(file, metadata, license);
1185
+ await auth.origin.mintSocial("twitter", metadata, license);
1186
+ ```
1187
+
1188
+ ### File Upload & Minting
1189
+
1190
+ #### `mintFile(file, metadata, license, parents?, options?)`
1191
+
1192
+ Uploads a file and mints an IpNFT for it.
1193
+
1194
+ - `file`: File to upload and mint
1195
+ - `metadata`: Additional metadata for the IpNFT
1196
+ - `name`: Name of the IpNFT
1197
+ - `description`: Description of the IpNFT
1198
+ - `image`: Optional image URL for the IpNFT
1199
+ - `attributes`: Optional array of attributes
1200
+ - `license`: LicenseTerms object
1201
+ - `parents`: Optional array of parent token IDs for derivatives
1202
+ - `options.progressCallback`: Optional progress callback
1203
+ - **Returns:** Minted token ID as a string, or throws on failure
1204
+
1205
+ #### `mintSocial(source, metadata, license)`
1206
+
1207
+ Mints an IpNFT for a connected social account.
1208
+
1209
+ - `source`: Social platform (`"spotify" | "twitter" | "tiktok"`)
1210
+ - `metadata`: Additional metadata for the IpNFT
1211
+ - `license`: LicenseTerms object
1212
+ - **Returns:** Minted token ID as a string, or throws on failure
1213
+
1214
+ ### IpNFT & Marketplace Methods
1215
+
1216
+ Most methods mirror smart contract functions and require appropriate permissions.
1217
+
1218
+ #### Core IpNFT Methods
1219
+
1220
+ - `mintWithSignature(account, tokenId, parents, creatorContentHash, uri, license, deadline, signature)`
1221
+ - `registerIpNFT(source, deadline, license, metadata, fileKey?, parents?)`
1222
+ - `updateTerms(tokenId, license)`
1223
+ - `finalizeDelete(tokenId)`
1224
+ - `getOrCreateRoyaltyVault(tokenId)`
1225
+ - `getTerms(tokenId)`
1226
+ - `ownerOf(tokenId)`
1227
+ - `balanceOf(owner)`
1228
+ - `tokenURI(tokenId)`
1229
+ - `dataStatus(tokenId)`
1230
+ - `isApprovedForAll(owner, operator)`
1231
+ - `transferFrom(from, to, tokenId)`
1232
+ - `safeTransferFrom(from, to, tokenId)`
1233
+ - `approve(to, tokenId)`
1234
+ - `setApprovalForAll(operator, approved)`
1235
+
1236
+ #### Marketplace Methods
1237
+
1238
+ - `buyAccess(buyer, tokenId, expectedPrice, expectedDuration, expectedPaymentToken, value?)`
1239
+ - `hasAccess(tokenId, user)`
1240
+ - `subscriptionExpiry(tokenId, user)`
1241
+
1242
+ #### Utility & Royalty Methods
1243
+
1244
+ - `getRoyalties(token?, owner?)` Get royalty vault and balance
1245
+ - `claimRoyalties(token?, owner?)` — Claim royalties
1246
+
1247
+ #### Smart Access & Data
1248
+
1249
+ - `buyAccessSmart(tokenId)` Buys access, handles payment approval and license details - **Recommended in place of buyAccess**
1250
+ - `getData(tokenId)` — Fetches the underlying IP for a given IPNFT if the user has purchased access to it
1251
+
1252
+ ### User Data & Stats
1253
+
1254
+ - `getOriginUploads()` — Fetch user's Origin file uploads (returns array or null)
1255
+ - `getOriginUsage()` — Fetch user's Origin stats (returns object with multiplier, points, active, teams, dataSources)
1256
+ - `setOriginConsent(consent: boolean)` — Set user's consent for Origin usage
1257
+
1258
+ ### Utility Methods
1259
+
1260
+ - `getJwt()` — Get current JWT token
1261
+ - `setViemClient(client)` — Set viem wallet client for blockchain interactions
1262
+
1263
+ ---
1264
+
1265
+ Call these methods as `await auth.origin.methodName(...)` after authenticating. See inline code documentation for full details and parameter types.
1266
+
1267
+ ---
1268
+
1269
+ # Advanced flows
1270
+
1271
+ ## App Revenue Share in Origin using derivatives
1272
+
1273
+ You can enable app-level revenue sharing in **Origin** using the following setup:
1274
+
1275
+ 1. **Mint a base (genesis) IPNFT** for your platform and set its royalty percentage to whatever share you want the platform to receive. This can be done using the Origin UI, or the SDK.
1276
+ 2. **Make all user-created IPNFTs derivatives** of this base IPNFT. There are two ways to do this:
1277
+ - **Automatic (recommended):**
1278
+ Set the `baseParentId` parameter on the `CampProvider` or `Auth` class (depending on your integration) to the tokenId of the base IPNFT.
1279
+ → This automatically applies to _all mints_, including those made through the **Camp SDK Modal**.
1280
+ - **Manual:**
1281
+ If you’re using a custom mint flow, add the base IPNFT’s tokenId to the `parents` array before calling `origin.mintFile` or `origin.mintSocial`.
1282
+ Note: this does **not** affect mints made through the Camp SDK Modal.
1283
+ 3. **Royalties flow automatically:**
1284
+
1285
+ The royalty percentage defined on the base IPNFT will be collected from every subscription to its derivative IPNFTs.
1286
+
1287
+ 4. **Claim your platform royalties:**
1288
+
1289
+ You can view and claim the accumulated platform royalties directly from your **Creator Dashboard** in the **Origin UI**.
1290
+
1291
+ **Note:** Each IPNFT can have a maximum of 8 parents. Using one parent slot for app-level revenue sharing reduces the available slots for user-defined parent–derivative relationships to 7.
1292
+
1293
+ ---
1294
+
1295
+ # Contributing
1296
+
1297
+ Install the dependencies.
1298
+
1299
+ ```bash
1300
+ npm install
1301
+ ```
1302
+
1303
+ Build the SDK.
1304
+
1305
+ ```bash
1306
+ npm run build
1307
+ ```
1308
+
1309
+ This will generate the SDK in the `dist` folder.
1310
+
1311
+ You can also run the following command to watch for changes and rebuild the SDK automatically:
1312
+
1313
+ ```bash
1314
+ npm run dev
1315
+ ```
1316
+
1317
+ In order to use the sdk in a local project, you can link the sdk to the project.
1318
+
1319
+ ```bash
1320
+ npm link .
1321
+ ```
1322
+
1323
+ Then, in the project you want to use the sdk in, run:
1324
+
1325
+ ```bash
1326
+ npm link @campnetwork/origin
1327
+ ```
1328
+
1329
+ This will link the local sdk to the project.