├── .changeset
├── README.md
├── config.json
├── nice-baboons-protect.md
└── thirty-experts-tell.md
├── .editorconfig
├── .env.example
├── .github
├── actions
│ ├── setup
│ │ └── action.yml
│ └── tests
│ │ └── action.yml
└── workflows
│ ├── pull-request.yml
│ ├── snapshot.yml
│ ├── trigger-tests.yml
│ └── verify.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .prettierignore
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── biome.json
├── examples
├── create-app
│ ├── README.md
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── custom-fragments
│ ├── README.md
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── eoa-only
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── lens.svg
│ ├── src
│ │ ├── App.tsx
│ │ ├── ConnectButton.tsx
│ │ ├── DisconnectButton.tsx
│ │ ├── Web3Providers.tsx
│ │ ├── client.ts
│ │ ├── config.ts
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── nextjs-client
│ ├── .gitignore
│ ├── .stackblitzrc
│ ├── README.md
│ ├── next.config.ts
│ ├── package.json
│ ├── src
│ │ └── app
│ │ │ ├── client.ts
│ │ │ ├── favicon.ico
│ │ │ ├── globals.css
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
│ └── tsconfig.json
├── on-ramp
│ ├── README.md
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── react-login
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── lens.svg
│ ├── src
│ │ ├── App.tsx
│ │ ├── LoginForm.tsx
│ │ ├── LogoutButton.tsx
│ │ ├── MyAccount.tsx
│ │ ├── Web3Providers.tsx
│ │ ├── client.ts
│ │ ├── config.ts
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── react-post
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── lens.svg
│ ├── src
│ │ ├── App.tsx
│ │ ├── client.ts
│ │ ├── config.ts
│ │ ├── main.tsx
│ │ ├── vite-env.d.ts
│ │ └── wallet.ts
│ ├── tsconfig.json
│ └── vite.config.ts
├── sponsored-tx-poc
│ ├── README.md
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── sponsored-tx
│ ├── .stackblitzrc
│ ├── README.md
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── thirdweb-onramp
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── lens.svg
│ ├── src
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── tsconfig.json
│ └── vite.config.ts
└── user-onboarding
│ ├── README.md
│ ├── index.html
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── jest-extended.d.ts
├── package.json
├── packages
├── client
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── AuthenticatedUser.ts
│ │ ├── actions
│ │ │ ├── account.integration.test.ts
│ │ │ ├── account.ts
│ │ │ ├── accountManager.integration.test.ts
│ │ │ ├── accountManager.ts
│ │ │ ├── actions.ts
│ │ │ ├── admins.ts
│ │ │ ├── app.ts
│ │ │ ├── authentication.ts
│ │ │ ├── feed.ts
│ │ │ ├── follow.ts
│ │ │ ├── frames.ts
│ │ │ ├── funds.e2e.test.ts
│ │ │ ├── funds.ts
│ │ │ ├── graph.ts
│ │ │ ├── group.ts
│ │ │ ├── helpers.ts
│ │ │ ├── index.ts
│ │ │ ├── metadata.integration.test.ts
│ │ │ ├── metadata.ts
│ │ │ ├── misc.ts
│ │ │ ├── ml.integration.test.ts
│ │ │ ├── ml.ts
│ │ │ ├── namespace.ts
│ │ │ ├── notifications.integration.test.ts
│ │ │ ├── notifications.ts
│ │ │ ├── onboarding.e2e.test.ts
│ │ │ ├── post.integration.test.ts
│ │ │ ├── post.ts
│ │ │ ├── posts.integration.test.ts
│ │ │ ├── posts.ts
│ │ │ ├── sns.ts
│ │ │ ├── sponsorship.ts
│ │ │ ├── timeline.ts
│ │ │ ├── tipping.e2e.test.ts
│ │ │ ├── transactions.ts
│ │ │ ├── transfer.ts
│ │ │ └── username.ts
│ │ ├── authorization.ts
│ │ ├── batch.ts
│ │ ├── clients.integration.test.ts
│ │ ├── clients.ts
│ │ ├── config.ts
│ │ ├── context.ts
│ │ ├── crossRegion.e2e.test.ts
│ │ ├── errors.ts
│ │ ├── ethers
│ │ │ ├── index.ts
│ │ │ ├── signer.integration.test.ts
│ │ │ ├── signer.ts
│ │ │ ├── sponsorship.integration.test.ts
│ │ │ └── sponsorship.ts
│ │ ├── fragments.ts
│ │ ├── fragments.unit.test.ts
│ │ ├── index.ts
│ │ ├── logger.ts
│ │ ├── sponsorship.ts
│ │ ├── test-utils.ts
│ │ ├── tokens.ts
│ │ ├── types.ts
│ │ ├── utils.ts
│ │ └── viem
│ │ │ ├── authorization.test.ts
│ │ │ ├── authorization.ts
│ │ │ ├── index.ts
│ │ │ ├── signer.test.ts
│ │ │ ├── signer.ts
│ │ │ ├── sponsorship.test.ts
│ │ │ ├── sponsorship.ts
│ │ │ └── types.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsup.config.ts
├── env
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsup.config.ts
├── graphql
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ ├── schema.graphql
│ ├── src
│ │ ├── accounts
│ │ │ ├── account.ts
│ │ │ ├── index.ts
│ │ │ ├── managers.ts
│ │ │ └── signless.ts
│ │ ├── actions.ts
│ │ ├── admins.ts
│ │ ├── app.ts
│ │ ├── authentication.ts
│ │ ├── common.ts
│ │ ├── enums.ts
│ │ ├── feed.ts
│ │ ├── follow.ts
│ │ ├── fragments
│ │ │ ├── account.ts
│ │ │ ├── common.ts
│ │ │ ├── index.ts
│ │ │ ├── media.ts
│ │ │ ├── metadata.ts
│ │ │ ├── pagination.ts
│ │ │ ├── post.ts
│ │ │ ├── primitives.ts
│ │ │ ├── transactions.ts
│ │ │ └── username.ts
│ │ ├── frames.ts
│ │ ├── funds.ts
│ │ ├── graph.ts
│ │ ├── graphql-env.d.ts
│ │ ├── graphql.ts
│ │ ├── group.ts
│ │ ├── index.ts
│ │ ├── metadata.ts
│ │ ├── misc.ts
│ │ ├── ml.ts
│ │ ├── namespace.ts
│ │ ├── notifications.ts
│ │ ├── post.ts
│ │ ├── refinements.ts
│ │ ├── scalars.ts
│ │ ├── schema.d.ts
│ │ ├── sns.ts
│ │ ├── sponsorship.ts
│ │ ├── test-utils.ts
│ │ ├── timeline.ts
│ │ ├── transactions.ts
│ │ ├── transferOwnership.ts
│ │ └── username.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsup.config.ts
├── react
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── LensProvider.tsx
│ │ ├── account
│ │ │ ├── index.ts
│ │ │ ├── useAccount.ts
│ │ │ ├── useAccountManagers.ts
│ │ │ ├── useAccounts.ts
│ │ │ ├── useAccountsBlocked.ts
│ │ │ └── useAccountsBulk.ts
│ │ ├── authentication
│ │ │ ├── index.ts
│ │ │ ├── useAccountsAvailable.ts
│ │ │ ├── useAuthenticatedUser.integration.test.ts
│ │ │ ├── useAuthenticatedUser.ts
│ │ │ ├── useImpersonation.ts
│ │ │ ├── useLogin.integration.test.ts
│ │ │ ├── useLogin.ts
│ │ │ ├── useLogout.ts
│ │ │ ├── usePublicClient.ts
│ │ │ ├── useSessionClient.integration.test.ts
│ │ │ ├── useSessionClient.ts
│ │ │ └── useSwitchAccount.ts
│ │ ├── context.tsx
│ │ ├── ethers.ts
│ │ ├── feed
│ │ │ ├── index.ts
│ │ │ └── useFeed.ts
│ │ ├── follow
│ │ │ ├── index.ts
│ │ │ ├── useFollowers.ts
│ │ │ ├── useFollowersYouKnow.ts
│ │ │ └── useFollowing.ts
│ │ ├── graph
│ │ │ ├── index.ts
│ │ │ └── useGraph.ts
│ │ ├── group
│ │ │ ├── index.ts
│ │ │ ├── useGroup.ts
│ │ │ ├── useGroupMembers.ts
│ │ │ └── useGroups.ts
│ │ ├── helpers
│ │ │ ├── index.ts
│ │ │ ├── reads.ts
│ │ │ ├── results.ts
│ │ │ ├── tasks.integration.test.ts
│ │ │ └── tasks.ts
│ │ ├── index.ts
│ │ ├── notification
│ │ │ ├── index.ts
│ │ │ └── useNotifications.ts
│ │ ├── post
│ │ │ ├── index.ts
│ │ │ ├── useCreatePost.integration.test.ts
│ │ │ ├── useCreatePost.ts
│ │ │ ├── usePost.ts
│ │ │ ├── usePostBookmarks.ts
│ │ │ ├── usePostReactions.ts
│ │ │ ├── usePostReferences.ts
│ │ │ └── usePosts.ts
│ │ ├── test-utils.tsx
│ │ ├── timeline
│ │ │ ├── index.ts
│ │ │ ├── useTimeline.ts
│ │ │ └── useTimelineHighlights.ts
│ │ ├── username
│ │ │ ├── index.ts
│ │ │ ├── useNamespace.ts
│ │ │ └── useUsernames.ts
│ │ └── viem.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── tsup.config.ts
│ └── vitest.setup.ts
├── storage
│ ├── README.md
│ ├── package.json
│ ├── src
│ │ ├── BaseStorageSchema.ts
│ │ ├── CredentialsStorageSchema.ts
│ │ ├── IStorage.ts
│ │ ├── InMemoryStorageProvider.ts
│ │ ├── Storage.test.ts
│ │ ├── Storage.ts
│ │ ├── __helpers__
│ │ │ └── mocks.ts
│ │ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsup.config.ts
└── types
│ ├── README.md
│ ├── package.json
│ ├── src
│ ├── errors.ts
│ ├── helpers
│ │ ├── Deferred.ts
│ │ ├── assertions.ts
│ │ ├── fail.ts
│ │ ├── identity.ts
│ │ ├── index.ts
│ │ ├── invariant.ts
│ │ ├── never.ts
│ │ ├── refinements.ts
│ │ └── typeguards.ts
│ ├── hex.ts
│ ├── id.ts
│ ├── index.ts
│ ├── jwt.ts
│ ├── misc.ts
│ ├── number.ts
│ ├── tag.ts
│ └── uri.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsup.config.ts
├── plopfile.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── renovate.json
├── templates
├── example-react
│ ├── README.md
│ ├── index.html
│ ├── package.json
│ ├── public
│ │ └── lens.svg
│ ├── src
│ │ ├── App.tsx
│ │ ├── client.ts
│ │ ├── main.tsx
│ │ └── vite-env.d.ts
│ ├── tsconfig.json
│ └── vite.config.ts
└── lib
│ ├── README.md.hbs
│ ├── package.json.hbs
│ ├── src
│ └── index.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsup.config.ts
├── tsconfig.base.json
├── tsconfig.json
├── turbo.json
├── vite-env.d.ts
├── vitest.config.ts
├── vitest.d.ts
└── vitest.setup.ts
/.changeset/README.md:
--------------------------------------------------------------------------------
1 | # Changesets
2 |
3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4 | with multi-package repos, or single-package repos to help you version and publish your code. You can
5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6 |
7 | We have a quick list of common questions to get you started engaging with this project in
8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
9 |
--------------------------------------------------------------------------------
/.changeset/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://unpkg.com/@changesets/config@3.0.4/schema.json",
3 | "changelog": "@changesets/cli/changelog",
4 | "commit": false,
5 | "fixed": [["@lens-protocol/client", "@lens-protocol/react"]],
6 | "linked": [],
7 | "access": "public",
8 | "baseBranch": "next",
9 | "updateInternalDependencies": "patch",
10 | "ignore": []
11 | }
12 |
--------------------------------------------------------------------------------
/.changeset/nice-baboons-protect.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@lens-protocol/client": major
3 | "@lens-protocol/react": major
4 | "@lens-protocol/storage": major
5 | "@lens-protocol/env": minor
6 | "@lens-protocol/graphql": minor
7 | "@lens-protocol/types": minor
8 | ---
9 |
10 | **chore**: first changeset
11 |
--------------------------------------------------------------------------------
/.changeset/thirty-experts-tell.md:
--------------------------------------------------------------------------------
1 | ---
2 | "@lens-protocol/client": patch
3 | ---
4 |
5 | Fix incorrect destructuring of sponsored transactions `customData` in ethers client
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | insert_final_newline = true
6 | end_of_line = lf
7 | indent_style = space
8 | indent_size = 2
9 | max_line_length = 100
10 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | PRIVATE_KEY=
2 | TEST_ACCOUNT=
3 | TEST_APP=0x6F6AC764c218C57a509De5E05013EEdA8c9264E0
4 | TEST_ERC20=0xeee5a340Cdc9c179Db25dea45AcfD5FE8d4d3eB8
5 | ENVIRONMENT=local
6 | GLOBAL_SPONSORSHIP=
7 | SPONSORSHIP_APPROVER_PRIVATE_KEY=
8 |
--------------------------------------------------------------------------------
/.github/actions/setup/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Setup'
2 | description: 'Setup repo and install dependencies'
3 |
4 | runs:
5 | using: 'composite'
6 | steps:
7 | - name: Setup pnpm
8 | uses: pnpm/action-setup@v4
9 |
10 | - name: Setup Node.js
11 | uses: actions/setup-node@v4
12 | with:
13 | node-version-file: '.nvmrc'
14 | cache: 'pnpm'
15 |
16 | - name: Install Dependencies
17 | shell: bash
18 | run: pnpm install --frozen-lockfile
19 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | branches: [next]
6 |
7 | concurrency:
8 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
9 | cancel-in-progress: true
10 |
11 | jobs:
12 | verify:
13 | name: Verify
14 | uses: ./.github/workflows/verify.yml
15 | secrets: inherit
16 |
--------------------------------------------------------------------------------
/.github/workflows/snapshot.yml:
--------------------------------------------------------------------------------
1 | name: Canary Release
2 |
3 | on:
4 | push:
5 | branches: [next]
6 |
7 | concurrency:
8 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
9 | cancel-in-progress: true
10 |
11 | jobs:
12 | verify:
13 | name: Verify
14 | uses: ./.github/workflows/verify.yml
15 | secrets: inherit
16 |
17 | publish:
18 | name: Publish Snapshot
19 | runs-on: ubuntu-latest
20 | needs: verify
21 |
22 | steps:
23 | - uses: actions/checkout@v4
24 |
25 | - name: Setup Repository
26 | uses: ./.github/actions/setup
27 |
28 | - name: Configure NPM Auth Token
29 | run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NODE_AUTH_TOKEN }}" >> ~/.npmrc
30 |
31 | - name: Create Snapshot
32 | id: create_snapshot
33 | run: pnpm changeset version --snapshot canary || echo "No unreleased changesets found"
34 |
35 | - name: Check for Changeset
36 | id: check_changeset
37 | run: echo "changeset_exists=$(grep -q 'No unreleased changesets found' <<< '${{ steps.create_snapshot.outputs.stdout }}' && echo false || echo true)" >> $GITHUB_ENV
38 |
39 | - name: Build
40 | if: env.changeset_exists == 'true'
41 | run: pnpm build
42 |
43 | - name: Publish Snapshot
44 | if: env.changeset_exists == 'true'
45 | run: pnpm changeset publish --snapshot --tag canary --no-git-tag
46 |
--------------------------------------------------------------------------------
/.github/workflows/trigger-tests.yml:
--------------------------------------------------------------------------------
1 | name: Run Tests on Demand
2 |
3 | on:
4 | repository_dispatch:
5 | types: [on-demand-test]
6 | workflow_dispatch:
7 | inputs:
8 | environment:
9 | description: 'Environment to run tests against'
10 | required: true
11 | default: 'staging'
12 | type: choice
13 | options:
14 | - staging
15 | - testnet
16 | publish_results:
17 | description: 'Publish test results'
18 | required: false
19 | default: 'true'
20 | type: string
21 |
22 | jobs:
23 | tests:
24 | name: Full Test Suite
25 | runs-on: ubuntu-latest
26 |
27 | steps:
28 | - uses: actions/checkout@v4
29 |
30 | - name: Run Tests
31 | uses: ./.github/actions/tests
32 | with:
33 | environment: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.environment || github.event.client_payload.lens_env || 'staging' }}
34 | private_key: ${{ secrets.PRIVATE_KEY }}
35 | test_app: ${{ vars.TEST_APP }}
36 | test_account: ${{ vars.TEST_ACCOUNT }}
37 | test_erc20: ${{ vars.TEST_ERC20 }}
38 | global_sponsorship: ${{ secrets.GLOBAL_SPONSORSHIP }}
39 | sponsorship_approver_private_key: ${{ secrets.SPONSORSHIP_APPROVER_PRIVATE_KEY }}
40 | publish_results: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.publish_results || 'true' }}
41 |
--------------------------------------------------------------------------------
/.github/workflows/verify.yml:
--------------------------------------------------------------------------------
1 | name: 'Verify'
2 | on:
3 | workflow_call:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | lint:
8 | name: Lint
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v4
13 |
14 | - name: Setup Biome
15 | uses: biomejs/setup-biome@v2
16 | with:
17 | version: latest
18 |
19 | - name: Run Biome
20 | run: biome ci .
21 |
22 | test:
23 | name: Test
24 | runs-on: ubuntu-latest
25 |
26 | steps:
27 | - uses: actions/checkout@v4
28 |
29 | - name: Run Tests
30 | uses: ./.github/actions/tests
31 | with:
32 | environment: 'staging'
33 | private_key: ${{ secrets.PRIVATE_KEY }}
34 | test_app: ${{ vars.TEST_APP }}
35 | test_account: ${{ vars.TEST_ACCOUNT }}
36 | test_erc20: ${{ vars.TEST_ERC20 }}
37 | global_sponsorship: ${{ secrets.GLOBAL_SPONSORSHIP }}
38 | sponsorship_approver_private_key: ${{ secrets.SPONSORSHIP_APPROVER_PRIVATE_KEY }}
39 | publish_results: 'false'
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | node_modules
5 | .pnp
6 | .pnp.js
7 |
8 | # build output
9 | **/dist
10 | /docs
11 | */graphql-cache.d.ts
12 |
13 | # misc
14 | .DS_Store
15 | *.pem
16 |
17 | # turbo
18 | .turbo
19 |
20 | # lockfiles
21 | package-lock.json
22 | yarn.lock
23 | examples/*/pnpm-lock.yaml
24 |
25 | # debug
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env
30 |
31 | # typescript
32 | *.tsbuildinfo
33 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 | auto-install-peers=false
3 | strict-peer-dependencies=false
4 | link-workspace-packages=false
5 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20.13.1
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # use biome instead
2 | *
3 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[graphql]": {
3 | "editor.defaultFormatter": "biomejs.biome",
4 | "editor.formatOnSave": false
5 | },
6 | "[typescript]": {
7 | "editor.defaultFormatter": "biomejs.biome",
8 | "editor.codeActionsOnSave": {
9 | "source.fixAll": "explicit",
10 | "source.removeUnused.ts": "never",
11 | "source.removeUnusedImports": "never",
12 | "source.organizeImports.biome": "explicit"
13 | },
14 | "editor.formatOnSave": true
15 | },
16 | "[typescriptreact]": {
17 | "editor.defaultFormatter": "biomejs.biome",
18 | "editor.codeActionsOnSave": {
19 | "source.fixAll": "explicit",
20 | "source.removeUnused.ts": "never",
21 | "source.removeUnusedImports": "never",
22 | "source.organizeImports.biome": "explicit"
23 | },
24 | "editor.formatOnSave": true
25 | },
26 | "[json]": {
27 | "editor.defaultFormatter": "biomejs.biome",
28 | "editor.quickSuggestions": {
29 | "strings": true
30 | },
31 | "editor.formatOnSave": true,
32 | "editor.suggest.insertMode": "replace"
33 | },
34 | "disabledExtensions": ["Orta.vscode-jest"],
35 | "typescript.tsdk": "node_modules/typescript/lib",
36 | "typescript.enablePromptUseWorkspaceTsdk": true
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Avara Labs Cayman Holdings SEZC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/examples/create-app/README.md:
--------------------------------------------------------------------------------
1 | # Create an App
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/create-app)
4 |
--------------------------------------------------------------------------------
/examples/create-app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Create an App
10 | Loading...
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/create-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-create-app",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite"
7 | },
8 | "dependencies": {
9 | "@lens-chain/sdk": "latest",
10 | "@lens-protocol/client": "canary",
11 | "@lens-protocol/metadata": "latest",
12 | "@lens-chain/storage-client": "latest",
13 | "viem": "^2.21.55"
14 | },
15 | "devDependencies": {
16 | "typescript": "^5.6.3",
17 | "vite": "^5.4.11"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/create-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ESNext", "DOM"],
6 | "module": "ESNext",
7 | "moduleResolution": "Bundler",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["./"]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/custom-fragments/README.md:
--------------------------------------------------------------------------------
1 | # Custom Fragments
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/custom-fragments)
4 |
--------------------------------------------------------------------------------
/examples/custom-fragments/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Custom Fragments
10 | Loading...
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/custom-fragments/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-custom-fragments",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite"
7 | },
8 | "dependencies": {
9 | "@lens-protocol/client": "canary",
10 | "viem": "^2.21.55"
11 | },
12 | "devDependencies": {
13 | "typescript": "^5.6.3",
14 | "vite": "^5.4.11"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/custom-fragments/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ESNext", "DOM"],
6 | "module": "ESNext",
7 | "moduleResolution": "Bundler",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["./"]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/eoa-only/README.md:
--------------------------------------------------------------------------------
1 | # EOA Only Example
2 |
3 | This is a POC example to explore a new type of integration. Nothing to adopt here yet.
4 |
5 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/eoa-only)
6 |
--------------------------------------------------------------------------------
/examples/eoa-only/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | EOA Only Example
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/eoa-only/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "eoa-only-example",
3 | "description": "EOA Only Example",
4 | "private": true,
5 | "version": "0.0.0",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite"
9 | },
10 | "dependencies": {
11 | "@lens-chain/sdk": "latest",
12 | "@lens-protocol/react": "0.0.0-canary-20250603100701",
13 | "@tanstack/react-query": "^5.63.0",
14 | "connectkit": "^1.9.0",
15 | "react": "^18.3.1",
16 | "react-dom": "^18.3.1",
17 | "viem": "^2.22.4",
18 | "wagmi": "^2.14.6"
19 | },
20 | "devDependencies": {
21 | "@types/react": "^18.3.12",
22 | "@types/react-dom": "^18.3.1",
23 | "@vitejs/plugin-react-swc": "^3.8.1",
24 | "typescript": "^5.6.3",
25 | "vite": "^5.4.9"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/eoa-only/public/lens.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useAuthenticatedUser } from '@lens-protocol/react';
2 | import { ConnectButton } from './ConnectButton';
3 | import { DisconnectButton } from './DisconnectButton';
4 |
5 | export function App() {
6 | const { data } = useAuthenticatedUser({ suspense: true });
7 |
8 | if (data) {
9 | return (
10 | <>
11 |
12 | Connected with {data.address} with {data.role} role.
13 |
14 |
15 | >
16 | );
17 | }
18 |
19 | return ;
20 | }
21 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/ConnectButton.tsx:
--------------------------------------------------------------------------------
1 | import { evmAddress, useImpersonation } from '@lens-protocol/react';
2 |
3 | import { useModal } from 'connectkit';
4 |
5 | export function ConnectButton() {
6 | const { execute } = useImpersonation();
7 | const { setOpen } = useModal({
8 | onConnect: async ({ address }) => {
9 | if (!address) {
10 | throw new Error('Address is required to connect');
11 | }
12 | await execute({ signer: evmAddress(address) });
13 | },
14 | });
15 |
16 | return (
17 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/DisconnectButton.tsx:
--------------------------------------------------------------------------------
1 | import { useLogout } from '@lens-protocol/react';
2 | import { useDisconnect } from 'wagmi';
3 |
4 | export function DisconnectButton() {
5 | const { execute } = useLogout();
6 | const { disconnectAsync } = useDisconnect();
7 |
8 | const onClick = async () => {
9 | await execute();
10 | await disconnectAsync();
11 | };
12 |
13 | return (
14 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/Web3Providers.tsx:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 |
3 | import { LensProvider } from '@lens-protocol/react';
4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5 | import { WagmiProvider } from 'wagmi';
6 |
7 | import { ConnectKitProvider } from 'connectkit';
8 |
9 | import { client } from './client';
10 | import { config } from './config';
11 |
12 | const queryClient = new QueryClient();
13 |
14 | export function Web3Providers({
15 | children,
16 | }: {
17 | children: React.ReactNode;
18 | }) {
19 | return (
20 |
21 |
22 |
23 | {children}
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/client.ts:
--------------------------------------------------------------------------------
1 | import { PublicClient, mainnet } from '@lens-protocol/react';
2 |
3 | export const client = PublicClient.create({
4 | environment: mainnet,
5 | storage: window.localStorage,
6 | });
7 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/config.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { getDefaultConfig } from 'connectkit';
3 | import { http, createConfig } from 'wagmi';
4 |
5 | export const config = createConfig(
6 | getDefaultConfig({
7 | chains: [chains.mainnet, chains.testnet],
8 | transports: {
9 | [chains.mainnet.id]: http(chains.mainnet.rpcUrls.default.http[0]!),
10 | [chains.testnet.id]: http(chains.testnet.rpcUrls.default.http[0]!),
11 | },
12 | walletConnectProjectId: '',
13 | appName: 'EOA Only Example',
14 | appDescription: 'A sample app.',
15 | appUrl: `${import.meta.env.BASE_URL}`,
16 | appIcon: `${import.meta.env.BASE_URL}/lens.svg`,
17 | }),
18 | );
19 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 |
3 | import { Suspense } from 'react';
4 | import { App } from './App';
5 | import { Web3Providers } from './Web3Providers';
6 |
7 | createRoot(document.getElementById('root')!).render(
8 |
9 | Loading...
}>
10 |
11 |
12 | ,
13 | );
14 |
--------------------------------------------------------------------------------
/examples/eoa-only/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/eoa-only/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "jsx": "react-jsx",
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "strict": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src", "vite.config.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/eoa-only/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/examples/nextjs-client/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
--------------------------------------------------------------------------------
/examples/nextjs-client/.stackblitzrc:
--------------------------------------------------------------------------------
1 | {
2 | "startCommand": "npm run dev",
3 | "env": {}
4 | }
5 |
--------------------------------------------------------------------------------
/examples/nextjs-client/README.md:
--------------------------------------------------------------------------------
1 | # Next.js - Lens Client Integration Example
2 |
3 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
4 |
5 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/nextjs-client)
6 |
--------------------------------------------------------------------------------
/examples/nextjs-client/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from 'next';
2 |
3 | const nextConfig: NextConfig = {
4 | /* config options here */
5 | };
6 |
7 | export default nextConfig;
8 |
--------------------------------------------------------------------------------
/examples/nextjs-client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@lens-protocol/client": "canary",
13 | "next": "15.1.3",
14 | "react": "^19.0.0",
15 | "react-dom": "^19.0.0",
16 | "simpledotcss": "^2.3.3"
17 | },
18 | "devDependencies": {
19 | "@types/node": "^20",
20 | "@types/react": "^19",
21 | "@types/react-dom": "^19",
22 | "typescript": "^5"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples/nextjs-client/src/app/client.ts:
--------------------------------------------------------------------------------
1 | import { PublicClient, testnet } from '@lens-protocol/client';
2 |
3 | export const client = PublicClient.create({
4 | environment: testnet,
5 | });
6 |
--------------------------------------------------------------------------------
/examples/nextjs-client/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lens-protocol/lens-sdk/ce39ebbeb963a36397934a30324ef6b296aadee6/examples/nextjs-client/src/app/favicon.ico
--------------------------------------------------------------------------------
/examples/nextjs-client/src/app/globals.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --background: #ffffff;
3 | --foreground: #171717;
4 | }
5 |
6 | @media (prefers-color-scheme: dark) {
7 | :root {
8 | --background: #0a0a0a;
9 | --foreground: #ededed;
10 | }
11 | }
12 |
13 | html,
14 | body {
15 | max-width: 100vw;
16 | overflow-x: hidden;
17 | }
18 |
19 | body {
20 | color: var(--foreground);
21 | background: var(--background);
22 | font-family: Arial, Helvetica, sans-serif;
23 | -webkit-font-smoothing: antialiased;
24 | -moz-osx-font-smoothing: grayscale;
25 | }
26 |
27 | * {
28 | box-sizing: border-box;
29 | padding: 0;
30 | margin: 0;
31 | }
32 |
33 | a {
34 | color: inherit;
35 | text-decoration: none;
36 | }
37 |
38 | @media (prefers-color-scheme: dark) {
39 | html {
40 | color-scheme: dark;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/nextjs-client/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from 'next';
2 | import { Geist, Geist_Mono } from 'next/font/google';
3 | import 'simpledotcss/simple.min.css';
4 | import './globals.css';
5 |
6 | const geistSans = Geist({
7 | variable: '--font-geist-sans',
8 | subsets: ['latin'],
9 | });
10 |
11 | const geistMono = Geist_Mono({
12 | variable: '--font-geist-mono',
13 | subsets: ['latin'],
14 | });
15 |
16 | export const metadata: Metadata = {
17 | title: 'Next.js - Lens Client Integration',
18 | description: 'Generated by create next app',
19 | };
20 |
21 | export default function RootLayout({
22 | children,
23 | }: Readonly<{
24 | children: React.ReactNode;
25 | }>) {
26 | return (
27 |
28 | {children}
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/examples/nextjs-client/src/app/page.tsx:
--------------------------------------------------------------------------------
1 | import { AccountsOrderBy, PageSize } from '@lens-protocol/client';
2 | import { fetchAccounts } from '@lens-protocol/client/actions';
3 | import { client } from './client';
4 |
5 | export default async function Home() {
6 | const result = await fetchAccounts(client, {
7 | orderBy: AccountsOrderBy.AccountScore,
8 | pageSize: PageSize.Ten,
9 | });
10 |
11 | if (result.isErr()) {
12 | return {result.error.message}
;
13 | }
14 |
15 | return (
16 |
17 |
Top 10 Accounts by Account Score:
18 |
19 |
20 | {result.value?.items.map((account) => (
21 | - {account.username?.value}
22 | ))}
23 |
24 |
25 | );
26 | }
27 |
--------------------------------------------------------------------------------
/examples/nextjs-client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2017",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "plugins": [
17 | {
18 | "name": "next"
19 | }
20 | ],
21 | "paths": {
22 | "@/*": ["./src/*"]
23 | }
24 | },
25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26 | "exclude": ["node_modules"]
27 | }
28 |
--------------------------------------------------------------------------------
/examples/on-ramp/README.md:
--------------------------------------------------------------------------------
1 | # Create an App
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/on-ramp)
4 |
--------------------------------------------------------------------------------
/examples/on-ramp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Fiat On-Ramp
10 | Loading...
11 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/on-ramp/index.ts:
--------------------------------------------------------------------------------
1 | import 'viem/window';
2 |
3 | import { openHalliday } from '@halliday-sdk/commerce';
4 | import { chains } from '@lens-chain/sdk/viem';
5 | import { PublicClient, testnet as protocolTestnet } from '@lens-protocol/client';
6 | import { type Address, createWalletClient, custom } from 'viem';
7 |
8 | const chain = chains.testnet;
9 |
10 | // hoist account
11 | const [address] = (await window.ethereum!.request({ method: 'eth_requestAccounts' })) as [Address];
12 |
13 | const walletClient = createWalletClient({
14 | account: address,
15 | chain,
16 | transport: custom(window.ethereum!),
17 | });
18 |
19 | const client = PublicClient.create({
20 | environment: protocolTestnet,
21 | });
22 |
23 | await openHalliday({
24 | apiKey: '10e4e6e2-7d66-4fb3-b691-48dba9e6a24d',
25 | destinationChainId: 421614,
26 | destinationTokenAddress: '0x9EbD77f6F4f94C86C138E1da78CD15Ba72808eA1',
27 | services: ['ONRAMP'],
28 | useSandbox: true,
29 | windowType: 'EMBED',
30 | targetElementId: 'app',
31 | });
32 |
33 | export default [];
34 |
--------------------------------------------------------------------------------
/examples/on-ramp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-on-ramp",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite"
7 | },
8 | "dependencies": {
9 | "@halliday-sdk/commerce": "^3.2.2",
10 | "@lens-chain/sdk": "latest",
11 | "@lens-protocol/client": "link:../../packages/client",
12 | "@lens-protocol/metadata": "latest",
13 | "@lens-protocol/storage-node-client": "next",
14 | "viem": "^2.21.55"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5.6.3",
18 | "vite": "^5.4.11"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/on-ramp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ESNext", "DOM"],
6 | "module": "ESNext",
7 | "moduleResolution": "Bundler",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["./"]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/react-login/README.md:
--------------------------------------------------------------------------------
1 | # Log in to Lens
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/react-login)
4 |
--------------------------------------------------------------------------------
/examples/react-login/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Log in to Lens
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/react-login/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-login",
3 | "description": "Log in to Lens",
4 | "private": true,
5 | "version": "0.0.0",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite"
9 | },
10 | "dependencies": {
11 | "@lens-chain/sdk": "latest",
12 | "@lens-protocol/react": "canary",
13 | "@tanstack/react-query": "^5.63.0",
14 | "connectkit": "^1.9.0",
15 | "react": "^18.3.1",
16 | "react-dom": "^18.3.1",
17 | "viem": "^2.22.4",
18 | "wagmi": "^2.14.6"
19 | },
20 | "devDependencies": {
21 | "@types/react": "^18.3.12",
22 | "@types/react-dom": "^18.3.1",
23 | "@vitejs/plugin-react-swc": "^3.8.1",
24 | "typescript": "^5.6.3",
25 | "vite": "^5.4.9"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/react-login/public/lens.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/react-login/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { useAuthenticatedUser } from '@lens-protocol/react';
2 | import { LoginForm } from './LoginForm';
3 | import { MyAccount } from './MyAccount';
4 |
5 | export function App() {
6 | const { data } = useAuthenticatedUser({ suspense: true });
7 |
8 | if (data) {
9 | return ;
10 | }
11 |
12 | return ;
13 | }
14 |
--------------------------------------------------------------------------------
/examples/react-login/src/LogoutButton.tsx:
--------------------------------------------------------------------------------
1 | import { useLogout } from '@lens-protocol/react';
2 | import { useDisconnect } from 'wagmi';
3 |
4 | export function LogoutButton() {
5 | const { execute } = useLogout();
6 | const { disconnectAsync } = useDisconnect();
7 |
8 | const onClick = async () => {
9 | await execute();
10 | await disconnectAsync();
11 | };
12 |
13 | return (
14 |
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/examples/react-login/src/MyAccount.tsx:
--------------------------------------------------------------------------------
1 | import { type EvmAddress, useAccount } from '@lens-protocol/react';
2 | import { LogoutButton } from './LogoutButton';
3 |
4 | export function MyAccount({ address }: { address: EvmAddress }) {
5 | const { data } = useAccount({ address, suspense: true });
6 |
7 | return (
8 |
9 |
Welcome, {data?.metadata?.name ?? data?.username?.value ?? data?.address}!
10 |
11 |
Created on: {data?.createdAt}
12 |
Account Score: {data?.score}
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/examples/react-login/src/Web3Providers.tsx:
--------------------------------------------------------------------------------
1 | import type React from 'react';
2 |
3 | import { LensProvider } from '@lens-protocol/react';
4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5 | import { WagmiProvider } from 'wagmi';
6 |
7 | import { ConnectKitProvider } from 'connectkit';
8 |
9 | import { client } from './client';
10 | import { config } from './config';
11 |
12 | const queryClient = new QueryClient();
13 |
14 | export function Web3Providers({
15 | children,
16 | }: {
17 | children: React.ReactNode;
18 | }) {
19 | return (
20 |
21 |
22 |
23 | {children}
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/examples/react-login/src/client.ts:
--------------------------------------------------------------------------------
1 | import { PublicClient, mainnet } from '@lens-protocol/react';
2 |
3 | export const client = PublicClient.create({
4 | environment: mainnet,
5 | storage: window.localStorage,
6 | });
7 |
--------------------------------------------------------------------------------
/examples/react-login/src/config.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { getDefaultConfig } from 'connectkit';
3 | import { http, createConfig } from 'wagmi';
4 |
5 | export const config = createConfig(
6 | getDefaultConfig({
7 | chains: [chains.mainnet, chains.testnet],
8 | transports: {
9 | [chains.mainnet.id]: http(chains.mainnet.rpcUrls.default.http[0]!),
10 | [chains.testnet.id]: http(chains.testnet.rpcUrls.default.http[0]!),
11 | },
12 | walletConnectProjectId: '',
13 | appName: 'Lens + ConnectKit Example',
14 | appDescription: 'A sample app integrating ConnectKit and Lens React SDK.',
15 | appUrl: `${import.meta.env.BASE_URL}`,
16 | appIcon: `${import.meta.env.BASE_URL}/lens.svg`,
17 | }),
18 | );
19 |
--------------------------------------------------------------------------------
/examples/react-login/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 |
3 | import { Suspense } from 'react';
4 | import { App } from './App';
5 | import { Web3Providers } from './Web3Providers';
6 |
7 | createRoot(document.getElementById('root')!).render(
8 |
9 | Loading...}>
10 |
11 |
12 | ,
13 | );
14 |
--------------------------------------------------------------------------------
/examples/react-login/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/react-login/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "jsx": "react-jsx",
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "strict": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src", "vite.config.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/react-login/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/examples/react-post/README.md:
--------------------------------------------------------------------------------
1 | # Create a Lens Post with Lens React Hooks
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/react-post)
4 |
--------------------------------------------------------------------------------
/examples/react-post/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Create a Lens Post with Lens React Hooks
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/examples/react-post/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-post",
3 | "description": "Create a Lens Post with Lens React Hooks",
4 | "private": true,
5 | "version": "0.0.0",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite"
9 | },
10 | "dependencies": {
11 | "@lens-chain/sdk": "^1.0.3",
12 | "@lens-protocol/client": "canary",
13 | "@lens-protocol/metadata": "^2.0.0",
14 | "@lens-protocol/react": "canary",
15 | "@tanstack/react-query": "^5.74.3",
16 | "react": "^18.3.1",
17 | "react-dom": "^18.3.1",
18 | "viem": "2.x",
19 | "wagmi": "^2.14.16"
20 | },
21 | "devDependencies": {
22 | "@types/react": "^18.3.12",
23 | "@types/react-dom": "^18.3.1",
24 | "@vitejs/plugin-react-swc": "^3.7.2",
25 | "typescript": "^5.6.3",
26 | "vite": "^5.4.9"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/react-post/public/lens.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/react-post/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { textOnly } from '@lens-protocol/metadata';
2 | import { useCreatePost } from '@lens-protocol/react';
3 | import { handleOperationWith } from '@lens-protocol/react/viem';
4 | import { useWalletClient } from 'wagmi';
5 |
6 | export function App() {
7 | const { data: wallet } = useWalletClient();
8 | const { execute, loading, data: post } = useCreatePost(handleOperationWith(wallet));
9 |
10 | const onSubmit = async (event: React.FormEvent) => {
11 | event.preventDefault();
12 | const formData = new FormData(event.currentTarget);
13 |
14 | const content = formData.get('content') as string;
15 |
16 | const metadata = textOnly({
17 | content,
18 | });
19 |
20 | const result = await execute({
21 | contentUri: `data:application/json,${JSON.stringify(metadata)}`,
22 | });
23 |
24 | if (result.isErr()) {
25 | alert(result.error.message);
26 | }
27 | };
28 |
29 | return (
30 |
31 |
Post Example
32 | {post && (
33 |
34 |
Post Created
35 |
Slug: {post.slug}
36 |
Created At: {post.timestamp.toString()}
37 |
Content: {post.metadata.__typename === 'TextOnlyMetadata' && post.metadata.content}
38 |
39 | )}
40 |
49 |
50 | );
51 | }
52 |
--------------------------------------------------------------------------------
/examples/react-post/src/client.ts:
--------------------------------------------------------------------------------
1 | import { fetchAccountsAvailable } from '@lens-protocol/client/actions';
2 | import { PublicClient, assertOk, invariant, testnet } from '@lens-protocol/react';
3 | import { signMessageWith } from '@lens-protocol/react/viem';
4 |
5 | import { walletClient } from './wallet';
6 |
7 | export const client = PublicClient.create({
8 | environment: testnet,
9 | storage: window.localStorage,
10 | });
11 |
12 | const result = await client.resumeSession().orElse(() =>
13 | fetchAccountsAvailable(client, { managedBy: walletClient.account.address }).andThen(
14 | ({ items }) => {
15 | invariant(items.length > 0, 'No available accounts found');
16 |
17 | const loginAs =
18 | items[0].__typename === 'AccountOwned'
19 | ? {
20 | accountOwner: {
21 | owner: walletClient.account.address,
22 | account: items[0].account.address,
23 | },
24 | }
25 | : {
26 | accountManager: {
27 | manager: walletClient.account.address,
28 | account: items[0].account.address,
29 | },
30 | };
31 |
32 | return client.login({
33 | ...loginAs,
34 | signMessage: signMessageWith(walletClient),
35 | });
36 | },
37 | ),
38 | );
39 |
40 | assertOk(result);
41 |
--------------------------------------------------------------------------------
/examples/react-post/src/config.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { http, createConfig, injected } from 'wagmi';
3 |
4 | export const config = createConfig({
5 | chains: [chains.testnet],
6 | connectors: [injected()],
7 | transports: {
8 | [chains.testnet.id]: http(),
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/examples/react-post/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 |
3 | import { LensProvider } from '@lens-protocol/react';
4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5 | import { WagmiProvider } from 'wagmi';
6 |
7 | import { App } from './App';
8 | import { client } from './client';
9 | import { config } from './config';
10 |
11 | const queryClient = new QueryClient();
12 |
13 | createRoot(document.getElementById('root')!).render(
14 |
15 |
16 |
17 |
18 |
19 |
20 | ,
21 | );
22 |
--------------------------------------------------------------------------------
/examples/react-post/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import 'viem/window';
3 |
--------------------------------------------------------------------------------
/examples/react-post/src/wallet.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { type Address, createWalletClient, custom } from 'viem';
3 |
4 | const chain = chains.testnet;
5 |
6 | // hoist account
7 | const [address] = (await window.ethereum!.request({ method: 'eth_requestAccounts' })) as [Address];
8 |
9 | export const walletClient = createWalletClient({
10 | account: address,
11 | chain,
12 | transport: custom(window.ethereum!),
13 | });
14 |
15 | const chainId = await walletClient.getChainId();
16 |
17 | if (chainId !== chain.id) {
18 | try {
19 | await walletClient.switchChain({ id: chain.id });
20 | } catch {
21 | await walletClient.addChain({ chain });
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/react-post/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "jsx": "react-jsx",
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "strict": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src", "vite.config.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/react-post/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/examples/sponsored-tx-poc/README.md:
--------------------------------------------------------------------------------
1 | # Sponsored Transaction via RPC Wallet
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/sponsored-tx-poc)
4 |
--------------------------------------------------------------------------------
/examples/sponsored-tx-poc/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Sponsored Transaction
10 | Loading...
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/sponsored-tx-poc/index.ts:
--------------------------------------------------------------------------------
1 | import 'viem/window';
2 |
3 | import { chains } from '@lens-chain/sdk/viem';
4 | import { evmAddress } from '@lens-protocol/client';
5 | import { SponsorshipApprovalSigner } from '@lens-protocol/client/viem';
6 |
7 | import { http, createWalletClient, custom } from 'viem';
8 | import { privateKeyToAccount } from 'viem/accounts';
9 | import { sendTransaction } from 'viem/zksync';
10 |
11 | const chain = chains.testnet;
12 |
13 | const [address] = (await window.ethereum!.request({ method: 'eth_requestAccounts' })) as [Address];
14 |
15 | const wallet = createWalletClient({
16 | account: address,
17 | chain,
18 | transport: custom(window.ethereum!),
19 | });
20 |
21 | const signer = createWalletClient({
22 | account: privateKeyToAccount(window.prompt('Sponsorship Signer PK')),
23 | chain: chain,
24 | transport: http(),
25 | });
26 |
27 | const approver = new SponsorshipApprovalSigner({
28 | signer,
29 | sponsorship: evmAddress(window.prompt('Sponsorship Address')),
30 | });
31 |
32 | const tx = await approver.approveSponsorship({
33 | account: wallet.account,
34 | to: wallet.account.address,
35 | data: '0x',
36 | value: 1, // 1 wei
37 | });
38 |
39 | console.log('tx', tx);
40 |
41 | const hash = await sendTransaction(wallet, tx);
42 |
43 | export default [
44 | `Network: ${chain.name}
`,
45 | `Wallet: ${wallet.account.address}
`,
46 | `Sponsorship Signer: ${signer.account.address}
`,
47 | `Transaction hash: ${hash}
`,
48 | ];
49 |
--------------------------------------------------------------------------------
/examples/sponsored-tx-poc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-sponsored-tx-poc",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite"
7 | },
8 | "dependencies": {
9 | "@lens-chain/sdk": "latest",
10 | "@lens-protocol/client": "canary",
11 | "viem": "^2.21.55"
12 | },
13 | "devDependencies": {
14 | "typescript": "^5.6.3",
15 | "vite": "^5.4.11"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/sponsored-tx-poc/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ESNext", "DOM"],
6 | "module": "ESNext",
7 | "moduleResolution": "Bundler",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["./"]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/sponsored-tx/.stackblitzrc:
--------------------------------------------------------------------------------
1 | {
2 | "startCommand": "pnpm start",
3 | "env": {
4 | "ENVIRONMENT": "testnet",
5 | "WALLET_PRIVATE_KEY": "0x…",
6 | "SPONSORSHIP_ADDRESS": "0x…",
7 | "SPONSORSHIP_SIGNER_PRIVATE_KEY": "0x…"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/sponsored-tx/README.md:
--------------------------------------------------------------------------------
1 | # Sponsored Transaction Example
2 |
3 | This example demonstrates how to sponsor a transaction on Lens Chain using the Lens Protocol SDK.
4 |
5 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/sponsored-tx)
6 |
--------------------------------------------------------------------------------
/examples/sponsored-tx/index.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { evmAddress } from '@lens-protocol/client';
3 | import { SponsorshipApprovalSigner } from '@lens-protocol/client/viem';
4 | import { http, type Address, type Hash, type Hex, createWalletClient } from 'viem';
5 | import { privateKeyToAccount } from 'viem/accounts';
6 | import { waitForTransactionReceipt } from 'viem/actions';
7 | import { sendTransaction } from 'viem/zksync';
8 |
9 | const chain = process.env.ENVIRONMENT === 'mainnet' ? chains.mainnet : chains.testnet;
10 | console.log(`Network: ${chain.name}`);
11 |
12 | const wallet = createWalletClient({
13 | account: privateKeyToAccount(process.env.WALLET_PRIVATE_KEY as Hex),
14 | chain: chain,
15 | transport: http(),
16 | });
17 | console.log(`Wallet: ${wallet.account.address}`);
18 |
19 | const signer = createWalletClient({
20 | account: privateKeyToAccount(process.env.SPONSORSHIP_SIGNER_PRIVATE_KEY as Hex),
21 | chain: chain,
22 | transport: http(),
23 | });
24 | console.log(`Sponsorship Signer: ${signer.account.address}`);
25 |
26 | const approver = new SponsorshipApprovalSigner({
27 | signer,
28 | sponsorship: evmAddress(process.env.SPONSORSHIP_ADDRESS as Address),
29 | });
30 |
31 | export interface SponsorRequest {
32 | to: Address;
33 | value: string | number;
34 | data?: Hex;
35 | }
36 |
37 | async function sendSponsoredTransaction({ to, value, data }: SponsorRequest): Promise {
38 | const tx = await approver.approveSponsorship({
39 | account: wallet.account,
40 | to,
41 | value,
42 | data,
43 | });
44 |
45 | // biome-ignore lint/suspicious/noExplicitAny: keep it simple
46 | return await sendTransaction(wallet, tx as any);
47 | }
48 |
49 | const hash = await sendSponsoredTransaction({
50 | to: wallet.account.address,
51 | value: 1, // 1 wei
52 | });
53 | console.log(`Transaction hash: ${hash}`);
54 | await waitForTransactionReceipt(wallet, { hash });
55 |
--------------------------------------------------------------------------------
/examples/sponsored-tx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-sponsored-tx",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "start": "vite-node index.ts"
7 | },
8 | "dependencies": {
9 | "@lens-chain/sdk": "latest",
10 | "@lens-protocol/client": "canary",
11 | "viem": "~2.22.4"
12 | },
13 | "devDependencies": {
14 | "typescript": "^5.8.3",
15 | "vite-node": "^1.6.1"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/sponsored-tx/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ESNext"],
6 | "module": "NodeNext",
7 | "moduleResolution": "NodeNext",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["./"]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/README.md:
--------------------------------------------------------------------------------
1 | # thirdweb On-Ramp
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/thirdweb-onramp)
4 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | thirdweb On-Ramp
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-thirdweb-onramp",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite"
7 | },
8 | "dependencies": {
9 | "@lens-chain/sdk": "latest",
10 | "@lens-protocol/client": "canary",
11 | "@lens-protocol/metadata": "latest",
12 | "@lens-protocol/storage-node-client": "next",
13 | "@tanstack/react-query": "^5.66.8",
14 | "react": "^18.3.1",
15 | "react-dom": "^18.3.1",
16 | "thirdweb": "^5.89.0",
17 | "viem": "^2.21.55",
18 | "wagmi": "^2.14.11"
19 | },
20 | "devDependencies": {
21 | "@types/react": "18",
22 | "@types/react-dom": "18",
23 | "@vitejs/plugin-react-swc": "^3.8.0",
24 | "typescript": "^5.6.3",
25 | "vite": "^5.4.11"
26 | },
27 | "resolutions": {
28 | "viem": "^2.21.55"
29 | },
30 | "pnpm": {
31 | "overrides": {
32 | "viem": "^2.21.55"
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/public/lens.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3 | import { createRoot } from 'react-dom/client';
4 | import { ThirdwebProvider } from 'thirdweb/react';
5 | import { http, WagmiProvider, createConfig, injected } from 'wagmi';
6 |
7 | import { App } from './App';
8 |
9 | export const config = createConfig({
10 | chains: [chains.testnet],
11 | connectors: [injected()],
12 | transports: {
13 | [chains.testnet.id]: http(),
14 | },
15 | });
16 |
17 | const queryClient = new QueryClient();
18 |
19 | createRoot(document.getElementById('root')!).render(
20 |
21 |
22 |
23 |
24 |
25 |
26 | ,
27 | );
28 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "jsx": "react-jsx",
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "strict": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src", "vite.config.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/thirdweb-onramp/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/examples/user-onboarding/README.md:
--------------------------------------------------------------------------------
1 | # User Onboarding
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/user-onboarding)
4 |
--------------------------------------------------------------------------------
/examples/user-onboarding/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | User Onboarding Example
10 | Loading...
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/examples/user-onboarding/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-user-onboarding",
3 | "private": true,
4 | "type": "module",
5 | "scripts": {
6 | "dev": "vite"
7 | },
8 | "dependencies": {
9 | "@lens-chain/sdk": "latest",
10 | "@lens-protocol/client": "canary",
11 | "@lens-protocol/metadata": "latest",
12 | "@lens-chain/storage-client": "latest",
13 | "viem": "^2.21.55"
14 | },
15 | "devDependencies": {
16 | "typescript": "^5.6.3",
17 | "vite": "^5.4.11"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/user-onboarding/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ESNext", "DOM"],
6 | "module": "ESNext",
7 | "moduleResolution": "Bundler",
8 | "strict": true,
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "noEmit": true,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "noImplicitReturns": true,
16 | "skipLibCheck": true
17 | },
18 | "include": ["./"]
19 | }
20 |
--------------------------------------------------------------------------------
/jest-extended.d.ts:
--------------------------------------------------------------------------------
1 | import type CustomMatchers from 'jest-extended';
2 | import 'vitest';
3 |
4 | declare module 'vitest' {
5 | interface Assertion extends CustomMatchers {}
6 | interface AsymmetricMatchersContaining extends CustomMatchers {}
7 | interface ExpectStatic extends CustomMatchers {}
8 | }
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lens-sdk",
3 | "version": "0.0.0",
4 | "description": "The quickest way to build on top of Lens.",
5 | "private": true,
6 | "type": "module",
7 | "workspaces": [
8 | "packages/*"
9 | ],
10 | "engines": {
11 | "node": ">=20",
12 | "pnpm": ">=9.1.2"
13 | },
14 | "scripts": {
15 | "build": "turbo build",
16 | "dev": "turbo watch build",
17 | "clean": "rimraf .turbo packages/*/dist",
18 | "lint": "biome check",
19 | "lint:fix": "biome check --write",
20 | "new:package": "NODE_OPTIONS='--import tsx' plop --plopfile=plopfile.ts",
21 | "prepublish": "pnpm run build",
22 | "test:client": "vitest --project client",
23 | "test:react": "vitest --project react",
24 | "test:storage": "vitest --project storage",
25 | "test": "vitest"
26 | },
27 | "devDependencies": {
28 | "@biomejs/biome": "1.9.4",
29 | "@changesets/cli": "^2.29.1",
30 | "@types/node": "^22.15.29",
31 | "jest-extended": "^4.0.2",
32 | "plop": "^4.0.1",
33 | "rimraf": "^6.0.1",
34 | "tsx": "^4.19.3",
35 | "turbo": "^2.2.3",
36 | "typescript": "^5.6.3",
37 | "vite": "^6.3.5",
38 | "vite-tsconfig-paths": "^5.0.1",
39 | "vitest": "^3.2.0"
40 | },
41 | "license": "MIT",
42 | "packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228"
43 | }
44 |
--------------------------------------------------------------------------------
/packages/client/README.md:
--------------------------------------------------------------------------------
1 | # Lens JavaScript SDK
2 |
3 | The official framework-agnostic JavaScript SDK for Lens Protocol.
4 |
5 | ---
6 |
7 | This package enables you to interact with the Lens API via a type safe interface that abstracts away some of the GraphQL intricacies.
8 |
--------------------------------------------------------------------------------
/packages/client/src/AuthenticatedUser.ts:
--------------------------------------------------------------------------------
1 | import { Role } from '@lens-protocol/graphql';
2 | import { type EvmAddress, type Result, type UUID, err, never, ok } from '@lens-protocol/types';
3 | import { UnexpectedError } from './errors';
4 | import { type IdTokenClaims, ROLE_CLAIM, SPONSORED_CLAIM } from './tokens';
5 |
6 | export type AuthenticatedUser = {
7 | address: EvmAddress;
8 | app: EvmAddress;
9 | authenticationId: UUID;
10 | role: Role;
11 | signer: EvmAddress;
12 | sponsored: boolean;
13 | };
14 |
15 | /**
16 | * @internal
17 | */
18 | export function authenticatedUser(
19 | claims: IdTokenClaims,
20 | ): Result {
21 | switch (claims[ROLE_CLAIM]) {
22 | case Role.AccountManager:
23 | return ok({
24 | address: claims.act?.sub ?? never('Account Manager must have an Actor Claim'),
25 | app: claims.aud,
26 | authenticationId: claims.sid,
27 | role: Role.AccountManager,
28 | signer: claims.sub,
29 | sponsored: claims[SPONSORED_CLAIM],
30 | });
31 |
32 | case Role.AccountOwner:
33 | return ok({
34 | address: claims.act?.sub ?? never('Account Owner must have an Actor Claim'),
35 | app: claims.aud,
36 | authenticationId: claims.sid,
37 | role: Role.AccountOwner,
38 | signer: claims.sub,
39 | sponsored: claims[SPONSORED_CLAIM],
40 | });
41 |
42 | case Role.OnboardingUser:
43 | case Role.Builder:
44 | case Role.UnverifiedEOA:
45 | return ok({
46 | address: claims.sub,
47 | app: claims.aud,
48 | authenticationId: claims.sid,
49 | role: claims[ROLE_CLAIM],
50 | signer: claims.sub,
51 | sponsored: claims[SPONSORED_CLAIM],
52 | });
53 |
54 | default:
55 | return err(UnexpectedError.from(`Unexpected role: ${claims[ROLE_CLAIM]}`));
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/client/src/actions/account.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { assertOk, evmAddress } from '@lens-protocol/types';
2 | import { describe, it } from 'vitest';
3 |
4 | import { createPublicClient } from '../test-utils';
5 | import { fetchAccount } from './account';
6 |
7 | describe('Given the Account query actions', () => {
8 | const client = createPublicClient();
9 |
10 | describe(`When invoking the '${fetchAccount.name}' action`, () => {
11 | it('Then it should not fail w/ a GQL BadRequest error', async () => {
12 | const result = await fetchAccount(client, {
13 | address: evmAddress(import.meta.env.TEST_ACCOUNT),
14 | });
15 |
16 | assertOk(result);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/client/src/actions/index.ts:
--------------------------------------------------------------------------------
1 | export * from './account';
2 | export * from './accountManager';
3 | export * from './actions';
4 | export * from './admins';
5 | export * from './app';
6 | export * from './authentication';
7 | export * from './feed';
8 | export * from './follow';
9 | export * from './frames';
10 | export * from './funds';
11 | export * from './graph';
12 | export * from './group';
13 | export * from './helpers';
14 | export * from './metadata';
15 | export * from './misc';
16 | export * from './ml';
17 | export * from './namespace';
18 | export * from './notifications';
19 | export * from './post';
20 | export * from './posts';
21 | export * from './sns';
22 | export * from './sponsorship';
23 | export * from './timeline';
24 | export * from './transactions';
25 | export * from './transfer';
26 | export * from './username';
27 |
--------------------------------------------------------------------------------
/packages/client/src/actions/misc.ts:
--------------------------------------------------------------------------------
1 | import {
2 | AccessControlQuery,
3 | type AccessControlRequest,
4 | type AccessControlResult,
5 | HealthQuery,
6 | } from '@lens-protocol/graphql';
7 | import type { ResultAsync } from '@lens-protocol/types';
8 | import type { AnyClient } from '../clients';
9 | import type { UnexpectedError } from '../errors';
10 |
11 | /**
12 | * Health check query.
13 | *
14 | * ```ts
15 | * const result = await health(anyClient);
16 | * ```
17 | *
18 | * @param client - Any Lens client.
19 | * @returns True or false
20 | */
21 | export function health(client: AnyClient): ResultAsync {
22 | return client.query(HealthQuery, {});
23 | }
24 |
25 | /**
26 | * Fetch an Access Control details.
27 | *
28 | * @param client - Any Lens client.
29 | * @param request - The Access Control query request.
30 | * @returns The Access Control or `null` if it does not exist.
31 | */
32 | export function fetchAccessControl(
33 | client: AnyClient,
34 | request: AccessControlRequest,
35 | ): ResultAsync {
36 | return client.query(AccessControlQuery, { request });
37 | }
38 |
--------------------------------------------------------------------------------
/packages/client/src/actions/ml.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { assertOk } from '@lens-protocol/types';
2 | import { describe, it } from 'vitest';
3 |
4 | import { TEST_ACCOUNT, createPublicClient } from '../test-utils';
5 | import { fetchAccountRecommendations, fetchPostsForYou, fetchPostsToExplore } from './ml';
6 |
7 | describe('Given the ML query actions', () => {
8 | const client = createPublicClient();
9 |
10 | describe(`When invoking the '${fetchAccountRecommendations.name}' action`, () => {
11 | it('Then it should not fail w/ a GQL BadRequest error', async () => {
12 | const result = await fetchAccountRecommendations(client, {
13 | account: TEST_ACCOUNT,
14 | });
15 | assertOk(result);
16 | });
17 | });
18 |
19 | describe(`When invoking the '${fetchPostsForYou.name}' action`, () => {
20 | it('Then it should not fail w/ a GQL BadRequest error', async () => {
21 | const result = await fetchPostsForYou(client, {
22 | account: TEST_ACCOUNT,
23 | });
24 | assertOk(result);
25 | });
26 | });
27 |
28 | describe(`When invoking the '${fetchPostsToExplore.name}' action`, () => {
29 | it('Then it should not fail w/ a GQL BadRequest error', async () => {
30 | const result = await fetchPostsToExplore(client, {});
31 | assertOk(result);
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/client/src/actions/notifications.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { assertOk } from '@lens-protocol/types';
2 | import { describe, it } from 'vitest';
3 |
4 | import { loginAsAccountOwner } from '../test-utils';
5 | import { fetchNotifications } from './notifications';
6 |
7 | describe(`Given the '${fetchNotifications.name}' action`, () => {
8 | describe('When invoked', () => {
9 | it('Then it should not fail w/ a GQL BadRequest error', async () => {
10 | const result = await loginAsAccountOwner().andThen(fetchNotifications);
11 |
12 | assertOk(result);
13 | });
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/packages/client/src/actions/notifications.ts:
--------------------------------------------------------------------------------
1 | import type { Notification, NotificationsRequest, Paginated } from '@lens-protocol/graphql';
2 | import { NotificationsQuery } from '@lens-protocol/graphql';
3 | import type { ResultAsync } from '@lens-protocol/types';
4 |
5 | import type { SessionClient } from '../clients';
6 | import type { UnexpectedError } from '../errors';
7 |
8 | /**
9 | * Fetch notifications for the authenticated Account.
10 | *
11 | * ```ts
12 | * const result = await fetchNotifications(sessionClient);
13 | * ```
14 | *
15 | * @param client - The session client for the authenticated Account.
16 | * @param request - The query request.
17 | * @returns Paginated notifications.
18 | */
19 | export function fetchNotifications(
20 | client: SessionClient,
21 | request: NotificationsRequest = {},
22 | ): ResultAsync, UnexpectedError> {
23 | return client.query(NotificationsQuery, { request }) as ResultAsync<
24 | Paginated,
25 | UnexpectedError
26 | >;
27 | }
28 |
--------------------------------------------------------------------------------
/packages/client/src/actions/posts.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { assertOk, postId } from '@lens-protocol/types';
2 | import { describe, it } from 'vitest';
3 |
4 | import { createPublicClient } from '../test-utils';
5 | import { fetchPost } from './posts';
6 |
7 | describe('Given the Post query actions', () => {
8 | const client = createPublicClient();
9 |
10 | describe(`When invoking the '${fetchPost.name}' action`, () => {
11 | it('Then it should not fail w/ a GQL BadRequest error', async () => {
12 | const result = await fetchPost(client, {
13 | post: postId('42'),
14 | });
15 |
16 | assertOk(result);
17 | });
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/packages/client/src/actions/timeline.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AnyPost,
3 | Paginated,
4 | TimelineHighlightsRequest,
5 | TimelineItem,
6 | TimelineRequest,
7 | } from '@lens-protocol/graphql';
8 | import { TimelineHighlightsQuery, TimelineQuery } from '@lens-protocol/graphql';
9 | import type { ResultAsync } from '@lens-protocol/types';
10 |
11 | import type { AnyClient, SessionClient } from '../clients';
12 | import type { UnexpectedError } from '../errors';
13 |
14 | /**
15 | * Fetch timeline from an account.
16 | *
17 | * ```ts
18 | * const result = await fetchTimeline(sessionClient, {
19 | * account: evmAddress('0xe2f2a5C287993345a840db3B0845fbc70f5935a5'),
20 | * });
21 | * ```
22 | *
23 | * @param client - The session client for the authenticated Account.
24 | * @param request - The query request.
25 | * @returns The list of timeline items.
26 | */
27 | export function fetchTimeline(
28 | client: SessionClient,
29 | request: TimelineRequest,
30 | ): ResultAsync, UnexpectedError> {
31 | return client.query(TimelineQuery, { request });
32 | }
33 |
34 | /**
35 | * Fetch Timeline Highlights for an account.
36 | *
37 | * ```ts
38 | * const result = await fetchTimelineHighlights(anyClient, {
39 | * account: evmAddress('0xe2f2a5C287993345a840db3B0845fbc70f5935a5'),
40 | * });
41 | * ```
42 | *
43 | * @param client - Any Lens client.
44 | * @param request - The query request.
45 | * @returns The list of highlights post for an account.
46 | */
47 | export function fetchTimelineHighlights(
48 | client: AnyClient,
49 | request: TimelineHighlightsRequest,
50 | ): ResultAsync, UnexpectedError> {
51 | return client.query(TimelineHighlightsQuery, { request });
52 | }
53 |
--------------------------------------------------------------------------------
/packages/client/src/actions/transactions.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | PrepareSignerErc20ApprovalRequest,
3 | PrepareSignerErc20ApprovalResult,
4 | TransactionStatusRequest,
5 | TransactionStatusResult,
6 | } from '@lens-protocol/graphql';
7 | import { PrepareSignerErc20ApprovalMutation, TransactionStatusQuery } from '@lens-protocol/graphql';
8 | import type { ResultAsync } from '@lens-protocol/types';
9 |
10 | import type { AnyClient, SessionClient } from '../clients';
11 | import type { UnauthenticatedError, UnexpectedError } from '../errors';
12 |
13 | /**
14 | * Fetch the indexing status of a Transaction.
15 | *
16 | * ```ts
17 | * const result = await transactionStatus(anyClient, {
18 | * txHash: txHash('0x97589c9e3a3c5b007d…'),
19 | * });
20 | * ```
21 | *
22 | * @param client - Any Lens client.
23 | * @param request - The query request.
24 | * @returns The indexing status of the Transaction.
25 | */
26 | export function transactionStatus(
27 | client: AnyClient,
28 | request: TransactionStatusRequest,
29 | ): ResultAsync {
30 | return client.query(TransactionStatusQuery, { request });
31 | }
32 |
33 | /**
34 | * Prepare a signer ERC20 approval transaction.
35 | *
36 | * ```ts
37 | * const result = await prepareSignerErc20Approval(sessionClient, {
38 | * approval: {
39 | * infinite: evmAddress('0x1235678901234567890123456789012345678901'),
40 | * },
41 | * });
42 | * ```
43 | *
44 | * @param client - Session Lens client.
45 | * @param request - The mutation request.
46 | * @returns The prepared transaction.
47 | */
48 | export function prepareSignerErc20Approval(
49 | client: SessionClient,
50 | request: PrepareSignerErc20ApprovalRequest,
51 | ): ResultAsync {
52 | return client.mutation(PrepareSignerErc20ApprovalMutation, { request });
53 | }
54 |
--------------------------------------------------------------------------------
/packages/client/src/actions/transfer.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | TransferPrimitiveOwnershipRequest,
3 | TransferPrimitiveOwnershipResult,
4 | } from '@lens-protocol/graphql';
5 | import { TransferPrimitiveOwnershipMutation } from '@lens-protocol/graphql';
6 | import type { ResultAsync } from '@lens-protocol/types';
7 |
8 | import type { SessionClient } from '../clients';
9 | import type { UnauthenticatedError, UnexpectedError } from '../errors';
10 |
11 | /**
12 | * Transfer primitive ownership.
13 | *
14 | * ```ts
15 | * const result = await transferPrimitiveOwnership(sessionClient, {
16 | * newOwner: evmAddress('0x1234…'),
17 | * address: evmAddress('0x5678…'),
18 | * });
19 | * ```
20 | *
21 | * @param client - Session client.
22 | * @param request - The mutation request.
23 | * @returns Tiered transaction result.
24 | */
25 | export function transferPrimitiveOwnership(
26 | client: SessionClient,
27 | request: TransferPrimitiveOwnershipRequest,
28 | ): ResultAsync {
29 | return client.mutation(TransferPrimitiveOwnershipMutation, { request });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/client/src/authorization.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Operations types.
3 | */
4 | export enum OperationType {
5 | Post = 'Post',
6 | Repost = 'Repost',
7 | EditPost = 'EditPost',
8 | DeletePost = 'DeletePost',
9 | Follow = 'Follow',
10 | Unfollow = 'Unfollow',
11 | CreateAccount = 'CreateAccount',
12 | CreateUsername = 'CreateUsername',
13 | CreateAndAssignUsername = 'CreateAndAssignUsername',
14 | AssignUsername = 'AssignUsername',
15 | UnassignUsername = 'UnassignUsername',
16 | SetAccountMetadata = 'SetAccountMetadata',
17 | JoinGroup = 'JoinGroup',
18 | LeaveGroup = 'LeaveGroup',
19 | AddGroupMember = 'AddGroupMember',
20 | RemoveGroupMember = 'RemoveGroupMember',
21 | }
22 |
23 | /**
24 | * An operation approval request.
25 | */
26 | export type OperationApprovalRequest = {
27 | nonce: string;
28 | deadline: string;
29 | operation: OperationType;
30 | validator: string;
31 | account: string;
32 | };
33 |
--------------------------------------------------------------------------------
/packages/client/src/config.ts:
--------------------------------------------------------------------------------
1 | import type { EnvironmentConfig } from '@lens-protocol/env';
2 | import type { IStorageProvider } from '@lens-protocol/storage';
3 | import type { TypedDocumentNode } from '@urql/core';
4 |
5 | /**
6 | * The client configuration.
7 | */
8 | export type ClientConfig = {
9 | /**
10 | * The environment configuration to use (e.g. `mainnet`, `testnet`).
11 | */
12 | environment: EnvironmentConfig;
13 | /**
14 | * Whether to enable caching.
15 | *
16 | * @defaultValue `false`
17 | */
18 | cache?: boolean;
19 | /**
20 | * Whether to enable debug mode.
21 | *
22 | * @defaultValue `false`
23 | */
24 | debug?: boolean;
25 | /**
26 | * The URL origin of the client.
27 | *
28 | * Use this to set the `Origin` header for requests from non-browser environments.
29 | */
30 | origin?: string;
31 |
32 | /**
33 | * The storage provider to use for authentication state and other data.
34 | *
35 | * @defaultValue {@link InMemoryStorageProvider}
36 | */
37 | storage?: IStorageProvider;
38 |
39 | /**
40 | * The Server API Key for your Lens App.
41 | *
42 | * Use this with a Server API Key to not incur in rate limits when used on a server-to-server scenario.
43 | */
44 | apiKey?: string;
45 |
46 | /**
47 | * The custom fragments to use.
48 | */
49 | fragments?: TypedDocumentNode[];
50 | };
51 |
--------------------------------------------------------------------------------
/packages/client/src/context.ts:
--------------------------------------------------------------------------------
1 | import type { EnvironmentConfig } from '@lens-protocol/env';
2 | import { type IStorageProvider, InMemoryStorageProvider } from '@lens-protocol/storage';
3 |
4 | import type { ClientConfig } from './config';
5 | import { FragmentResolver } from './fragments';
6 |
7 | /**
8 | * @internal
9 | */
10 | export type Context = {
11 | environment: EnvironmentConfig;
12 | cache: boolean;
13 | debug: boolean;
14 | origin?: string;
15 | storage: IStorageProvider;
16 | apiKey?: string;
17 | fragments: FragmentResolver;
18 | };
19 |
20 | /**
21 | * @internal
22 | */
23 | export function configureContext(from: ClientConfig): Context {
24 | return {
25 | environment: from.environment,
26 | cache: from.cache ?? false,
27 | debug: from.debug ?? false,
28 | origin: from.origin,
29 | storage: from.storage ?? new InMemoryStorageProvider(),
30 | apiKey: from.apiKey,
31 | fragments: FragmentResolver.from(from.fragments ?? []),
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/packages/client/src/ethers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './signer';
2 | export * from './sponsorship';
3 |
--------------------------------------------------------------------------------
/packages/client/src/ethers/signer.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { Network, Wallet, getDefaultProvider } from '@lens-chain/sdk/ethers';
2 | import { assertOk, uri } from '@lens-protocol/types';
3 | import { describe, it } from 'vitest';
4 |
5 | import { post } from '../actions/post';
6 | import { loginAsAccountOwner } from '../test-utils';
7 | import { handleOperationWith } from './signer';
8 |
9 | // biome-ignore lint/suspicious/noExplicitAny: needs a fix in @lens-chain/sdk
10 | const wallet = new Wallet(import.meta.env.PRIVATE_KEY, getDefaultProvider(Network.Testnet) as any);
11 |
12 | describe(`Given the '${handleOperationWith.name}' helper for ethers.js`, { timeout: 10000 }, () => {
13 | describe('When handling the result of a transaction mutation', () => {
14 | it('Then it should be possible to chain them with other helpers', async () => {
15 | const result = await loginAsAccountOwner().andThen((sessionClient) =>
16 | post(sessionClient, {
17 | contentUri: uri('https://devnet.irys.xyz/3n3Ujg3jPBHX58MPPqYXBSQtPhTgrcTk4RedJgV1Ejhb'),
18 | })
19 | .andThen(handleOperationWith(wallet))
20 | .andThen(sessionClient.waitForTransaction),
21 | );
22 |
23 | assertOk(result);
24 | });
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/packages/client/src/ethers/sponsorship.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { Network, Wallet, getDefaultProvider } from '@lens-chain/sdk/ethers';
2 | import { describe, it } from 'vitest';
3 |
4 | import { GLOBAL_SPONSORSHIP, PRIVATE_KEY, SPONSORSHIP_APPROVER_PRIVATE_KEY } from '../test-utils';
5 | import { SponsorshipApprovalSigner } from './sponsorship';
6 |
7 | const provider = getDefaultProvider(Network.Testnet);
8 |
9 | const user = new Wallet(
10 | PRIVATE_KEY,
11 | // biome-ignore lint/suspicious/noExplicitAny: needs a fix in @lens-chain/sdk
12 | provider as any,
13 | );
14 | const signer = new Wallet(
15 | SPONSORSHIP_APPROVER_PRIVATE_KEY,
16 | // biome-ignore lint/suspicious/noExplicitAny: needs a fix in @lens-chain/sdk
17 | provider as any,
18 | );
19 |
20 | describe(
21 | `Given an instance of the '${SponsorshipApprovalSigner.name}' for ethers.js`,
22 | { timeout: 10_000 },
23 | () => {
24 | describe('When approving a TransactionRequest', () => {
25 | it('Then it should return the corresponding EIP-712 TransactionRequest with sponsorship approval', async () => {
26 | const approver = new SponsorshipApprovalSigner({
27 | signer,
28 | sponsorship: GLOBAL_SPONSORSHIP,
29 | });
30 |
31 | const approved = await approver.approveSponsorship({
32 | from: user.address,
33 | to: user.address,
34 | value: 1n,
35 | });
36 |
37 | const response = await user.sendTransaction(approved);
38 | await response.wait();
39 | });
40 | });
41 | },
42 | );
43 |
--------------------------------------------------------------------------------
/packages/client/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from '@lens-protocol/env';
2 | export * from '@lens-protocol/graphql';
3 | export type { IStorageProvider, InMemoryStorageProvider } from '@lens-protocol/storage';
4 | export * from '@lens-protocol/types';
5 | export * from './AuthenticatedUser';
6 | export * from './authorization';
7 | export * from './clients';
8 | export * from './config';
9 | export type * from './context';
10 | export * from './errors';
11 | export * from './sponsorship';
12 | export * from './types';
13 |
--------------------------------------------------------------------------------
/packages/client/src/logger.ts:
--------------------------------------------------------------------------------
1 | type LogMethod = (...args: unknown[]) => void;
2 |
3 | export enum LogLevel {
4 | TRACE = 0,
5 | DEBUG = 1,
6 | INFO = 2,
7 | WARN = 3,
8 | ERROR = 4,
9 | SILENT = 5,
10 | }
11 |
12 | export class Logger {
13 | private level: LogLevel;
14 | private name: string;
15 |
16 | private constructor(name: string, level: LogLevel = LogLevel.WARN) {
17 | this.name = name;
18 | this.level = level;
19 | this.replaceMethods();
20 | }
21 |
22 | static named(name: string, level: LogLevel = LogLevel.WARN): Logger {
23 | return new Logger(name, level);
24 | }
25 |
26 | trace: LogMethod = () => {};
27 | debug: LogMethod = () => {};
28 | info: LogMethod = () => {};
29 | warn: LogMethod = () => {};
30 | error: LogMethod = () => {};
31 | log: LogMethod = () => {}; // alias for debug
32 |
33 | private replaceMethods() {
34 | (['trace', 'debug', 'info', 'warn', 'error'] as const).forEach((methodName, index) => {
35 | if (index >= this.level) {
36 | this[methodName] = () => {};
37 | } else {
38 | this[methodName] = (...args) =>
39 | console[methodName === 'debug' ? 'log' : methodName](`[${this.name}]`, ...args);
40 | }
41 | });
42 | this.log = this.debug;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/packages/client/src/sponsorship.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/packages/client/src/types.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | SelfFundedTransactionRequest,
3 | SponsoredTransactionRequest,
4 | } from '@lens-protocol/graphql';
5 | import type { ResultAsync, TxHash } from '@lens-protocol/types';
6 | import type { SigningError, ValidationError } from './errors';
7 |
8 | export function isTransactionRequest(request: { __typename: string }): request is
9 | | SponsoredTransactionRequest
10 | | SelfFundedTransactionRequest {
11 | return (
12 | request.__typename === 'SponsoredTransactionRequest' ||
13 | request.__typename === 'SelfFundedTransactionRequest'
14 | );
15 | }
16 |
17 | export type OperationResponse = {
18 | __typename: T;
19 | hash: TxHash;
20 | };
21 |
22 | export type ErrorResponse = {
23 | __typename: T;
24 | reason: string;
25 | };
26 |
27 | export type DelegableOperationResult =
28 | | OperationResponse
29 | | SponsoredTransactionRequest
30 | | SelfFundedTransactionRequest
31 | | ErrorResponse;
32 |
33 | export type RestrictedOperationResult =
34 | | SponsoredTransactionRequest
35 | | SelfFundedTransactionRequest
36 | | ErrorResponse;
37 |
38 | export type OperationResult =
39 | | DelegableOperationResult
40 | | RestrictedOperationResult;
41 |
42 | export type RestrictedOperationHandler = (
43 | result: RestrictedOperationResult,
44 | ) => ResultAsync>;
45 |
46 | export type DelegableOperationHandler = (
47 | result: DelegableOperationResult,
48 | ) => ResultAsync>;
49 |
50 | export type OperationHandler<
51 | T extends string = string,
52 | E extends string = string,
53 | > = T extends string ? DelegableOperationHandler : RestrictedOperationHandler;
54 |
--------------------------------------------------------------------------------
/packages/client/src/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @internal
3 | */
4 | export function delay(ms: number): Promise {
5 | return new Promise((resolve) => setTimeout(resolve, ms));
6 | }
7 |
--------------------------------------------------------------------------------
/packages/client/src/viem/authorization.test.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import { evmAddress } from '@lens-protocol/types';
3 | import { privateKeyToAccount } from 'viem/accounts';
4 | import { describe, expect, it } from 'vitest';
5 |
6 | import { OperationType } from '../authorization';
7 | import { OperationApprovalSigner } from './authorization';
8 |
9 | const privateKey = '0xa7d25f98c7996df6418d5205d03386b254451d45de060dcd4c7f486d9c12061e';
10 | const appAddress = evmAddress('0x3a24d26AdEBA0d6330F207d0ca699cBE5fFbE553');
11 | const accountAddress = '0xbca85dda68cC21B98F6a416c28F9de94C4cBdcB9';
12 | const lensPrimitive = '0x07753ab956B70498196772E8421379DB12de54eb';
13 |
14 | describe(
15 | `Given an instance of the '${OperationApprovalSigner.name}' for viem`,
16 | { timeout: 10000 },
17 | () => {
18 | describe('When signing an OperationApprovalRequest', () => {
19 | it('Then it should return the expected signature', async () => {
20 | const approver = new OperationApprovalSigner({
21 | app: appAddress,
22 | chain: chains.testnet,
23 | signer: privateKeyToAccount(privateKey),
24 | });
25 |
26 | const signature = await approver.signOperationApproval({
27 | nonce: '42',
28 | deadline: '1630000000',
29 | operation: OperationType.Post,
30 | validator: lensPrimitive,
31 | account: accountAddress,
32 | });
33 |
34 | expect(signature).toEqual(
35 | '0xc0dc1300e351b79ba63b17581c5d5dd914ed2d6ddcd7cd4476b3930c199fd75807adc8c989db3da1af0ceb66689d0d3954f7180a81cfcb3e77af1bdcb6508d111c',
36 | );
37 | });
38 | });
39 | },
40 | );
41 |
--------------------------------------------------------------------------------
/packages/client/src/viem/index.ts:
--------------------------------------------------------------------------------
1 | export * from './authorization';
2 | export * from './signer';
3 | export * from './sponsorship';
4 |
--------------------------------------------------------------------------------
/packages/client/src/viem/signer.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from 'vitest';
2 |
3 | import { chains } from '@lens-chain/sdk/viem';
4 | import { uri } from '@lens-protocol/types';
5 | import { http, createWalletClient } from 'viem';
6 | import { handleOperationWith } from '.';
7 | import { post } from '../actions/post';
8 | import { loginAsAccountOwner, signer } from '../test-utils';
9 |
10 | const walletClient = createWalletClient({
11 | account: signer,
12 | chain: chains.testnet,
13 | transport: http(),
14 | });
15 |
16 | describe(`Given the '${handleOperationWith.name}' helper for viem`, { timeout: 10000 }, () => {
17 | describe('When handling the result of a transaction mutation', () => {
18 | it('Then it should be possible to chain them with other helpers', async () => {
19 | const result = await loginAsAccountOwner().andThen((sessionClient) =>
20 | post(sessionClient, {
21 | contentUri: uri('https://devnet.irys.xyz/3n3Ujg3jPBHX58MPPqYXBSQtPhTgrcTk4RedJgV1Ejhb'),
22 | })
23 | .andThen(handleOperationWith(walletClient))
24 | .andThen(sessionClient.waitForTransaction),
25 | );
26 |
27 | expect(result.isOk(), result.isErr() ? result.error.message : undefined).toBe(true);
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/packages/client/src/viem/sponsorship.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, it } from 'vitest';
2 |
3 | import { http, createWalletClient } from 'viem';
4 | import { privateKeyToAccount } from 'viem/accounts';
5 | import { waitForTransactionReceipt } from 'viem/actions';
6 | import { sendTransaction } from 'viem/zksync';
7 | import {
8 | CHAIN,
9 | GLOBAL_SPONSORSHIP,
10 | PRIVATE_KEY,
11 | SPONSORSHIP_APPROVER_PRIVATE_KEY,
12 | } from '../test-utils';
13 | import { SponsorshipApprovalSigner } from './sponsorship';
14 |
15 | const user = createWalletClient({
16 | account: privateKeyToAccount(PRIVATE_KEY),
17 | chain: CHAIN,
18 | transport: http(),
19 | });
20 |
21 | const signer = createWalletClient({
22 | account: privateKeyToAccount(SPONSORSHIP_APPROVER_PRIVATE_KEY),
23 | chain: CHAIN,
24 | transport: http(),
25 | });
26 |
27 | describe(
28 | `Given an instance of the '${SponsorshipApprovalSigner.name}' for viem`,
29 | { timeout: 10_000 },
30 | () => {
31 | describe('When approving a transaction Request', () => {
32 | it('Then it should return the corresponding EIP-712 SendTransactionRequest with sponsorship approval', async () => {
33 | const approver = new SponsorshipApprovalSigner({
34 | signer,
35 | sponsorship: GLOBAL_SPONSORSHIP,
36 | });
37 |
38 | const approved = await approver.approveSponsorship({
39 | account: user.account,
40 | to: user.account.address,
41 | value: 1n,
42 | });
43 |
44 | // biome-ignore lint/suspicious/noExplicitAny: needs a fix in viem/zksync
45 | const hash = await sendTransaction(user, approved as any);
46 | await waitForTransactionReceipt(user, { hash });
47 | });
48 | });
49 | },
50 | );
51 |
--------------------------------------------------------------------------------
/packages/client/src/viem/types.ts:
--------------------------------------------------------------------------------
1 | import { chains } from '@lens-chain/sdk/viem';
2 | import type { Account, Chain, Transport, WalletClient } from 'viem';
3 |
4 | /**
5 | * @internal
6 | */
7 | export function isOnLensChain(
8 | client: WalletClient,
9 | ): client is WalletClient {
10 | return client.chain?.id === chains.mainnet.id || client.chain?.id === chains.testnet.id;
11 | }
12 |
13 | /**
14 | * @internal
15 | */
16 | export function hasHoistedAccount(
17 | client: WalletClient,
18 | ): client is WalletClient {
19 | return client.account !== undefined;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/client/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/**/*.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/packages/client/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/client/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: [
6 | 'src/index.ts',
7 | 'src/actions/index.ts',
8 | 'src/ethers/index.ts',
9 | 'src/viem/index.ts',
10 | 'src/test-utils.ts',
11 | ],
12 | outDir: 'dist',
13 | splitting: false,
14 | sourcemap: true,
15 | treeshake: true,
16 | clean: true,
17 | tsconfig: 'tsconfig.build.json',
18 | bundle: true,
19 | minify: false,
20 | dts: true,
21 | platform: 'neutral',
22 | format: ['esm', 'cjs'],
23 | }));
24 |
--------------------------------------------------------------------------------
/packages/env/README.md:
--------------------------------------------------------------------------------
1 | # `@lens-protocol/env`
2 |
3 | The Lens environments.
4 |
5 | ---
6 |
7 | **It is not intended to be used directly. Its interface will change without notice, use it at your own risk.**
8 |
9 |
--------------------------------------------------------------------------------
/packages/env/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lens-protocol/env",
3 | "version": "0.1.0",
4 | "description": "The Lens environments.",
5 | "repository": {
6 | "directory": "packages/env",
7 | "type": "git",
8 | "url": "git://github.com/lens-protocol/lens-sdk.git"
9 | },
10 | "type": "module",
11 | "main": "dist/index.cjs",
12 | "module": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "exports": {
15 | ".": {
16 | "import": "./dist/index.js",
17 | "require": "./dist/index.cjs"
18 | }
19 | },
20 | "typesVersions": {
21 | "*": {
22 | "import": ["./dist/index.d.ts"],
23 | "require": ["./dist/index.d.cts"]
24 | }
25 | },
26 | "files": ["dist"],
27 | "sideEffects": false,
28 | "scripts": {
29 | "build": "tsup"
30 | },
31 | "dependencies": {
32 | "@lens-protocol/types": "workspace:*"
33 | },
34 | "devDependencies": {
35 | "@types/node": "^22.7.8",
36 | "tsup": "^8.3.5",
37 | "typescript": "^5.6.3"
38 | },
39 | "license": "MIT"
40 | }
41 |
--------------------------------------------------------------------------------
/packages/env/src/index.ts:
--------------------------------------------------------------------------------
1 | import { url, type URL } from '@lens-protocol/types';
2 |
3 | /**
4 | * The environment configuration type.
5 | */
6 | export type EnvironmentConfig = {
7 | name: string;
8 | backend: URL;
9 | indexingTimeout: number;
10 | pollingInterval: number;
11 | };
12 |
13 | /**
14 | * The mainnet environment configuration.
15 | *
16 | * Use this environment for the live instance of your application, involving real users, real accounts, and real data.
17 | */
18 | export const mainnet: EnvironmentConfig = {
19 | name: 'mainnet',
20 | backend: url('https://api.lens.xyz/graphql'),
21 | indexingTimeout: 10000,
22 | pollingInterval: 100,
23 | };
24 |
25 | /**
26 | * The testnet environment configuration.
27 | *
28 | * Use this environment to develop and test your application (test users, test accounts, test data).
29 | */
30 | export const testnet: EnvironmentConfig = {
31 | name: 'testnet',
32 | backend: url('https://api.testnet.lens.xyz/graphql'),
33 | indexingTimeout: 10000,
34 | pollingInterval: 100,
35 | };
36 |
37 | /**
38 | * @internal
39 | */
40 | export const staging: EnvironmentConfig = {
41 | name: 'staging',
42 | backend: url('https://api.staging.lens.dev/graphql'),
43 | indexingTimeout: 20000,
44 | pollingInterval: 100,
45 | };
46 |
47 | /**
48 | * @internal
49 | */
50 | export const local: EnvironmentConfig = {
51 | name: 'local',
52 | backend: url('http://localhost:3000/graphql'),
53 | indexingTimeout: 60000,
54 | pollingInterval: 100,
55 | };
56 |
--------------------------------------------------------------------------------
/packages/env/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist",
6 | "lib": ["ESNext"]
7 | },
8 | "include": ["./src/**/*.ts"],
9 | "exclude": ["node_modules", "dist"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/env/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/env/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: ['src/index.ts'],
6 | outDir: 'dist',
7 | splitting: false,
8 | sourcemap: true,
9 | treeshake: false,
10 | clean: true,
11 | tsconfig: 'tsconfig.build.json',
12 | bundle: true,
13 | minify: true,
14 | dts: true,
15 | platform: 'neutral',
16 | format: ['esm', 'cjs'],
17 | }));
18 |
--------------------------------------------------------------------------------
/packages/graphql/.gitignore:
--------------------------------------------------------------------------------
1 | src/graphql-cache.d.ts
2 |
--------------------------------------------------------------------------------
/packages/graphql/README.md:
--------------------------------------------------------------------------------
1 | # `@lens-protocol/graphql`
2 |
3 | This package contains [`gql.tada`](https://gql-tada.0no.co/) bindings for the Lens API. **It is not intended to be used directly.**
4 |
5 | **Its interface will change without notice, use it at your own risk.**
6 |
--------------------------------------------------------------------------------
/packages/graphql/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lens-protocol/graphql",
3 | "version": "0.1.0",
4 | "description": "GraphQL bindings for the Lens API",
5 | "repository": {
6 | "directory": "packages/graphql",
7 | "type": "git",
8 | "url": "git://github.com/lens-protocol/lens-sdk.git"
9 | },
10 | "type": "module",
11 | "main": "dist/index.cjs",
12 | "module": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "exports": {
15 | ".": {
16 | "import": "./dist/index.js",
17 | "require": "./dist/index.cjs"
18 | },
19 | "./test-utils": {
20 | "import": "./dist/test-utils.js",
21 | "require": "./dist/test-utils.cjs",
22 | "types": "./dist/test-utils.d.cts"
23 | }
24 | },
25 | "typesVersions": {
26 | "*": {
27 | "import": ["./dist/index.d.ts"],
28 | "require": ["./dist/index.d.cts"],
29 | "test-utils": ["./dist/test-utils.d.ts"]
30 | }
31 | },
32 | "files": ["dist"],
33 | "sideEffects": false,
34 | "scripts": {
35 | "build": "tsup",
36 | "check": "gql.tada check",
37 | "doctor": "gql.tada doctor",
38 | "gql:generate:cache": "gql.tada generate turbo",
39 | "gql:download:local": "gql-tada generate schema 'http://localhost:3000/graphql' --output './schema.graphql'",
40 | "gql:download:staging": "gql-tada generate schema 'https://api.staging.lens.dev/graphql' --output './schema.graphql'",
41 | "gql:generate": "gql-tada generate output",
42 | "gql:turbo": "gql-tada turbo",
43 | "prebuild": "pnpm run gql:turbo"
44 | },
45 | "dependencies": {
46 | "@lens-protocol/types": "workspace:*",
47 | "gql.tada": "^1.8.10",
48 | "graphql": "^16.9.0",
49 | "type-fest": "^4.26.1"
50 | },
51 | "devDependencies": {
52 | "tsup": "^8.3.5",
53 | "typescript": "^5.6.3"
54 | },
55 | "license": "MIT"
56 | }
57 |
--------------------------------------------------------------------------------
/packages/graphql/src/accounts/index.ts:
--------------------------------------------------------------------------------
1 | export * from './account';
2 | export * from './managers';
3 | export * from './signless';
4 |
--------------------------------------------------------------------------------
/packages/graphql/src/common.ts:
--------------------------------------------------------------------------------
1 | import type { PaginatedResultInfo } from './fragments';
2 |
3 | /**
4 | * A paginated list of items.
5 | */
6 | export type Paginated = {
7 | items: readonly T[];
8 | pageInfo: PaginatedResultInfo;
9 | };
10 |
11 | /**
12 | * A standardized data object.
13 | *
14 | * All GQL operations should alias their results to `value` to ensure interoperability
15 | * with this client interface.
16 | */
17 | export type StandardData = { value: T };
18 |
--------------------------------------------------------------------------------
/packages/graphql/src/fragments/index.ts:
--------------------------------------------------------------------------------
1 | export * from './account';
2 | export * from './common';
3 | export * from './media';
4 | export * from './metadata';
5 | export * from './pagination';
6 | export * from './post';
7 | export * from './primitives';
8 | export * from './transactions';
9 | export * from './username';
10 |
--------------------------------------------------------------------------------
/packages/graphql/src/fragments/media.ts:
--------------------------------------------------------------------------------
1 | import type { FragmentOf } from 'gql.tada';
2 | import { graphql } from '../graphql';
3 |
4 | export const MediaAudioFragment = graphql(
5 | `fragment MediaAudio on MediaAudio {
6 | __typename
7 | artist
8 | cover
9 | credits
10 | duration
11 | genre
12 | item
13 | kind
14 | license
15 | lyrics
16 | recordLabel
17 | type
18 | }`,
19 | );
20 | export interface MediaAudio extends FragmentOf {}
21 |
22 | export const MediaImageFragment = graphql(
23 | `fragment MediaImage on MediaImage {
24 | __typename
25 | altTag
26 | item
27 | license
28 | type
29 | width
30 | height
31 | }`,
32 | );
33 | export interface MediaImage extends FragmentOf {}
34 |
35 | export const MediaVideoFragment = graphql(
36 | `fragment MediaVideo on MediaVideo {
37 | __typename
38 | altTag
39 | cover
40 | duration
41 | item
42 | license
43 | type
44 | }`,
45 | );
46 | export interface MediaVideo extends FragmentOf {}
47 |
48 | export const AnyMediaFragment = graphql(
49 | `fragment AnyMedia on AnyMedia {
50 | ... on MediaAudio {
51 | ...MediaAudio
52 | }
53 | ... on MediaImage {
54 | ...MediaImage
55 | }
56 | ... on MediaVideo {
57 | ...MediaVideo
58 | }
59 | }
60 | `,
61 | [MediaAudioFragment, MediaImageFragment, MediaVideoFragment],
62 | );
63 | export type AnyMedia = FragmentOf;
64 |
--------------------------------------------------------------------------------
/packages/graphql/src/fragments/pagination.ts:
--------------------------------------------------------------------------------
1 | import { type FragmentDocumentFor, graphql } from '../graphql';
2 |
3 | export type PaginatedResultInfo = {
4 | __typename: 'PaginatedResultInfo';
5 | prev: string | null;
6 | next: string | null;
7 | };
8 |
9 | export const PaginatedResultInfoFragment: FragmentDocumentFor<
10 | PaginatedResultInfo,
11 | 'PaginatedResultInfo'
12 | > = graphql(
13 | `fragment PaginatedResultInfo on PaginatedResultInfo {
14 | __typename
15 | prev
16 | next
17 | }`,
18 | );
19 |
--------------------------------------------------------------------------------
/packages/graphql/src/fragments/username.ts:
--------------------------------------------------------------------------------
1 | import type { FragmentOf } from 'gql.tada';
2 | import { graphql } from '../graphql';
3 | import { NamespaceOperationValidationOutcomeFragment } from './primitives';
4 |
5 | export const LoggedInUsernameOperationsFragment = graphql(
6 | `fragment LoggedInUsernameOperations on LoggedInUsernameOperations {
7 | __typename
8 | id
9 | canRemove {
10 | ...NamespaceOperationValidationOutcome
11 | }
12 | canAssign {
13 | ...NamespaceOperationValidationOutcome
14 | }
15 | canUnassign {
16 | ...NamespaceOperationValidationOutcome
17 | }
18 | }`,
19 | [NamespaceOperationValidationOutcomeFragment],
20 | );
21 | export interface LoggedInUsernameOperations
22 | extends FragmentOf {}
23 |
24 | export const UsernameFragment = graphql(
25 | `fragment Username on Username {
26 | __typename
27 | id
28 | value
29 | localName
30 | linkedTo
31 | ownedBy
32 | timestamp
33 | namespace
34 | operations {
35 | ...LoggedInUsernameOperations
36 | }
37 | }`,
38 | [LoggedInUsernameOperationsFragment],
39 | );
40 | export type Username = FragmentOf;
41 |
--------------------------------------------------------------------------------
/packages/graphql/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './accounts';
2 | export * from './actions';
3 | export * from './admins';
4 | export * from './app';
5 | export * from './authentication';
6 | export * from './common';
7 | export * from './enums';
8 | export * from './feed';
9 | export * from './follow';
10 | export * from './fragments';
11 | export * from './frames';
12 | export * from './funds';
13 | export * from './graph';
14 | export * from './graphql';
15 | export * from './group';
16 | export * from './metadata';
17 | export * from './misc';
18 | export * from './ml';
19 | export * from './namespace';
20 | export * from './notifications';
21 | export * from './post';
22 | export * from './refinements';
23 | export * from './scalars';
24 | export * from './sns';
25 | export * from './sponsorship';
26 | export * from './timeline';
27 | export * from './transactions';
28 | export * from './transferOwnership';
29 | export * from './username';
30 |
--------------------------------------------------------------------------------
/packages/graphql/src/metadata.ts:
--------------------------------------------------------------------------------
1 | import type { FragmentOf } from 'gql.tada';
2 | import { type RequestOf, graphql } from './graphql';
3 |
4 | export const RefreshMetadataStatusResultFragment = graphql(
5 | `fragment RefreshMetadataStatusResult on RefreshMetadataStatusResult {
6 | __typename
7 | id
8 | status
9 | reason
10 | updatedAt
11 | }`,
12 | );
13 | export type RefreshMetadataStatusResult = FragmentOf;
14 |
15 | export const RefreshMetadataStatusQuery = graphql(
16 | `query RefreshMetadataStatus($request: RefreshMetadataStatusRequest!) {
17 | value: refreshMetadataStatus(request: $request) {
18 | ...RefreshMetadataStatusResult
19 | }
20 | }`,
21 | [RefreshMetadataStatusResultFragment],
22 | );
23 | export type RefreshMetadataStatusRequest = RequestOf;
24 |
25 | export const RefreshMetadataResultFragment = graphql(
26 | `fragment RefreshMetadataResult on RefreshMetadataResult {
27 | __typename
28 | id
29 | }`,
30 | );
31 | export type RefreshMetadataResult = FragmentOf;
32 |
33 | export const RefreshMetadataMutation = graphql(
34 | `mutation RefreshMetadata($request: RefreshMetadataRequest!) {
35 | value: refreshMetadata(request: $request){
36 | ...RefreshMetadataResult
37 | }
38 | }`,
39 | [RefreshMetadataResultFragment],
40 | );
41 | export type RefreshMetadataRequest = RequestOf;
42 |
--------------------------------------------------------------------------------
/packages/graphql/src/misc.ts:
--------------------------------------------------------------------------------
1 | import type { FragmentOf } from 'gql.tada';
2 | import { type RequestOf, graphql } from './graphql';
3 |
4 | export const HealthQuery = graphql(
5 | `query Health {
6 | value: health
7 | }`,
8 | );
9 |
10 | const AccessControlResultFragment = graphql(`
11 | fragment AccessControlResult on AccessControlResult {
12 | address
13 | createdAt
14 | }`);
15 | export type AccessControlResult = FragmentOf;
16 |
17 | export const AccessControlQuery = graphql(
18 | `query AccessControl($request: AccessControlRequest!) {
19 | value: accessControl(request: $request) {
20 | ...AccessControlResult
21 | }
22 | }`,
23 | [AccessControlResultFragment],
24 | );
25 | export type AccessControlRequest = RequestOf;
26 |
--------------------------------------------------------------------------------
/packages/graphql/src/refinements.ts:
--------------------------------------------------------------------------------
1 | import { invariant } from '@lens-protocol/types';
2 | import type { AnyPost, Post } from './fragments';
3 |
4 | /**
5 | * Refine the type of a Post from AnyPost.
6 | */
7 | export function justPost(post: AnyPost): Post {
8 | invariant(post.__typename === 'Post', `Expected AnyPost ${post.id} to be a Post`);
9 | return post;
10 | }
11 |
--------------------------------------------------------------------------------
/packages/graphql/src/scalars.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged } from 'type-fest';
2 |
3 | /**
4 | * A string that represents a server API key.
5 | */
6 | export type ServerAPIKey = Tagged;
7 |
--------------------------------------------------------------------------------
/packages/graphql/src/schema.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.graphql' {
2 | const content: string;
3 | export default content;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/graphql/src/sns.ts:
--------------------------------------------------------------------------------
1 | import type { FragmentOf } from 'gql.tada';
2 | import { type RequestOf, graphql } from './graphql';
3 |
4 | const SnsSubscriptionFragment = graphql(
5 | `fragment SnsSubscription on SnsSubscription {
6 | __typename
7 | id
8 | account
9 | webhook
10 | app
11 | topic
12 | topicArn
13 | filter
14 | }`,
15 | );
16 | export type SnsSubscription = FragmentOf;
17 |
18 | export const GetSnsSubscriptionsQuery = graphql(
19 | `query GetSnsSubscriptions($request: GetSnsSubscriptionsRequest!) {
20 | value: getSnsSubscriptions(request: $request) {
21 | ...SnsSubscription
22 | }
23 | }`,
24 | [SnsSubscriptionFragment],
25 | );
26 | export type GetSnsSubscriptionsRequest = RequestOf;
27 |
28 | export const CreateSnsSubscriptionsMutation = graphql(
29 | `mutation CreateSnsSubscriptions($request: CreateSnsSubscriptionRequest!) {
30 | value: createSnsSubscriptions(request: $request) {
31 | ...SnsSubscription
32 | }
33 | }`,
34 | [SnsSubscriptionFragment],
35 | );
36 | export type CreateSnsSubscriptionRequest = RequestOf;
37 |
38 | export const DeleteSnsSubscriptionMutation = graphql(
39 | `mutation DeleteSnsSubscription($request: DeleteSnsSubscriptionRequest!) {
40 | value: deleteSnsSubscription(request: $request)
41 | }`,
42 | );
43 | export type DeleteSnsSubscriptionRequest = RequestOf;
44 |
--------------------------------------------------------------------------------
/packages/graphql/src/test-utils.ts:
--------------------------------------------------------------------------------
1 | import { buildSchema } from 'graphql';
2 |
3 | import schemaSDL from '../schema.graphql';
4 |
5 | /**
6 | * @internal
7 | */
8 | export const schema = buildSchema(schemaSDL);
9 |
--------------------------------------------------------------------------------
/packages/graphql/src/timeline.ts:
--------------------------------------------------------------------------------
1 | import type { UUID } from '@lens-protocol/types';
2 | import type { Prettify } from '@lens-protocol/types';
3 | import {
4 | PaginatedResultInfoFragment,
5 | type Post,
6 | PostFragment,
7 | type Repost,
8 | RepostFragment,
9 | } from './fragments';
10 | import { type FragmentDocumentFor, type RequestOf, graphql } from './graphql';
11 |
12 | export type TimelineItem = Prettify<{
13 | __typename: 'TimelineItem';
14 | id: UUID;
15 | primary: Post;
16 | comments: Post[];
17 | reposts: Repost[];
18 | }>;
19 |
20 | // mitigates error TS7056: The inferred type of this node exceeds the maximum length
21 | // the compiler will serialize. An explicit type annotation is needed.
22 | const TimelineItemFragment: FragmentDocumentFor = graphql(
23 | `fragment TimelineItem on TimelineItem {
24 | __typename
25 | id
26 | primary {
27 | ...Post
28 | }
29 | comments {
30 | ...Post
31 | }
32 | reposts {
33 | ...Repost
34 | }
35 | }`,
36 | [PostFragment, RepostFragment],
37 | );
38 |
39 | export const TimelineQuery = graphql(
40 | `query Timeline($request: TimelineRequest!) {
41 | value: timeline(request: $request) {
42 | items {
43 | ...TimelineItem
44 | }
45 | pageInfo {
46 | ...PaginatedResultInfo
47 | }
48 | }
49 | }`,
50 | [TimelineItemFragment, PaginatedResultInfoFragment],
51 | );
52 | export type TimelineRequest = RequestOf;
53 |
54 | export const TimelineHighlightsQuery = graphql(
55 | `query TimelineHighlights($request: TimelineHighlightsRequest!) {
56 | value: timelineHighlights(request: $request) {
57 | items {
58 | ...Post
59 | }
60 | pageInfo {
61 | ...PaginatedResultInfo
62 | }
63 | }
64 | }`,
65 | [PostFragment, PaginatedResultInfoFragment],
66 | );
67 | export type TimelineHighlightsRequest = RequestOf;
68 |
--------------------------------------------------------------------------------
/packages/graphql/src/transferOwnership.ts:
--------------------------------------------------------------------------------
1 | import type { FragmentOf } from 'gql.tada';
2 | import {
3 | SelfFundedTransactionRequestFragment,
4 | SponsoredTransactionRequestFragment,
5 | TransactionWillFailFragment,
6 | } from './fragments';
7 | import { type RequestOf, graphql } from './graphql';
8 |
9 | const TransferPrimitiveOwnershipResultFragment = graphql(
10 | `fragment TransferPrimitiveOwnershipResult on TransferPrimitiveOwnershipResult {
11 | ...on SponsoredTransactionRequest {
12 | ...SponsoredTransactionRequest
13 | }
14 | ...on SelfFundedTransactionRequest {
15 | ...SelfFundedTransactionRequest
16 | }
17 | ...on TransactionWillFail {
18 | ...TransactionWillFail
19 | }
20 | }`,
21 | [
22 | SponsoredTransactionRequestFragment,
23 | SelfFundedTransactionRequestFragment,
24 | TransactionWillFailFragment,
25 | ],
26 | );
27 | export type TransferPrimitiveOwnershipResult = FragmentOf<
28 | typeof TransferPrimitiveOwnershipResultFragment
29 | >;
30 |
31 | export const TransferPrimitiveOwnershipMutation = graphql(
32 | `mutation TransferPrimitiveOwnership($request: TransferPrimitiveOwnershipRequest!) {
33 | value: transferPrimitiveOwnership(request: $request) {
34 | ...TransferPrimitiveOwnershipResult
35 | }
36 | }`,
37 | [TransferPrimitiveOwnershipResultFragment],
38 | );
39 | export type TransferPrimitiveOwnershipRequest = RequestOf<
40 | typeof TransferPrimitiveOwnershipMutation
41 | >;
42 |
--------------------------------------------------------------------------------
/packages/graphql/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist",
6 | "declarationMap": true
7 | },
8 | "include": ["src/**/*.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/graphql/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"],
4 | "compilerOptions": {
5 | "plugins": [
6 | {
7 | "name": "gql.tada/ts-plugin",
8 | "schema": "./schema.graphql",
9 | "tadaOutputLocation": "./src/graphql-env.d.ts",
10 | "tadaTurboLocation": "./src/graphql-cache.d.ts",
11 | "shouldCheckForColocatedFragments": false,
12 | "trackFieldUsage": false
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/graphql/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: ['src/index.ts', 'src/test-utils.ts'],
6 | outDir: 'dist',
7 | splitting: false,
8 | sourcemap: true,
9 | treeshake: true,
10 | clean: true,
11 | tsconfig: 'tsconfig.build.json',
12 | bundle: true,
13 | minify: true,
14 | dts: true,
15 | platform: 'neutral',
16 | format: ['esm', 'cjs'],
17 | loader: {
18 | '.graphql': 'text',
19 | },
20 | }));
21 |
--------------------------------------------------------------------------------
/packages/react/README.md:
--------------------------------------------------------------------------------
1 | # `@lens-protocol/react`
2 |
3 | The official React bindings for the Lens Protocol.
4 |
5 | ---
6 |
7 | TBD
8 |
9 |
--------------------------------------------------------------------------------
/packages/react/src/LensProvider.tsx:
--------------------------------------------------------------------------------
1 | import type { PublicClient } from '@lens-protocol/client';
2 | import React from 'react';
3 | import type { ReactNode } from 'react';
4 |
5 | import { LensContextProvider } from './context';
6 |
7 | /**
8 | * props
9 | */
10 | export type LensProviderProps = {
11 | /**
12 | * The children to render
13 | */
14 | children: ReactNode;
15 | /**
16 | * The Lens client to use
17 | */
18 | client: PublicClient;
19 | };
20 |
21 | /**
22 | * Manages the lifecycle and internal state of the Lens SDK
23 | *
24 | * ```tsx
25 | * import { LensProvider, PublicClient, testnet } from '@lens-protocol/react';
26 | *
27 | * const client = PublicClient.create({
28 | * environment: testnet,
29 | * });
30 | *
31 | * function App() {
32 | * return (
33 | *
34 | * // ...
35 | *
36 | * );
37 | * }
38 | * ```
39 | */
40 | export function LensProvider({ children, client }: LensProviderProps) {
41 | return {children};
42 | }
43 |
--------------------------------------------------------------------------------
/packages/react/src/account/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useAccount';
2 | export * from './useAccountManagers';
3 | export * from './useAccounts';
4 | export * from './useAccountsBlocked';
5 | export * from './useAccountsBulk';
6 |
--------------------------------------------------------------------------------
/packages/react/src/account/useAccount.ts:
--------------------------------------------------------------------------------
1 | import type { Account, AccountRequest } from '@lens-protocol/graphql';
2 | import { AccountQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type UseAccountArgs = AccountRequest;
8 |
9 | /**
10 | * Fetch a single Account.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useAccount({ address: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useAccount(args: UseAccountArgs & Suspendable): SuspenseResult;
19 |
20 | /**
21 | * Fetch a single Account.
22 | *
23 | * ```tsx
24 | * const { data, loading } = useAccount({ address: evmAddress('0x…') });
25 | * ```
26 | */
27 | export function useAccount(args: UseAccountArgs): ReadResult;
28 |
29 | export function useAccount({
30 | suspense = false,
31 | ...request
32 | }: UseAccountArgs & { suspense?: boolean }): SuspendableResult {
33 | return useSuspendableQuery({
34 | document: AccountQuery,
35 | variables: { request },
36 | suspense,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react/src/account/useAccountManagers.ts:
--------------------------------------------------------------------------------
1 | import type { AccountManager, AccountManagersRequest, Paginated } from '@lens-protocol/graphql';
2 | import { AccountManagersQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type AccountManagersArgs = AccountManagersRequest;
8 |
9 | /**
10 | * Fetch Account Managers.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useAccountManagers({ suspense: true });
16 | * ```
17 | */
18 | export function useAccountManagers(
19 | args: AccountManagersArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch Account Managers.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useAccountManagers();
27 | * ```
28 | */
29 | export function useAccountManagers(
30 | args?: AccountManagersArgs,
31 | ): ReadResult>;
32 |
33 | export function useAccountManagers({
34 | suspense = false,
35 | ...request
36 | }: AccountManagersArgs & { suspense?: boolean } = {}): SuspendableResult<
37 | Paginated
38 | > {
39 | return useSuspendableQuery({
40 | document: AccountManagersQuery,
41 | variables: { request },
42 | suspense: suspense,
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react/src/account/useAccounts.ts:
--------------------------------------------------------------------------------
1 | import type { Account, AccountsRequest, Paginated } from '@lens-protocol/graphql';
2 | import { AccountsQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type AccountsArgs = AccountsRequest;
8 |
9 | /**
10 | * Fetch accounts available filtered by the given arguments.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useAccounts({
16 | * filter: {
17 | * searchBy: {
18 | * localNameQuery: 'wagmi'
19 | * },
20 | * },
21 | * suspense: true
22 | * });
23 | * ```
24 | */
25 | export function useAccounts(args: AccountsArgs & Suspendable): SuspenseResult>;
26 |
27 | /**
28 | * Fetch accounts available filtered by the given arguments.
29 | *
30 | * ```tsx
31 | * const { data, loading } = useAccounts({
32 | * filter: {
33 | * searchBy: {
34 | * localNameQuery: 'wagmi'
35 | * },
36 | * },
37 | * );
38 | * ```
39 | */
40 | export function useAccounts(args: AccountsArgs): ReadResult>;
41 |
42 | export function useAccounts({
43 | suspense = false,
44 | ...request
45 | }: AccountsArgs & { suspense?: boolean }): SuspendableResult> {
46 | return useSuspendableQuery({
47 | document: AccountsQuery,
48 | variables: { request },
49 | suspense: suspense,
50 | });
51 | }
52 |
--------------------------------------------------------------------------------
/packages/react/src/account/useAccountsBlocked.ts:
--------------------------------------------------------------------------------
1 | import type { AccountBlocked, AccountsBlockedRequest, Paginated } from '@lens-protocol/graphql';
2 | import { AccountsBlockedQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type AccountsBlockedArgs = AccountsBlockedRequest;
8 |
9 | /**
10 | * Fetch Blocked Accounts.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useAccountsBlocked({ suspense: true });
16 | * ```
17 | */
18 | export function useAccountsBlocked(
19 | args: AccountsBlockedArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch Blocked Accounts.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useAccountsBlocked();
27 | * ```
28 | */
29 | export function useAccountsBlocked(
30 | args?: AccountsBlockedArgs,
31 | ): ReadResult>;
32 |
33 | export function useAccountsBlocked({
34 | suspense = false,
35 | ...request
36 | }: AccountsBlockedArgs & { suspense?: boolean } = {}): SuspendableResult<
37 | Paginated
38 | > {
39 | return useSuspendableQuery({
40 | document: AccountsBlockedQuery,
41 | variables: { request },
42 | suspense: suspense,
43 | });
44 | }
45 |
--------------------------------------------------------------------------------
/packages/react/src/account/useAccountsBulk.ts:
--------------------------------------------------------------------------------
1 | import type { Account, AccountsBulkRequest } from '@lens-protocol/graphql';
2 | import { AccountsBulkQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type AccountsBulkArgs = AccountsBulkRequest;
8 |
9 | /**
10 | * Fetch Accounts in Bulk.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useAccountsBulk({
16 | * addresses: [evmAddress('0x…'), evmAddress('0x…')],
17 | * suspense: true
18 | * });
19 | * ```
20 | */
21 | export function useAccountsBulk(args: AccountsBulkArgs & Suspendable): SuspenseResult;
22 |
23 | /**
24 | * Fetch Accounts in Bulk.
25 | *
26 | * ```tsx
27 | * const { data, loading } = useAccountsBulk({
28 | * addresses: [evmAddress('0x…'), evmAddress('0x…')]
29 | * });
30 | * ```
31 | */
32 | export function useAccountsBulk(args: AccountsBulkArgs): ReadResult;
33 |
34 | export function useAccountsBulk({
35 | suspense = false,
36 | ...request
37 | }: AccountsBulkArgs & { suspense?: boolean }): SuspendableResult {
38 | return useSuspendableQuery({
39 | document: AccountsBulkQuery,
40 | variables: { request },
41 | suspense: suspense,
42 | });
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useAccountsAvailable';
2 | export * from './useAuthenticatedUser';
3 | export * from './useImpersonation';
4 | export * from './useLogin';
5 | export * from './useLogout';
6 | export * from './usePublicClient';
7 | export * from './useSessionClient';
8 | export * from './useSwitchAccount';
9 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useAccountsAvailable.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type AccountAvailable,
3 | AccountsAvailableQuery,
4 | type AccountsAvailableRequest,
5 | type Paginated,
6 | } from '@lens-protocol/graphql';
7 | import {
8 | type ReadResult,
9 | type Suspendable,
10 | type SuspendableResult,
11 | type SuspenseResult,
12 | useSuspendableQuery,
13 | } from '../helpers';
14 |
15 | export type UseAccountsAvailableArgs = AccountsAvailableRequest;
16 |
17 | /**
18 | * Fetch the accounts available for a given address.
19 | *
20 | * This signature supports React Suspense:
21 | *
22 | * ```tsx
23 | * const { data } = useAccountsAvailable({ managedBy: evmAddress('0x…'), suspense: true });
24 | * ```
25 | */
26 | export function useAccountsAvailable(
27 | args: UseAccountsAvailableArgs & Suspendable,
28 | ): SuspenseResult>;
29 |
30 | /**
31 | * Fetch the accounts available for a given address.
32 | *
33 | * ```tsx
34 | * const { data, loading } = useAccountsAvailable({ managedBy: evmAddress('0x…') });
35 | * ```
36 | */
37 | export function useAccountsAvailable(
38 | args: UseAccountsAvailableArgs,
39 | ): ReadResult>;
40 |
41 | export function useAccountsAvailable({
42 | suspense = false,
43 | ...request
44 | }: UseAccountsAvailableArgs & { suspense?: boolean }): SuspendableResult<
45 | Paginated
46 | > {
47 | return useSuspendableQuery({
48 | document: AccountsAvailableQuery,
49 | variables: { request },
50 | suspense: suspense,
51 | });
52 | }
53 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useAuthenticatedUser.ts:
--------------------------------------------------------------------------------
1 | import type { AuthenticatedUser } from '@lens-protocol/client';
2 |
3 | import { useSessionState } from '../context';
4 | import type { ReadResult, SuspenseResult } from '../helpers';
5 |
6 | /**
7 | * {@link useAuthenticatedUser} hook arguments
8 | */
9 | export type UseAuthenticatedUserArgs = {
10 | /**
11 | * Enables React Suspense support.
12 | */
13 | suspense: true;
14 | };
15 |
16 | /**
17 | * Retrieve the {@link AuthenticatedUser} if available.
18 | *
19 | * This signature supports React Suspense:
20 | *
21 | * ```tsx
22 | * import { useAuthenticatedUser } from '@lens-protocol/react';
23 | *
24 | * const { data } = useAuthenticatedUser({ suspense: true });
25 | * ```
26 | */
27 | export function useAuthenticatedUser(
28 | args: UseAuthenticatedUserArgs,
29 | ): SuspenseResult;
30 |
31 | /**
32 | * Retrieve the {@link AuthenticatedUser} if available.
33 | *
34 | * ```tsx
35 | * import { useAuthenticatedUser } from '@lens-protocol/react';
36 | *
37 | * const { data, loading } = useAuthenticatedUser();
38 | * ```
39 | */
40 | export function useAuthenticatedUser(): ReadResult;
41 |
42 | export function useAuthenticatedUser(
43 | args: { suspense: boolean } = { suspense: false },
44 | ): ReadResult | SuspenseResult {
45 | const result = useSessionState(args);
46 |
47 | if (result.data) {
48 | return {
49 | ...result,
50 | data: result.data.isSessionClient()
51 | ? result.data.getAuthenticatedUser().unwrapOr(null)
52 | : null,
53 | };
54 | }
55 |
56 | return result;
57 | }
58 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useImpersonation.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AuthenticatedUser,
3 | AuthenticationError,
4 | ImpersonationRequest,
5 | UnexpectedError,
6 | } from '@lens-protocol/client';
7 | import type { ResultAsync } from '@lens-protocol/types';
8 |
9 | import { useLensContext } from '../context';
10 | import { type UseAsyncTask, useAsyncTask } from '../helpers';
11 | import { usePublicClient } from './usePublicClient';
12 |
13 | export type ImpersonationError = AuthenticationError | UnexpectedError;
14 |
15 | /**
16 | * @internal
17 | */
18 | export function useImpersonation(): UseAsyncTask<
19 | ImpersonationRequest,
20 | AuthenticatedUser,
21 | ImpersonationError
22 | > {
23 | const { afterLogin } = useLensContext();
24 | const publicClient = usePublicClient();
25 |
26 | return useAsyncTask(
27 | (params: ImpersonationRequest): ResultAsync =>
28 | publicClient
29 | .impersonate(params)
30 | .andTee(afterLogin)
31 | .andThen((sessionClient) => sessionClient.getAuthenticatedUser()),
32 | );
33 | }
34 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useLogin.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { signMessageWith } from '@lens-protocol/client/viem';
2 | import { assertOk } from '@lens-protocol/types';
3 | import { describe, expect, it } from 'vitest';
4 |
5 | import {
6 | TEST_ACCOUNT,
7 | TEST_APP,
8 | TEST_SIGNER,
9 | createPublicClient,
10 | wallet,
11 | } from '@lens-protocol/client/test-utils';
12 | import { renderHookWithContext } from '../test-utils';
13 | import { useLogin } from './useLogin';
14 |
15 | describe(`Given the ${useLogin.name} hook`, () => {
16 | const client = createPublicClient();
17 |
18 | describe('When used to login into an existing Account', () => {
19 | it('Then it should return the Account data', async () => {
20 | const { result } = renderHookWithContext(() => useLogin(), {
21 | client,
22 | });
23 |
24 | const record = await result.current.execute({
25 | accountOwner: {
26 | account: TEST_ACCOUNT,
27 | owner: TEST_SIGNER,
28 | app: TEST_APP,
29 | },
30 | signMessage: signMessageWith(wallet),
31 | });
32 |
33 | assertOk(record);
34 | expect(record.value).toHaveProperty('address', TEST_ACCOUNT.toLowerCase());
35 | });
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useLogout.ts:
--------------------------------------------------------------------------------
1 | import type { UnauthenticatedError, UnexpectedError } from '@lens-protocol/client';
2 | import { invariant } from '@lens-protocol/types';
3 |
4 | import { useLensContext } from '../context';
5 | import { type UseAsyncTask, useAsyncTask } from '../helpers';
6 | import { useSessionClient } from './useSessionClient';
7 |
8 | export type LogoutError = UnauthenticatedError | UnexpectedError;
9 |
10 | /**
11 | * Log out of Lens.
12 | *
13 | * ```tsx
14 | * const { execute, error } = useLogout();
15 | * ```
16 | */
17 | export function useLogout(): UseAsyncTask {
18 | const { afterLogout } = useLensContext();
19 | const { data: sessionClient } = useSessionClient();
20 |
21 | return useAsyncTask(() => {
22 | invariant(
23 | sessionClient,
24 | 'It appears that you are not logged in. Please log in before attempting to log out.',
25 | );
26 |
27 | return sessionClient.logout().andTee(afterLogout);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/usePublicClient.ts:
--------------------------------------------------------------------------------
1 | import type { PublicClient } from '@lens-protocol/client';
2 |
3 | import { useLensContext } from '../context';
4 |
5 | /**
6 | * Retrieve the injected {@link PublicClient} from the context.
7 | */
8 | export function usePublicClient(): PublicClient {
9 | const { state } = useLensContext();
10 |
11 | return state.client.isSessionClient() ? state.client.parent : state.client;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useSessionClient.ts:
--------------------------------------------------------------------------------
1 | import type { SessionClient } from '@lens-protocol/client';
2 | import { useSessionState } from '../context';
3 | import type { ReadResult, SuspenseResult } from '../helpers';
4 |
5 | /**
6 | * {@link useSessionClient} hook arguments
7 | */
8 | export type UseSessionArgs = {
9 | /**
10 | * Enables React Suspense support.
11 | */
12 | suspense: true;
13 | };
14 |
15 | /**
16 | * Retrieve the current {@link SessionClient} if available.
17 | * If the session is not available, it will attempt to resume it from storage.
18 | *
19 | * This signature supports React Suspense:
20 | *
21 | * ```tsx
22 | * import { useSessionClient } from '@lens-protocol/react';
23 | *
24 | * const { data } = useSessionClient({ suspense: true });
25 | * ```
26 | */
27 | export function useSessionClient(args: UseSessionArgs): SuspenseResult;
28 |
29 | /**
30 | * Retrieve the current {@link SessionClient} if available.
31 | * If the session is not available, it will attempt to resume it from storage.
32 | *
33 | * ```tsx
34 | * import { useSessionClient } from '@lens-protocol/react';
35 | *
36 | * const { data, loading } = useSessionClient();
37 | * ```
38 | */
39 | export function useSessionClient(): ReadResult;
40 |
41 | export function useSessionClient(
42 | args: { suspense: boolean } = { suspense: false },
43 | ): ReadResult | SuspenseResult {
44 | const result = useSessionState(args);
45 |
46 | if (result.data) {
47 | return {
48 | ...result,
49 | data: result.data.isSessionClient() ? result.data : null,
50 | };
51 | }
52 |
53 | return result;
54 | }
55 |
--------------------------------------------------------------------------------
/packages/react/src/authentication/useSwitchAccount.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | AuthenticatedUser,
3 | AuthenticationError,
4 | LoginParams,
5 | SignMessage,
6 | UnauthenticatedError,
7 | UnexpectedError,
8 | } from '@lens-protocol/client';
9 | import type { SwitchAccountRequest } from '@lens-protocol/graphql';
10 | import { type ResultAsync, invariant } from '@lens-protocol/types';
11 |
12 | import { useLensContext } from '../context';
13 | import { type UseAsyncTask, useAsyncTask } from '../helpers';
14 | import { useSessionClient } from './useSessionClient';
15 |
16 | export type { LoginParams, SignMessage };
17 |
18 | export type SwitchAccountError = AuthenticationError | UnauthenticatedError | UnexpectedError;
19 |
20 | /**
21 | * Switch to a different account.
22 | *
23 | * ```tsx
24 | * import { evmAddress, useSwitchAccount } from '@lens-protocol/react';
25 | *
26 | * const { execute } = useSwitchAccount();
27 | *
28 | * const result = await execute({
29 | * account: evmAddress('0xB8d87f414EDc074A1808497BA2Fefc0fb37164C3'),
30 | * });
31 | *
32 | * if (result.isOk()) {
33 | * console.log(result.value); // AuthenticatedUser
34 | * }
35 | * ```
36 | */
37 | export function useSwitchAccount(): UseAsyncTask<
38 | SwitchAccountRequest,
39 | AuthenticatedUser,
40 | SwitchAccountError
41 | > {
42 | const { afterLogin } = useLensContext();
43 | const { data: sessionClient } = useSessionClient();
44 |
45 | return useAsyncTask(
46 | (request: SwitchAccountRequest): ResultAsync => {
47 | invariant(
48 | sessionClient,
49 | 'It appears that you are not logged in. Please log in before attempting to switch account.',
50 | );
51 |
52 | return sessionClient
53 | .switchAccount(request)
54 | .andTee(afterLogin)
55 | .andThen((sessionClient) => sessionClient.getAuthenticatedUser());
56 | },
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/packages/react/src/ethers.ts:
--------------------------------------------------------------------------------
1 | export * from '@lens-protocol/client/ethers';
2 |
--------------------------------------------------------------------------------
/packages/react/src/feed/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useFeed';
2 |
--------------------------------------------------------------------------------
/packages/react/src/feed/useFeed.ts:
--------------------------------------------------------------------------------
1 | import type { Feed, FeedRequest } from '@lens-protocol/graphql';
2 | import { FeedQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type FeedArgs = FeedRequest;
8 |
9 | /**
10 | * Fetch a single Feed.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useFeed({ feed: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useFeed(args: FeedArgs & Suspendable): SuspenseResult;
19 |
20 | /**
21 | * Fetch a single Feed.
22 | *
23 | * ```tsx
24 | * const { data, loading } = useFeed({ feed: evmAddress('0x…') });
25 | * ```
26 | */
27 | export function useFeed(args: FeedArgs): ReadResult;
28 |
29 | export function useFeed({
30 | suspense = false,
31 | ...request
32 | }: FeedArgs & { suspense?: boolean }): SuspendableResult {
33 | return useSuspendableQuery({
34 | document: FeedQuery,
35 | variables: { request },
36 | suspense,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react/src/follow/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useFollowers';
2 | export * from './useFollowersYouKnow';
3 | export * from './useFollowing';
4 |
--------------------------------------------------------------------------------
/packages/react/src/follow/useFollowers.ts:
--------------------------------------------------------------------------------
1 | import type { Follower, FollowersRequest, Paginated } from '@lens-protocol/graphql';
2 | import { FollowersQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type FollowersArgs = FollowersRequest;
8 |
9 | /**
10 | * Fetch followers accounts.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useFollowers({ account: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useFollowers(
19 | args: FollowersArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch followers accounts.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useFollowers({ account: evmAddress('0x…') });
27 | * ```
28 | */
29 | export function useFollowers(args: FollowersArgs): ReadResult>;
30 |
31 | export function useFollowers({
32 | suspense = false,
33 | ...request
34 | }: FollowersArgs & { suspense?: boolean }): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: FollowersQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/follow/useFollowersYouKnow.ts:
--------------------------------------------------------------------------------
1 | import type { Follower, FollowersYouKnowRequest, Paginated } from '@lens-protocol/graphql';
2 | import { FollowersYouKnowQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type FollowersYouKnowArgs = FollowersYouKnowRequest;
8 |
9 | /**
10 | * Fetch followers you know.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useFollowersYouKnow({
16 | * observer: evmAddress('0x…'),
17 | * target: evmAddress('0x…'),
18 | * suspense: true
19 | * });
20 | * ```
21 | */
22 | export function useFollowersYouKnow(
23 | args: FollowersYouKnowArgs & Suspendable,
24 | ): SuspenseResult>;
25 |
26 | /**
27 | * Fetch followers you know.
28 | *
29 | * ```tsx
30 | * const { data, loading } = useFollowersYouKnow({
31 | * observer: evmAddress('0x…'),
32 | * target: evmAddress('0x…'),
33 | * });
34 | * ```
35 | */
36 | export function useFollowersYouKnow(args: FollowersYouKnowArgs): ReadResult>;
37 |
38 | export function useFollowersYouKnow({
39 | suspense = false,
40 | ...request
41 | }: FollowersYouKnowArgs & { suspense?: boolean }): SuspendableResult> {
42 | return useSuspendableQuery({
43 | document: FollowersYouKnowQuery,
44 | variables: { request },
45 | suspense: suspense,
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/src/follow/useFollowing.ts:
--------------------------------------------------------------------------------
1 | import type { Following, FollowingRequest, Paginated } from '@lens-protocol/graphql';
2 | import { FollowingQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type FollowingArgs = FollowingRequest;
8 |
9 | /**
10 | * Fetch accounts following.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useFollowing({ account: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useFollowing(
19 | args: FollowingArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch accounts following.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useFollowing({ account: evmAddress('0x…') });
27 | * ```
28 | */
29 | export function useFollowing(args: FollowingArgs): ReadResult>;
30 |
31 | export function useFollowing({
32 | suspense = false,
33 | ...request
34 | }: FollowingArgs & { suspense?: boolean }): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: FollowingQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/graph/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useGraph';
2 |
--------------------------------------------------------------------------------
/packages/react/src/graph/useGraph.ts:
--------------------------------------------------------------------------------
1 | import type { Graph, GraphRequest } from '@lens-protocol/graphql';
2 | import { GraphQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type GraphArgs = GraphRequest;
8 |
9 | /**
10 | * Fetch a single Graph.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useGraph({ graph: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useGraph(args: GraphArgs & Suspendable): SuspenseResult;
19 |
20 | /**
21 | * Fetch a single Graph.
22 | *
23 | * ```tsx
24 | * const { data, loading } = useGraph({ graph: evmAddress('0x…') });
25 | * ```
26 | */
27 | export function useGraph(args: GraphArgs): ReadResult;
28 |
29 | export function useGraph({
30 | suspense = false,
31 | ...request
32 | }: GraphArgs & { suspense?: boolean }): SuspendableResult {
33 | return useSuspendableQuery({
34 | document: GraphQuery,
35 | variables: { request },
36 | suspense,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react/src/group/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useGroup';
2 | export * from './useGroups';
3 | export * from './useGroupMembers';
4 |
--------------------------------------------------------------------------------
/packages/react/src/group/useGroup.ts:
--------------------------------------------------------------------------------
1 | import type { Group, GroupRequest } from '@lens-protocol/graphql';
2 | import { GroupQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type GroupArgs = GroupRequest;
8 |
9 | /**
10 | * Fetch a single Group.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useGroup({ group: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useGroup(args: GroupArgs & Suspendable): SuspenseResult;
19 |
20 | /**
21 | * Fetch a single Group.
22 | *
23 | * ```tsx
24 | * const { data, loading } = useGroup({ group: evmAddress('0x…') });
25 | * ```
26 | */
27 | export function useGroup(args: GroupArgs): ReadResult;
28 |
29 | export function useGroup({
30 | suspense = false,
31 | ...request
32 | }: GroupArgs & { suspense?: boolean }): SuspendableResult {
33 | return useSuspendableQuery({
34 | document: GroupQuery,
35 | variables: { request },
36 | suspense,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react/src/group/useGroupMembers.ts:
--------------------------------------------------------------------------------
1 | import type { GroupMember, GroupMembersRequest, Paginated } from '@lens-protocol/graphql';
2 | import { GroupMembersQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type GroupMembersArgs = GroupMembersRequest;
8 |
9 | /**
10 | * Fetch group members.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useGroupMembers({ group: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useGroupMembers(
19 | args: GroupMembersArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch group members.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useGroups({ group: evmAddress('0x…') });
27 | * ```
28 | */
29 | export function useGroupMembers(args: GroupMembersArgs): ReadResult>;
30 |
31 | export function useGroupMembers({
32 | suspense = false,
33 | ...request
34 | }: GroupMembersArgs & { suspense?: boolean }): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: GroupMembersQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/group/useGroups.ts:
--------------------------------------------------------------------------------
1 | import type { Group, GroupsRequest, Paginated } from '@lens-protocol/graphql';
2 | import { GroupsQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type GroupsArgs = GroupsRequest;
8 |
9 | /**
10 | * Fetch groups available filtered by the given arguments.
11 | * Example: Filtered by search query.
12 | *
13 | * This signature supports React Suspense:
14 | *
15 | * ```tsx
16 | * const { data } = useGroups({
17 | * filter: {
18 | * searchQuery: 'test',
19 | * },
20 | * suspense: true
21 | * });
22 | * ```
23 | */
24 | export function useGroups(args: GroupsArgs & Suspendable): SuspenseResult>;
25 |
26 | /**
27 | * Fetch groups available filtered by the given arguments.
28 | * Example: Filtered by search query.
29 | *
30 | * ```tsx
31 | * const { data, loading } = useGroups({ filter: { searchQuery: 'test' }});
32 | * ```
33 | */
34 | export function useGroups(args: GroupsArgs): ReadResult>;
35 |
36 | export function useGroups({
37 | suspense = false,
38 | ...request
39 | }: GroupsArgs & { suspense?: boolean }): SuspendableResult> {
40 | return useSuspendableQuery({
41 | document: GroupsQuery,
42 | variables: { request },
43 | suspense: suspense,
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/packages/react/src/helpers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './reads';
2 | export * from './results';
3 | export * from './tasks';
4 |
--------------------------------------------------------------------------------
/packages/react/src/helpers/reads.ts:
--------------------------------------------------------------------------------
1 | import type { AnyVariables, StandardData } from '@lens-protocol/graphql';
2 | import { type TypedDocumentNode, useQuery } from 'urql';
3 |
4 | import { invariant } from '@lens-protocol/types';
5 | import { useMemo } from 'react';
6 | import { ReadResult, type SuspendableResult } from './results';
7 |
8 | /**
9 | * @internal
10 | */
11 | export type Suspendable = { suspense: true };
12 |
13 | /**
14 | * @internal
15 | */
16 | export type UseSuspendableQueryArgs = {
17 | document: TypedDocumentNode, Variables>;
18 | variables: Variables;
19 | suspense: boolean;
20 | };
21 |
22 | /**
23 | * @internal
24 | */
25 | export function useSuspendableQuery({
26 | document,
27 | variables,
28 | suspense,
29 | }: UseSuspendableQueryArgs): SuspendableResult {
30 | const [{ data, fetching, error }] = useQuery({
31 | query: document,
32 | variables,
33 | context: useMemo(() => ({ suspense }), [suspense]),
34 | });
35 |
36 | if (fetching) {
37 | return ReadResult.Initial();
38 | }
39 |
40 | if (error) {
41 | // biome-ignore lint/suspicious/noExplicitAny: temporary workaround
42 | return ReadResult.Failure(error) as any;
43 | }
44 |
45 | invariant(data, 'No data returned');
46 |
47 | return ReadResult.Success(data.value);
48 | }
49 |
--------------------------------------------------------------------------------
/packages/react/src/helpers/results.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A read hook result.
3 | *
4 | * It's a discriminated union of the possible results of a read operation:
5 | * - Rely on the `loading` value to determine if the `data` or `error` can be evaluated.
6 | * - If `error` is `undefined`, then `data` value will be available.
7 | */
8 | export type ReadResult =
9 | | {
10 | data: undefined;
11 | error: undefined;
12 | loading: true;
13 | }
14 | | {
15 | data: T;
16 | error: undefined;
17 | loading: false;
18 | }
19 | | {
20 | data: undefined;
21 | error: E;
22 | loading: false;
23 | };
24 |
25 | /**
26 | * @internal
27 | */
28 | export const ReadResult = {
29 | Initial: (): ReadResult => ({
30 | data: undefined,
31 | error: undefined,
32 | loading: true,
33 | }),
34 | Success: (data: T): ReadResult => ({
35 | data,
36 | error: undefined,
37 | loading: false,
38 | }),
39 | Failure: (error: E): ReadResult => ({
40 | data: undefined,
41 | error,
42 | loading: false,
43 | }),
44 | };
45 |
46 | /**
47 | * A read hook result that supports React Suspense
48 | */
49 | export type SuspenseResult = { data: T };
50 |
51 | export type SuspendableResult = ReadResult | SuspenseResult;
52 |
--------------------------------------------------------------------------------
/packages/react/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from '@lens-protocol/client';
2 |
3 | export * from './account';
4 | export * from './authentication';
5 | export * from './feed';
6 | export * from './follow';
7 | export * from './graph';
8 | export * from './group';
9 | export type {
10 | AsyncTaskError,
11 | AsyncTaskIdle,
12 | AsyncTaskLoading,
13 | AsyncTaskState,
14 | AsyncTaskSuccess,
15 | UseAsyncTask,
16 | } from './helpers';
17 | export * from './LensProvider';
18 | export * from './notification';
19 | export * from './post';
20 | export * from './timeline';
21 | export * from './username';
22 |
--------------------------------------------------------------------------------
/packages/react/src/notification/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useNotifications';
2 |
--------------------------------------------------------------------------------
/packages/react/src/notification/useNotifications.ts:
--------------------------------------------------------------------------------
1 | import type { Notification, NotificationsRequest, Paginated } from '@lens-protocol/graphql';
2 | import { NotificationsQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type NotificationsArgs = NotificationsRequest;
8 |
9 | /**
10 | * Fetch notifications for the authenticated Account.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useNotifications({ suspense: true });
16 | * ```
17 | */
18 | export function useNotifications(
19 | args: NotificationsArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch notifications for the authenticated Account.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useNotifications();
27 | * ```
28 | */
29 | export function useNotifications(args?: NotificationsArgs): ReadResult>;
30 |
31 | export function useNotifications({
32 | suspense = false,
33 | ...request
34 | }: NotificationsArgs & { suspense?: boolean } = {}): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: NotificationsQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/post/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useCreatePost';
2 | export * from './usePost';
3 | export * from './usePostBookmarks';
4 | export * from './usePostReactions';
5 | export * from './usePostReferences';
6 | export * from './usePosts';
7 |
--------------------------------------------------------------------------------
/packages/react/src/post/useCreatePost.integration.test.ts:
--------------------------------------------------------------------------------
1 | import { createPublicClient, loginAsAccountOwner, wallet } from '@lens-protocol/client/test-utils';
2 | import { handleOperationWith } from '@lens-protocol/client/viem';
3 | import { textOnly } from '@lens-protocol/metadata';
4 | import { assertOk } from '@lens-protocol/types';
5 | import { beforeAll, describe, expect, it } from 'vitest';
6 |
7 | import { renderHookWithContext } from '../test-utils';
8 | import { useCreatePost } from './useCreatePost';
9 |
10 | describe(`Given the ${useCreatePost.name} hook`, () => {
11 | const client = createPublicClient();
12 |
13 | describe('When creating a Post', () => {
14 | beforeAll(async () => {
15 | const result = await loginAsAccountOwner(client);
16 | assertOk(result);
17 | });
18 |
19 | it('Then it should return the newly created Post', { timeout: 10000 }, async () => {
20 | const { result } = renderHookWithContext(() => useCreatePost(handleOperationWith(wallet)), {
21 | client,
22 | });
23 |
24 | const record = await result.current.execute({
25 | contentUri: `data:application/json,${JSON.stringify(textOnly({ content: 'Hello world!' }))}`,
26 | });
27 |
28 | assertOk(record);
29 | expect(record.value.metadata).toMatchObject({
30 | content: 'Hello world!',
31 | });
32 | });
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/react/src/post/useCreatePost.ts:
--------------------------------------------------------------------------------
1 | import {
2 | type CreatePostRequest,
3 | type OperationHandler,
4 | type Post,
5 | type SigningError,
6 | type TransactionIndexingError,
7 | type UnauthenticatedError,
8 | type UnexpectedError,
9 | type ValidationError,
10 | expectTypename,
11 | nonNullable,
12 | } from '@lens-protocol/client';
13 | import { fetchPost, post } from '@lens-protocol/client/actions';
14 |
15 | import { type UseAsyncTask, useAuthenticatedAsyncTask } from '../helpers';
16 |
17 | /**
18 | * Creates a new post on the Lens.
19 | *
20 | * @alpha This is an alpha API and may be subject to breaking changes.
21 | */
22 | export function useCreatePost(
23 | handler: OperationHandler,
24 | ): UseAsyncTask<
25 | CreatePostRequest,
26 | Post,
27 | SigningError | ValidationError | TransactionIndexingError | UnauthenticatedError | UnexpectedError
28 | > {
29 | return useAuthenticatedAsyncTask((sessionClient, request) =>
30 | post(sessionClient, request)
31 | .andThen(handler)
32 | .andThen(sessionClient.waitForTransaction)
33 | .andThen((txHash) => fetchPost(sessionClient, { txHash }))
34 | .map(nonNullable)
35 | .map(expectTypename('Post')),
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/packages/react/src/post/usePost.ts:
--------------------------------------------------------------------------------
1 | import type { AnyPost, PostRequest } from '@lens-protocol/graphql';
2 | import { PostQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type UsePostArgs = PostRequest;
8 |
9 | /**
10 | * Fetch a single post.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = usePost({ post: postId('34…'), suspense: true });
16 | * ```
17 | */
18 | export function usePost(args: UsePostArgs & Suspendable): SuspenseResult;
19 |
20 | /**
21 | * Fetch a single post.
22 | *
23 | * ```tsx
24 | * const { data, loading } = usePost({ post: postId('34…') });
25 | * ```
26 | */
27 | export function usePost(args: UsePostArgs): ReadResult;
28 |
29 | export function usePost({
30 | suspense = false,
31 | ...request
32 | }: UsePostArgs & { suspense?: boolean }): SuspendableResult {
33 | return useSuspendableQuery({
34 | document: PostQuery,
35 | variables: { request },
36 | suspense,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/packages/react/src/post/usePostBookmarks.ts:
--------------------------------------------------------------------------------
1 | import type { AnyPost, Paginated, PostBookmarksRequest } from '@lens-protocol/graphql';
2 | import { PostBookmarksQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type PostBookmarksArgs = PostBookmarksRequest;
8 |
9 | /**
10 | * Fetch bookmarked posts.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = usePostBookmarks({ suspense: true });
16 | * ```
17 | */
18 | export function usePostBookmarks(
19 | args: PostBookmarksArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch bookmarked posts.
24 | *
25 | * ```tsx
26 | * const { data, loading } = usePostBookmarks();
27 | * ```
28 | */
29 | export function usePostBookmarks(args?: PostBookmarksArgs): ReadResult>;
30 |
31 | export function usePostBookmarks({
32 | suspense = false,
33 | ...request
34 | }: PostBookmarksArgs & { suspense?: boolean } = {}): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: PostBookmarksQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/post/usePostReactions.ts:
--------------------------------------------------------------------------------
1 | import type { AccountPostReaction, Paginated, PostReactionsRequest } from '@lens-protocol/graphql';
2 | import { PostReactionsQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type PostReactionsArgs = PostReactionsRequest;
8 |
9 | /**
10 | * Fetch reactions to a post.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = usePostReactions({
16 | * post: postId('42'),
17 | * suspense: true
18 | * });
19 | * ```
20 | */
21 | export function usePostReactions(
22 | args: PostReactionsArgs & Suspendable,
23 | ): SuspenseResult>;
24 |
25 | /**
26 | * Fetch reactions to a post.
27 | *
28 | * ```tsx
29 | * const { data, loading } = usePostReactions({
30 | * post: postId('42'),
31 | * );
32 | * ```
33 | */
34 | export function usePostReactions(
35 | args: PostReactionsArgs,
36 | ): ReadResult>;
37 |
38 | export function usePostReactions({
39 | suspense = false,
40 | ...request
41 | }: PostReactionsArgs & { suspense?: boolean }): SuspendableResult> {
42 | return useSuspendableQuery({
43 | document: PostReactionsQuery,
44 | variables: { request },
45 | suspense: suspense,
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/src/post/usePostReferences.ts:
--------------------------------------------------------------------------------
1 | import type { AnyPost, Paginated, PostReferencesRequest } from '@lens-protocol/graphql';
2 | import { PostReferencesQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type PostReferencesArgs = PostReferencesRequest;
8 |
9 | /**
10 | * Fetch references to a post.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = usePostReferences({
16 | * referencedTypes: [PostReferenceType.CommentOn],
17 | * referencedPost: postId('42'),
18 | * suspense: true
19 | * });
20 | * ```
21 | */
22 | export function usePostReferences(
23 | args: PostReferencesArgs & Suspendable,
24 | ): SuspenseResult>;
25 |
26 | /**
27 | * Fetch references to a post.
28 | *
29 | * ```tsx
30 | * const { data, loading } = usePostReferences({
31 | * referencedTypes: [PostReferenceType.CommentOn],
32 | * referencedPost: postId('42'),
33 | * );
34 | * ```
35 | */
36 | export function usePostReferences(args: PostReferencesArgs): ReadResult>;
37 |
38 | export function usePostReferences({
39 | suspense = false,
40 | ...request
41 | }: PostReferencesArgs & { suspense?: boolean }): SuspendableResult> {
42 | return useSuspendableQuery({
43 | document: PostReferencesQuery,
44 | variables: { request },
45 | suspense: suspense,
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/src/post/usePosts.ts:
--------------------------------------------------------------------------------
1 | import type { AnyPost, Paginated, PostsRequest } from '@lens-protocol/graphql';
2 | import { PostsQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type PostsArgs = PostsRequest;
8 |
9 | /**
10 | * Fetch posts available filtered by the given arguments.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = usePosts({
16 | * filter: {
17 | * searchQuery: 'test',
18 | * },
19 | * suspense: true
20 | * });
21 | * ```
22 | */
23 | export function usePosts(args: PostsArgs & Suspendable): SuspenseResult>;
24 |
25 | /**
26 | * Fetch posts available filtered by the given arguments.
27 | *
28 | * ```tsx
29 | * const { data, loading } = usePosts({
30 | * filter: {
31 | * searchQuery: 'test',
32 | * },
33 | * });
34 | * ```
35 | */
36 | export function usePosts(args: PostsArgs): ReadResult>;
37 |
38 | export function usePosts({
39 | suspense = false,
40 | ...request
41 | }: PostsArgs & { suspense?: boolean }): SuspendableResult> {
42 | return useSuspendableQuery({
43 | document: PostsQuery,
44 | variables: { request },
45 | suspense: suspense,
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/src/test-utils.tsx:
--------------------------------------------------------------------------------
1 | import { type RenderHookOptions, renderHook } from '@testing-library/react';
2 | import React, { type ReactNode, Suspense } from 'react';
3 |
4 | import type { PublicClient } from '@lens-protocol/client';
5 | import { LensContextProvider } from './context';
6 |
7 | function createWrapper(client: PublicClient) {
8 | return function TestWrapper({ children }: { children: ReactNode }) {
9 | return (
10 |
11 | {children}
12 |
13 | );
14 | };
15 | }
16 |
17 | export type RenderHookWithContextOptions = Omit, 'wrapper'> & {
18 | client: PublicClient;
19 | };
20 |
21 | /**
22 | * Wrapper around `renderHook` from `@testing-library/react`.
23 | *
24 | * Use it to run test on an hook that requires the Lens SDK context and/or a Suspense boundary.
25 | *
26 | * ```ts
27 | * const { result } = renderHookWithContext(() => useMyHook());
28 | * ```
29 | *
30 | * Use a mock object:
31 | * ```ts
32 | * const { result } = renderHookWithContext(() => useMyHook(), {
33 | * mocks: {
34 | * client: mock(),
35 | * },
36 | * });
37 | * ```
38 | */
39 | export function renderHookWithContext(
40 | callback: (props: TProps) => TResult,
41 | options: RenderHookWithContextOptions,
42 | ) {
43 | return renderHook(callback, {
44 | wrapper: createWrapper(options.client),
45 | ...options,
46 | });
47 | }
48 |
--------------------------------------------------------------------------------
/packages/react/src/timeline/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useTimeline';
2 | export * from './useTimelineHighlights';
3 |
--------------------------------------------------------------------------------
/packages/react/src/timeline/useTimeline.ts:
--------------------------------------------------------------------------------
1 | import type { Paginated, TimelineItem, TimelineRequest } from '@lens-protocol/graphql';
2 | import { TimelineQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type TimelineArgs = TimelineRequest;
8 |
9 | /**
10 | * Fetch timeline from an account.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useTimeline({ account: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useTimeline(
19 | args: TimelineArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch timeline from an account.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useTimeline({ account: evmAddress('0x…') });
27 | * ```
28 | */
29 | export function useTimeline(args: TimelineArgs): ReadResult>;
30 |
31 | export function useTimeline({
32 | suspense = false,
33 | ...request
34 | }: TimelineArgs & { suspense?: boolean }): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: TimelineQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/timeline/useTimelineHighlights.ts:
--------------------------------------------------------------------------------
1 | import type { Paginated, Post, TimelineHighlightsRequest } from '@lens-protocol/graphql';
2 | import { TimelineHighlightsQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type TimelineHighlightsArgs = TimelineHighlightsRequest;
8 |
9 | /**
10 | * Fetch Timeline Highlights for an account.
11 | *
12 | * This signature supports React Suspense:
13 | *
14 | * ```tsx
15 | * const { data } = useTimelineHighlights({ account: evmAddress('0x…'), suspense: true });
16 | * ```
17 | */
18 | export function useTimelineHighlights(
19 | args: TimelineHighlightsArgs & Suspendable,
20 | ): SuspenseResult>;
21 |
22 | /**
23 | * Fetch Timeline Highlights for an account.
24 | *
25 | * ```tsx
26 | * const { data, loading } = useTimelineHighlights({ account: evmAddress('0x…') });
27 | * ```
28 | */
29 | export function useTimelineHighlights(args: TimelineHighlightsArgs): ReadResult>;
30 |
31 | export function useTimelineHighlights({
32 | suspense = false,
33 | ...request
34 | }: TimelineHighlightsArgs & { suspense?: boolean }): SuspendableResult> {
35 | return useSuspendableQuery({
36 | document: TimelineHighlightsQuery,
37 | variables: { request },
38 | suspense: suspense,
39 | });
40 | }
41 |
--------------------------------------------------------------------------------
/packages/react/src/username/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useNamespace';
2 | export * from './useUsernames';
3 |
--------------------------------------------------------------------------------
/packages/react/src/username/useNamespace.ts:
--------------------------------------------------------------------------------
1 | import {
2 | NamespaceQuery,
3 | type NamespaceRequest,
4 | type UsernameNamespace,
5 | } from '@lens-protocol/graphql';
6 |
7 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
8 | import { useSuspendableQuery } from '../helpers';
9 |
10 | export type UseNamespaceArgs = NamespaceRequest;
11 |
12 | /**
13 | * Fetch a single namespace.
14 | *
15 | * This signature supports React Suspense:
16 | *
17 | * ```tsx
18 | * const { data } = useNamespace({ namespace: evmAddress('0x1234…'), suspense: true });
19 | * ```
20 | */
21 | export function useNamespace(
22 | args: UseNamespaceArgs & Suspendable,
23 | ): SuspenseResult;
24 |
25 | /**
26 | * Fetch a single namespace.
27 | *
28 | * ```tsx
29 | * const { data, loading } = useNamespace({ namespace: evmAddress('0x1234…') });
30 | * ```
31 | */
32 | export function useNamespace(args: UseNamespaceArgs): ReadResult;
33 |
34 | export function useNamespace({
35 | suspense = false,
36 | ...request
37 | }: UseNamespaceArgs & { suspense?: boolean }): SuspendableResult {
38 | return useSuspendableQuery({
39 | document: NamespaceQuery,
40 | variables: { request },
41 | suspense,
42 | });
43 | }
44 |
--------------------------------------------------------------------------------
/packages/react/src/username/useUsernames.ts:
--------------------------------------------------------------------------------
1 | import type { Paginated, Username, UsernamesRequest } from '@lens-protocol/graphql';
2 | import { UsernamesQuery } from '@lens-protocol/graphql';
3 |
4 | import type { ReadResult, Suspendable, SuspendableResult, SuspenseResult } from '../helpers';
5 | import { useSuspendableQuery } from '../helpers';
6 |
7 | export type UsernamesArgs = UsernamesRequest;
8 |
9 | /**
10 | * Fetch usernames available filtered by the given arguments.
11 | * Example: owned by a specific address.
12 | *
13 | * This signature supports React Suspense:
14 | *
15 | * ```tsx
16 | * const { data } = useUsernames({
17 | * filter: {
18 | * owner: evmAddress('0x..'),
19 | * },
20 | * suspense: true
21 | * });
22 | * ```
23 | */
24 | export function useUsernames(
25 | args: UsernamesArgs & Suspendable,
26 | ): SuspenseResult>;
27 |
28 | /**
29 | * Fetch usernames available filtered by the given arguments.
30 | * Example: owned by a specific address.
31 | *
32 | * ```tsx
33 | * const { data, loading } = useUsernames({
34 | * filter: {
35 | * owner: evmAddress('0x..'),
36 | * },
37 | * });
38 | * ```
39 | */
40 | export function useUsernames(args: UsernamesArgs): ReadResult>;
41 |
42 | export function useUsernames({
43 | suspense = false,
44 | ...request
45 | }: UsernamesArgs & { suspense?: boolean }): SuspendableResult> {
46 | return useSuspendableQuery({
47 | document: UsernamesQuery,
48 | variables: { request },
49 | suspense: suspense,
50 | });
51 | }
52 |
--------------------------------------------------------------------------------
/packages/react/src/viem.ts:
--------------------------------------------------------------------------------
1 | export * from '@lens-protocol/client/viem';
2 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist",
6 | "jsx": "react"
7 | },
8 | "include": ["./src/**/*.ts"],
9 | "exclude": ["node_modules", "dist"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/react/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: ['src/index.ts', 'src/ethers.ts', 'src/viem.ts'],
6 | outDir: 'dist',
7 | splitting: false,
8 | sourcemap: true,
9 | treeshake: false,
10 | clean: true,
11 | tsconfig: 'tsconfig.build.json',
12 | bundle: true,
13 | minify: true,
14 | dts: true,
15 | platform: 'neutral',
16 | format: ['esm', 'cjs'],
17 | }));
18 |
--------------------------------------------------------------------------------
/packages/react/vitest.setup.ts:
--------------------------------------------------------------------------------
1 | const originalFetch = globalThis.fetch;
2 |
3 | // Necessary due to a too-strict happy-dom interpretation of which
4 | // authentication headers are allowed. It should have allowed `Authorization: Bearer ...`.
5 | // https://github.com/capricorn86/happy-dom/blob/b61762e732872651af11f0c07c12a90850ac830f/packages/happy-dom/src/fetch/utilities/FetchRequestHeaderUtility.ts#L94
6 | globalThis.fetch = async (input, init = {}) => {
7 | const patchedInit = {
8 | ...init,
9 | credentials: 'include',
10 | } as const;
11 |
12 | return originalFetch(input, patchedInit);
13 | };
14 |
--------------------------------------------------------------------------------
/packages/storage/README.md:
--------------------------------------------------------------------------------
1 | # `@lens-protocol/storage`
2 |
3 | This package contains functionality related to storage.
4 |
5 | ---
6 |
7 | **It is not intended to be used directly. Its interface will change without notice, use it at your own risk.**
8 |
--------------------------------------------------------------------------------
/packages/storage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lens-protocol/storage",
3 | "version": "1.0.0",
4 | "description": "This package contains functionality related to storage.",
5 | "repository": {
6 | "directory": "packages/storage",
7 | "type": "git",
8 | "url": "git://github.com/lens-protocol/lens-sdk.git"
9 | },
10 | "type": "module",
11 | "main": "dist/index.cjs",
12 | "module": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "exports": {
15 | ".": {
16 | "import": "./dist/index.js",
17 | "require": "./dist/index.cjs"
18 | }
19 | },
20 | "typesVersions": {
21 | "*": {
22 | "import": ["./dist/index.d.ts"],
23 | "require": ["./dist/index.d.cts"]
24 | }
25 | },
26 | "files": ["dist"],
27 | "scripts": {
28 | "build": "tsup"
29 | },
30 | "dependencies": {
31 | "@lens-protocol/types": "workspace:*",
32 | "zod": "^3.23.8"
33 | },
34 | "devDependencies": {
35 | "tsup": "^8.3.5",
36 | "typescript": "^5.6.3"
37 | },
38 | "license": "MIT"
39 | }
40 |
--------------------------------------------------------------------------------
/packages/storage/src/CredentialsStorageSchema.ts:
--------------------------------------------------------------------------------
1 | import { accessToken, idToken, refreshToken } from '@lens-protocol/types';
2 | import type { AccessToken, IdToken, RefreshToken } from '@lens-protocol/types';
3 |
4 | import { z } from 'zod';
5 |
6 | import { BaseStorageSchema, type IStorageItem } from './BaseStorageSchema';
7 | import type { IStorageProvider } from './IStorage';
8 | import { Storage } from './Storage';
9 |
10 | export type Credentials = {
11 | accessToken: AccessToken;
12 | idToken: IdToken;
13 | refreshToken: RefreshToken;
14 | };
15 |
16 | const PersistedCredentialsSchema: z.ZodType = z.object({
17 | accessToken: z.string().transform(accessToken),
18 | idToken: z.string().transform(idToken),
19 | refreshToken: z.string().transform(refreshToken),
20 | });
21 |
22 | class CredentialsStorageSchema extends BaseStorageSchema {
23 | version = 3;
24 |
25 | constructor(key: string) {
26 | super(key, PersistedCredentialsSchema);
27 | }
28 |
29 | protected override async migrate(storageItem: IStorageItem): Promise {
30 | if (storageItem.metadata.version === 2) {
31 | throw new Error('Migration from v2 to v3 is not supported');
32 | }
33 | return this.parseData(storageItem.data);
34 | }
35 | }
36 |
37 | export class CredentialsStorage extends Storage {
38 | protected constructor(provider: IStorageProvider, namespace: string) {
39 | const schema = new CredentialsStorageSchema(`lens.${namespace}.credentials`);
40 | super(schema, provider);
41 | }
42 |
43 | static from(provider: IStorageProvider, namespace: string): CredentialsStorage {
44 | return new CredentialsStorage(provider, namespace);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/storage/src/IStorage.ts:
--------------------------------------------------------------------------------
1 | import type { ResultAsync } from '@lens-protocol/types';
2 | import type { SchemaMismatchError } from './BaseStorageSchema';
3 | import type { StorageError } from './Storage';
4 |
5 | /**
6 | * A generic storage subscriber
7 | *
8 | * @internal
9 | */
10 | export type StorageSubscriber = (newData: Data | null, oldData: Data | null) => void;
11 |
12 | /**
13 | * A string storage subscriber
14 | */
15 | export type StorageProviderSubscriber = StorageSubscriber;
16 |
17 | /**
18 | * A storage provider that supports asynchronous storage of key-value pairs
19 | */
20 | export interface IStorageProvider {
21 | getItem(key: string): Promise | string | null;
22 | setItem(key: string, value: string): Promise | Promise | void | string;
23 | removeItem(key: string): Promise | Promise | void;
24 | }
25 |
26 | export type StorageSubscription = {
27 | unsubscribe(): void;
28 | };
29 |
30 | /**
31 | * An observable storage provider that supports asynchronous storage of key-value pairs
32 | */
33 | export interface IObservableStorageProvider extends IStorageProvider {
34 | subscribe(key: string, subscriber: StorageProviderSubscriber): StorageSubscription;
35 | }
36 |
37 | /**
38 | * @internal
39 | */
40 | export interface IStorage {
41 | set(data: Data): ResultAsync, SchemaMismatchError | StorageError>;
42 | get(): Data | null;
43 | reset(): ResultAsync, StorageError>;
44 | resume(): ResultAsync, SchemaMismatchError | StorageError>;
45 | subscribe(subscriber: StorageSubscriber): StorageSubscription;
46 | }
47 |
--------------------------------------------------------------------------------
/packages/storage/src/InMemoryStorageProvider.ts:
--------------------------------------------------------------------------------
1 | import type { IStorageProvider } from './IStorage';
2 |
3 | export class InMemoryStorageProvider implements IStorageProvider {
4 | private readonly store: Map;
5 |
6 | constructor() {
7 | this.store = new Map();
8 | }
9 |
10 | getItem(key: string): string | null {
11 | return this.store.get(key) ?? null;
12 | }
13 |
14 | setItem(key: string, value: string): void {
15 | this.store.set(key, value);
16 | }
17 |
18 | removeItem(key: string): void {
19 | this.store.delete(key);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/storage/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './BaseStorageSchema';
2 | export * from './CredentialsStorageSchema';
3 | export * from './IStorage';
4 | export * from './Storage';
5 | export * from './InMemoryStorageProvider';
6 |
--------------------------------------------------------------------------------
/packages/storage/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/**/*.ts"],
8 | "exclude": ["node_modules", "dist"]
9 | }
10 |
--------------------------------------------------------------------------------
/packages/storage/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/storage/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: ['src/index.ts'],
6 | outDir: 'dist',
7 | splitting: false,
8 | sourcemap: true,
9 | treeshake: false,
10 | clean: true,
11 | tsconfig: 'tsconfig.build.json',
12 | bundle: true,
13 | minify: true,
14 | dts: true,
15 | platform: 'neutral',
16 | format: ['esm', 'cjs'],
17 | }));
18 |
--------------------------------------------------------------------------------
/packages/types/README.md:
--------------------------------------------------------------------------------
1 | # `@lens-protocol/types`
2 |
3 | This package contains low-level types and language extensions. **It is not intended to be used directly.**
4 |
5 | **Its interface will change without notice, use it at your own risk.**
6 |
--------------------------------------------------------------------------------
/packages/types/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lens-protocol/types",
3 | "version": "0.1.0",
4 | "description": "Low-level types for Lens SDK",
5 | "repository": {
6 | "directory": "packages/types",
7 | "type": "git",
8 | "url": "git://github.com/lens-protocol/lens-sdk.git"
9 | },
10 | "type": "module",
11 | "main": "dist/index.cjs",
12 | "module": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "exports": {
15 | ".": {
16 | "import": "./dist/index.js",
17 | "require": "./dist/index.cjs"
18 | }
19 | },
20 | "typesVersions": {
21 | "*": {
22 | "import": ["./dist/index.d.ts"],
23 | "require": ["./dist/index.d.cts"]
24 | }
25 | },
26 | "files": ["dist"],
27 | "sideEffects": false,
28 | "scripts": {
29 | "build": "tsup"
30 | },
31 | "license": "MIT",
32 | "dependencies": {
33 | "neverthrow": "^8.0.0",
34 | "type-fest": "^4.26.1"
35 | },
36 | "devDependencies": {
37 | "tsup": "^8.3.5",
38 | "typescript": "^5.6.3"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/Deferred.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Unwraps the promise to allow resolving/rejecting outside the Promise constructor
3 | */
4 | export class Deferred {
5 | readonly promise: Promise;
6 | resolve!: (value: T) => void;
7 | reject!: (reason?: unknown) => void;
8 |
9 | constructor() {
10 | this.promise = new Promise((resolve, reject) => {
11 | this.resolve = resolve;
12 | this.reject = reject;
13 | });
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/fail.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Throws the provided value and marks the function as never returning.
3 | *
4 | * Useful as a utility in functions that are expected to halt execution,
5 | * such as in unreachable code paths, error boundaries, or exhaustive checks.
6 | *
7 | * @param value - The value to throw, typically an Error or string.
8 | * @throws Always throws the provided value.
9 | * @experimental This function is not yet stable and may change in the future.
10 | */
11 | export function fail(value: unknown): never {
12 | throw value;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/identity.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The identity function returns the input value unchanged.
3 | *
4 | * @typeParam T - The type of the input and output.
5 | * @param value - The value to return.
6 | * @returns The same value that was passed in.
7 | */
8 | export function identity(value: T): T {
9 | return value;
10 | }
11 |
12 | /**
13 | * Alias for the {@link identity} function.
14 | */
15 | export const passthrough = identity;
16 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/index.ts:
--------------------------------------------------------------------------------
1 | export * from './assertions';
2 | export * from './Deferred';
3 | export * from './identity';
4 | export * from './invariant';
5 | export * from './never';
6 | export * from './refinements';
7 | export * from './fail';
8 | export * from './typeguards';
9 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/invariant.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * An error that occurs when a task violates a logical condition that is assumed to be true at all times.
3 | */
4 | export class InvariantError extends Error {
5 | name = 'InvariantError' as const;
6 | }
7 |
8 | /**
9 | * Asserts that the given condition is truthy
10 | * @internal
11 | *
12 | * @param condition - Either truthy or falsy value
13 | * @param message - An error message
14 | */
15 | export function invariant(condition: unknown, message: string): asserts condition {
16 | if (!condition) {
17 | throw new InvariantError(message);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/never.ts:
--------------------------------------------------------------------------------
1 | import { InvariantError } from './invariant';
2 |
3 | export function never(message = 'Unexpected call to never()'): never {
4 | throw new InvariantError(message);
5 | }
6 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/refinements.ts:
--------------------------------------------------------------------------------
1 | import { InvariantError } from './invariant';
2 |
3 | /**
4 | * Refinement function to check if a value is not null or undefined.
5 | */
6 | export function nonNullable(value: T): Exclude {
7 | if (value === null || value === undefined) {
8 | throw new InvariantError('Value should not be null or undefined');
9 | }
10 | return value as Exclude;
11 | }
12 |
13 | /**
14 | * Creates a refinement function to check if a value has a specific `__typename` in a union of types with `__typename`.
15 | */
16 | export function expectTypename(
17 | typename: Name,
18 | ) {
19 | return (value: T): Extract => {
20 | if (value.__typename !== typename) {
21 | throw new InvariantError(
22 | `Expected __typename to be "${typename}", but got "${value.__typename}"`,
23 | );
24 | }
25 | return value as Extract;
26 | };
27 | }
28 |
--------------------------------------------------------------------------------
/packages/types/src/helpers/typeguards.ts:
--------------------------------------------------------------------------------
1 | export function isObject(value: unknown): value is Record {
2 | return typeof value === 'object' && value !== null && !Array.isArray(value);
3 | }
4 |
--------------------------------------------------------------------------------
/packages/types/src/id.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged } from 'type-fest';
2 | import { tag } from './tag';
3 |
4 | /**
5 | * An identifier.
6 | */
7 | export type ID = Tagged;
8 |
9 | /**
10 | * A Universally Unique Identifier.
11 | */
12 | export type UUID = Tagged<`${string}-${string}-${string}-${string}-${string}`, 'UUID'>;
13 | export const uuid = tag;
14 |
15 | /**
16 | * A Lens Post ID.
17 | */
18 | export type PostId = Tagged;
19 | export const postId = tag;
20 |
21 | /**
22 | * A Lens Post ID.
23 | */
24 | export type RuleId = Tagged;
25 | export const ruleId = tag;
26 |
27 | /**
28 | * A Sponsorship Grant ID.
29 | */
30 | export type GrantId = Tagged;
31 | export const grantId = tag;
32 |
33 | /**
34 | * A Lens v2 Profile ID.
35 | */
36 | export type LegacyProfileId = Tagged;
37 |
38 | /**
39 | * A Lens Username (e.g., `lens/wagmi`, `lens/brainjammer`)
40 | */
41 | export type UsernameValue = Tagged<`${string}/${string}`, 'UsernameValue'>;
42 |
--------------------------------------------------------------------------------
/packages/types/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from 'neverthrow';
2 |
3 | export * from './helpers';
4 |
5 | export * from './errors';
6 | export * from './number';
7 | export * from './hex';
8 | export * from './id';
9 | export * from './jwt';
10 | export * from './misc';
11 | export * from './uri';
12 |
--------------------------------------------------------------------------------
/packages/types/src/jwt.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged } from 'type-fest';
2 |
3 | /**
4 | * A JWT in compact form.
5 | */
6 | export type CompactJwt = `${string}.${string}.${string}`;
7 | export function compactJwt(value: string): T {
8 | return value as T;
9 | }
10 |
11 | /**
12 | * The Access Token JWT.
13 | */
14 | export type AccessToken = Tagged;
15 | /**
16 | * @internal
17 | */
18 | export const accessToken = compactJwt;
19 |
20 | /**
21 | * The Refresh Token JWT.
22 | */
23 | export type RefreshToken = Tagged;
24 | export const refreshToken = compactJwt;
25 | /**
26 | * @internal
27 | */
28 |
29 | /**
30 | * The ID Token JWT.
31 | */
32 | export type IdToken = Tagged;
33 | /**
34 | * @internal
35 | */
36 | export const idToken = compactJwt;
37 |
--------------------------------------------------------------------------------
/packages/types/src/misc.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged } from 'type-fest';
2 | import { tag } from './tag';
3 |
4 | /**
5 | * A void value.
6 | */
7 | export type Void = Tagged;
8 |
9 | /**
10 | * An opaque pagination cursor.
11 | */
12 | export type Cursor = Tagged;
13 |
14 | /**
15 | * A DateTime string in ISO 8601 format.
16 | */
17 | export type DateTime = Tagged;
18 | export const dateTime = tag;
19 |
20 | /**
21 | * A JSON string.
22 | */
23 | export type JsonString = Tagged;
24 | export const jsonString = tag;
25 |
26 | /**
27 | * Beautify the readout of all of the members of that intersection.
28 | *
29 | * @see https://twitter.com/mattpocockuk/status/1622730173446557697
30 | *
31 | * @internal
32 | */
33 | export type Prettify = {
34 | [K in keyof T]: T[K];
35 | } & {};
36 |
--------------------------------------------------------------------------------
/packages/types/src/number.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged } from 'type-fest';
2 | import { invariant } from './helpers';
3 |
4 | /**
5 | * A string representation of an high precision decimal number (e.g., "0.1", "1000.42")
6 | */
7 | export type BigDecimal = Tagged;
8 | export function bigDecimal(value: string | number): BigDecimal {
9 | const str = String(value);
10 | invariant(/^-?\d+(\.\d+)?$/.test(str), `Invalid BigDecimal: ${str}`);
11 | return str as BigDecimal;
12 | }
13 |
14 | /**
15 | * A string representation of a big integer number.
16 | */
17 | export type BigIntString = Tagged;
18 | export function bigIntString(value: string): BigIntString {
19 | invariant(!/^-?\d+$/.test(value), `Invalid BigIntString: ${value}`);
20 | return value as BigIntString;
21 | }
22 |
--------------------------------------------------------------------------------
/packages/types/src/tag.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged, UnwrapTagged } from 'type-fest';
2 |
3 | type AnyTagged = Tagged;
4 |
5 | /**
6 | * A generic function that, when given some tagged type, can take a value with
7 | * the base type of the branded type, and assert that value to the tagged type.
8 | *
9 | * @param value - The value to assert to the tagged type.
10 | * @returns The tagged value.
11 | */
12 | export function tag(value: UnwrapTagged): T {
13 | return value as T;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/types/src/uri.ts:
--------------------------------------------------------------------------------
1 | import type { Tagged } from 'type-fest';
2 | import { tag } from './tag';
3 |
4 | /**
5 | * A Uniform Resource Identifier.
6 | *
7 | * It could be a URL pointing to a specific resource,
8 | * an IPFS URI (e.g. ipfs://Qm...), or an Arweave URI (e.g. ar://Qm...).
9 | */
10 | export type URI = Tagged;
11 | export const uri = tag;
12 |
13 | /**
14 | * A Uniform Resource Locator.
15 | *
16 | * A URL is a specific type of URI that, in addition to identifying a web resource,
17 | * specifies the means of acting upon or obtaining the representation of the resource,
18 | * i.e. specifying both its primary access mechanism and network location.
19 | */
20 | export type URL = Tagged;
21 | export const url = tag;
22 |
--------------------------------------------------------------------------------
/packages/types/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist",
6 | "lib": ["ESNext", "DOM"]
7 | },
8 | "include": ["./src/**/*.ts"],
9 | "exclude": ["node_modules", "dist"]
10 | }
11 |
--------------------------------------------------------------------------------
/packages/types/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/types/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: ['src/index.ts'],
6 | outDir: 'dist',
7 | splitting: false,
8 | sourcemap: true,
9 | treeshake: false,
10 | clean: true,
11 | tsconfig: 'tsconfig.build.json',
12 | bundle: true,
13 | minify: true,
14 | dts: true,
15 | platform: 'neutral',
16 | format: ['esm', 'cjs'],
17 | }));
18 |
--------------------------------------------------------------------------------
/plopfile.ts:
--------------------------------------------------------------------------------
1 | import type { NodePlopAPI } from 'plop';
2 |
3 | export default function (plop: NodePlopAPI) {
4 | plop.setGenerator('lib', {
5 | description: 'Generate a new package',
6 | prompts: [
7 | {
8 | type: 'list',
9 | name: 'template',
10 | message: 'Choose a template:',
11 | choices: [
12 | { name: 'Library', value: { name: 'lib', dest: 'packages' } },
13 | { name: 'React Example', value: { name: 'example-react', dest: 'examples' } },
14 | ],
15 | },
16 | {
17 | type: 'input',
18 | name: 'name',
19 | message: 'Package name (without scope):',
20 | },
21 | {
22 | type: 'input',
23 | name: 'description',
24 | message: 'Package description:',
25 | },
26 | ],
27 | actions: [
28 | {
29 | type: 'addMany',
30 | destination: '{{template.dest}}/{{name}}',
31 | base: 'templates/{{template.name}}/',
32 | templateFiles: 'templates/{{template.name}}/**',
33 | },
34 | ],
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/pnpm-workspace.yaml:
--------------------------------------------------------------------------------
1 | packages:
2 | - "packages/*"
3 | - '!examples/*'
4 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["config:base"],
4 | "prConcurrentLimit": 3
5 | }
6 |
--------------------------------------------------------------------------------
/templates/example-react/README.md:
--------------------------------------------------------------------------------
1 | # {{description}}
2 |
3 | [](https://stackblitz.com/github/lens-protocol/lens-sdk/tree/next/examples/{{name}})
4 |
--------------------------------------------------------------------------------
/templates/example-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{description}}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/templates/example-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{name}}",
3 | "description": "{{description}}",
4 | "private": true,
5 | "version": "0.0.0",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite"
9 | },
10 | "dependencies": {
11 | "@lens-protocol/react": "file:../../packages/react",
12 | "react": "^18.3.1",
13 | "react-dom": "^18.3.1"
14 | },
15 | "devDependencies": {
16 | "@types/react": "^18.3.12",
17 | "@types/react-dom": "^18.3.1",
18 | "@vitejs/plugin-react-swc": "^3.7.2",
19 | "typescript": "^5.6.3",
20 | "vite": "^5.4.9"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/templates/example-react/public/lens.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/example-react/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Provider } from '@lens-protocol/react';
2 | import { client } from './client';
3 |
4 | export function App() {
5 | return (
6 |
7 |
10 |
11 |
12 | Edit src/App.tsx
.
13 |
14 |
15 |
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/templates/example-react/src/client.ts:
--------------------------------------------------------------------------------
1 | import { PublicClient, testnet } from '@lens-protocol/react';
2 |
3 | export const client = PublicClient.create({
4 | environment: testnet,
5 | storage: window.localStorage,
6 | });
7 |
--------------------------------------------------------------------------------
/templates/example-react/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { StrictMode } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { App } from './App';
5 |
6 | createRoot(document.getElementById('root')!).render(
7 |
8 |
9 | ,
10 | );
11 |
--------------------------------------------------------------------------------
/templates/example-react/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/templates/example-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["ES2020", "DOM", "DOM.Iterable"],
6 | "jsx": "react-jsx",
7 | "module": "ESNext",
8 | "moduleResolution": "Bundler",
9 | "strict": true,
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "esModuleInterop": true,
13 | "noEmit": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "noImplicitReturns": true,
17 | "skipLibCheck": true
18 | },
19 | "include": ["src", "vite.config.ts"]
20 | }
21 |
--------------------------------------------------------------------------------
/templates/example-react/vite.config.ts:
--------------------------------------------------------------------------------
1 | import react from '@vitejs/plugin-react-swc';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [react()],
6 | });
7 |
--------------------------------------------------------------------------------
/templates/lib/README.md.hbs:
--------------------------------------------------------------------------------
1 | # `@lens-protocol/{{name}}`
2 |
3 | {{description}}
4 |
5 | ---
6 |
7 | **It is not intended to be used directly. Its interface will change without notice, use it at your own risk.**
8 |
9 |
--------------------------------------------------------------------------------
/templates/lib/package.json.hbs:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@lens-protocol/{{name}}",
3 | "version": "0.0.0",
4 | "description": "{{description}}",
5 | "repository": {
6 | "directory": "packages/{{name}}",
7 | "type": "git",
8 | "url": "git://github.com/lens-protocol/lens-sdk.git"
9 | },
10 | "type": "module",
11 | "main": "dist/index.cjs",
12 | "module": "dist/index.js",
13 | "types": "dist/index.d.ts",
14 | "exports": {
15 | ".": {
16 | "import": "./dist/index.js",
17 | "require": "./dist/index.cjs"
18 | }
19 | },
20 | "typesVersions": {
21 | "*": {
22 | "import": ["./dist/index.d.ts"],
23 | "require": ["./dist/index.d.cts"]
24 | }
25 | },
26 | "files": ["dist"],
27 | "scripts": {
28 | "build": "tsup"
29 | },
30 | "dependencies": {
31 | "@lens-protocol/types": "workspace:*"
32 | },
33 | "devDependencies": {
34 | "tsup": "^8.3.5",
35 | "typescript": "^5.6.3"
36 | },
37 | "license": "MIT"
38 | }
39 |
--------------------------------------------------------------------------------
/templates/lib/src/index.ts:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/templates/lib/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": "../../tsconfig.base.json",
4 | "compilerOptions": {
5 | "outDir": "dist"
6 | },
7 | "include": ["./src/**/*.ts"],
8 | "exclude": ["node_modules", "dist"]
9 | }
10 |
--------------------------------------------------------------------------------
/templates/lib/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "extends": ["./tsconfig.build.json", "../../tsconfig.json"]
4 | }
5 |
--------------------------------------------------------------------------------
/templates/lib/tsup.config.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-default-export */
2 | import { defineConfig } from 'tsup';
3 |
4 | export default defineConfig(() => ({
5 | entry: ['src/index.ts'],
6 | outDir: 'dist',
7 | splitting: false,
8 | sourcemap: true,
9 | treeshake: false,
10 | clean: true,
11 | tsconfig: 'tsconfig.build.json',
12 | bundle: true,
13 | minify: true,
14 | dts: true,
15 | platform: 'neutral',
16 | format: ['esm', 'cjs'],
17 | }));
18 |
--------------------------------------------------------------------------------
/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "allowJs": false,
5 | "allowSyntheticDefaultImports": true,
6 | "declaration": true,
7 | "declarationMap": true,
8 | "esModuleInterop": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "importHelpers": false,
11 | "isolatedModules": true,
12 | "lib": ["ESNext"],
13 | "module": "ESNext",
14 | "moduleResolution": "Bundler",
15 | "noFallthroughCasesInSwitch": true,
16 | "noImplicitAny": true,
17 | "noImplicitReturns": true,
18 | "noUncheckedIndexedAccess": true,
19 | "noUnusedLocals": true,
20 | "noUnusedParameters": true,
21 | "resolveJsonModule": true,
22 | "strictFunctionTypes": true,
23 | "skipLibCheck": true,
24 | "strict": true,
25 | "target": "ESNext"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "skipLibCheck": true, // Important for ambient types (e.g., vite-env.d.ts) to work
5 | "types": ["node"],
6 | "paths": {
7 | "@lens-protocol/client": ["./packages/client/src"],
8 | "@lens-protocol/client/actions": ["./packages/client/src/actions"],
9 | "@lens-protocol/client/test-utils": ["./packages/client/src/test-utils"],
10 | "@lens-protocol/env": ["./packages/env/src"],
11 | "@lens-protocol/graphql": ["./packages/graphql/src"],
12 | "@lens-protocol/react": ["./packages/react/src"],
13 | "@lens-protocol/storage": ["./packages/storage/src"],
14 | "@lens-protocol/types": ["./packages/types/src"]
15 | }
16 | },
17 | "include": ["**/*.ts"],
18 | "exclude": ["dist", "node_modules", "examples"]
19 | }
20 |
--------------------------------------------------------------------------------
/turbo.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://turborepo.org/schema.json",
3 | "ui": "tui",
4 | "tasks": {
5 | "build": {
6 | "dependsOn": ["^build"],
7 | "outputs": ["dist/**"]
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | interface ImportMetaEnv {
4 | readonly PRIVATE_KEY: `0x${string}`;
5 | readonly TEST_ACCOUNT: `0x${string}`;
6 | readonly TEST_APP: `0x${string}`;
7 | readonly TEST_ERC20: `0x${string}`;
8 | readonly ENVIRONMENT: 'testnet' | 'staging' | 'local' | undefined;
9 | readonly GLOBAL_SPONSORSHIP: `0x${string}`;
10 | readonly SPONSORSHIP_APPROVER_PRIVATE_KEY: `0x${string}`;
11 | }
12 |
13 | interface ImportMeta {
14 | readonly env: ImportMetaEnv;
15 | }
16 |
--------------------------------------------------------------------------------
/vitest.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'node:path';
2 | import { loadEnv } from 'vite';
3 | import { defineConfig } from 'vitest/config';
4 |
5 | export default defineConfig({
6 | root: './',
7 | test: {
8 | setupFiles: [resolve(__dirname, './vitest.setup.ts')],
9 | env: loadEnv('', process.cwd(), ''),
10 | testTimeout: 10_000,
11 | hookTimeout: 15_000,
12 | fileParallelism: false,
13 | projects: [
14 | {
15 | extends: true,
16 | test: {
17 | name: 'react',
18 | environment: 'happy-dom',
19 | setupFiles: [resolve(__dirname, './packages/react/vitest.setup.ts')],
20 | include: ['packages/react/**/*.{unit,integration}.test.{ts,tsx}'],
21 | },
22 | },
23 | {
24 | extends: true,
25 | test: {
26 | name: 'client',
27 | include: ['packages/client/**/*.{unit,integration}.test.{ts,tsx}'],
28 | environment: 'node',
29 | },
30 | },
31 | {
32 | extends: true,
33 | test: {
34 | name: 'storage',
35 | include: ['packages/storage/**/*.{unit,integration}.test.{ts,tsx}'],
36 | environment: 'node',
37 | },
38 | },
39 | {
40 | extends: true,
41 | test: {
42 | name: 'e2e',
43 | include: ['packages/**/*.{e2e}.test.{ts,tsx}'],
44 | environment: 'node',
45 | },
46 | },
47 | ],
48 | },
49 | });
50 |
--------------------------------------------------------------------------------
/vitest.d.ts:
--------------------------------------------------------------------------------
1 | import 'vitest';
2 |
3 | declare module 'vitest' {
4 | interface AsymmetricMatchersContaining extends JestExtendedMatchers {}
5 | }
6 |
--------------------------------------------------------------------------------
/vitest.setup.ts:
--------------------------------------------------------------------------------
1 | import * as matchers from 'jest-extended';
2 | import { expect } from 'vitest';
3 |
4 | expect.extend(matchers);
5 |
--------------------------------------------------------------------------------