Skip to content

Commit

Permalink
Introduce Hooks (#199)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucemans authored Sep 23, 2024
1 parent e932a19 commit 1c2aa83
Show file tree
Hide file tree
Showing 24 changed files with 11,516 additions and 6,272 deletions.
6 changes: 6 additions & 0 deletions .changeset/weak-cars-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@ensdomains/ensjs': patch
'@ensdomains/ensjs-react': patch
---

Introduce @ensdomains/ensjs-react
49 changes: 49 additions & 0 deletions packages/react/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"no-restricted-syntax": ["off"],
"consistent-return": ["off"],
"import/no-named-as-default": ["off"],
"func-names": ["off"],
"no-param-reassign": ["off"],
"no-underscore-dangle": ["off"],
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "default",
"format": ["camelCase"],
"leadingUnderscore": "allowSingleOrDouble",
"trailingUnderscore": "allowSingleOrDouble"
},
{
"selector": "memberLike",
"format": ["camelCase", "UPPER_CASE", "PascalCase"],
"leadingUnderscore": "allowSingleOrDouble",
"trailingUnderscore": "allowSingleOrDouble"
},
{
"selector": "objectLiteralProperty",
"filter": { "regex": "\\d+", "match": true },
"format": null
},
{
"selector": "variable",
"format": ["camelCase", "UPPER_CASE"],
"leadingUnderscore": "allowSingleOrDouble",
"trailingUnderscore": "allowSingleOrDouble"
},
{
"selector": "typeLike",
"format": ["PascalCase"]
}
],
"no-useless-return": ["off"],
"radix": ["error", "as-needed"],
"no-bitwise": ["off"],
"import/no-extraneous-dependencies": ["off"],
"no-empty": ["error", { "allowEmptyCatch": true }]
},
"ignorePatterns": ["node_modules", "dist", "data", "cache", "esbuild.js"]
}
7 changes: 7 additions & 0 deletions packages/react/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# hardhat
/cache
/artifacts
/coverage
LICENSE

.env.local
21 changes: 21 additions & 0 deletions packages/react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# @ensdomains/ensjs-react

React hooks & utilities to interact with the Ethereum Name Service using ENSjs

## Installation

After installing [wagmi](https://wagmi.sh), simply run

```sh
pnpm install @ensdomains/ensjs-react
```

## Hooks

- `useEnsAvailable` - checks availability (uses [getAvailable](/docs/public/function.getAvailable.md))
- `useEnsExpiry` - returns expiry of a name (uses [getExpiry](/docs/public/function.getExpiry.md))
- `useEnsResolverInterfaces` - check supportsInterface on resolver (uses [getSupportedInterfaces](/docs/public/function.getSupportedInterfaces.md))
- `useNamesForAddress` - lists names from subgraph (uses [getNamesForAddress](/docs/subgraph/function.getNamesForAddress.md))
- `useDecodedName` - decodes name using subgraph (uses [getDecodedName](/docs/subgraph/function.getDecodedName.md))
- `useEnsRecordsWrite` - writes records to a name (uses [setRecords](/docs/wallet/function.setRecords.md)) (wip)
- `useEnsCredentials` - returns credentials from a name (uses [getTextRecord](/docs/public/function.getTextRecord.md)) (lite)
57 changes: 57 additions & 0 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@ensdomains/ensjs-react",
"version": "0.0.1",
"description": "ENS javascript library for contract interaction",
"type": "module",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"typings": "./dist/types/index.d.ts",
"sideEffects": false,
"exports": {
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/esm/index.js",
"default": "./dist/cjs/index.js"
},
"./package.json": "./package.json"
},
"files": [
"dist/",
"src/",
"!src/**/*.test.ts",
"!src/test"
],
"repository": "[email protected]:ensdomains/ensjs.git",
"author": "Lucemans <[email protected]>",
"license": "MIT",
"scripts": {
"clean": "rm -rf ./dist",
"lint": "eslint ./src/* --no-error-on-unmatched-pattern",
"build:cjs": "tsc --project tsconfig.build.json --module commonjs --outDir ./dist/cjs --removeComments --verbatimModuleSyntax false && echo > ./dist/cjs/package.json '{\"type\":\"commonjs\"}'",
"build:esm": "tsc --project tsconfig.build.json --module es2022 --outDir ./dist/esm && echo > ./dist/esm/package.json '{\"type\":\"module\",\"sideEffects\":false}'",
"build:types": "tsc --project tsconfig.build.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap",
"build": "pnpm run clean && pnpm run build:cjs && pnpm run build:esm && pnpm run build:types"
},
"dependencies": {
"@ensdomains/ensjs": "workspace:*"
},
"devDependencies": {
"@ensdomains/buffer": "^0.0.13",
"@ensdomains/ens-contracts": "1.0.0",
"@ensdomains/ens-test-env": "workspace:*",
"ts-node": "^10.9.2",
"typescript": "5.3.2",
"viem": "2.9.2",
"tslib": "^2.7.0",
"wagmi": "^2"
},
"peerDependencies": {
"@tanstack/react-query": "^5.54",
"viem": "^2.9.2",
"wagmi": "^2"
},
"engines": {
"node": ">=18"
}
}
11 changes: 11 additions & 0 deletions packages/react/src/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ClientWithEns } from '@ensdomains/ensjs/contracts'
import type { QueryClient } from '@tanstack/react-query'
import type { UseQueryParameters } from './hooks/useQuery.js'

export type ParamWithClients<T> = T & {
client: ClientWithEns
}

export type QueryConfig = {
queryClient?: QueryClient
} & UseQueryParameters
7 changes: 7 additions & 0 deletions packages/react/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { useNamesForAddress } from './hooks/useNamesForAddress.js'
export { useEnsAvailable } from './hooks/useEnsAvailable.js'
export { useEnsResolverInterfaces } from './hooks/useEnsResolverInterfaces.js'
export { useDecodedName } from './hooks/useDecodedName.js'
export { useEnsRecordsWrite } from './hooks/useEnsRecordsWrite.js'
export { useEnsExpiry } from './hooks/useEnsExpiry.js'
export { useEnsCredentials } from './hooks/useEnsCredentials.js'
38 changes: 38 additions & 0 deletions packages/react/src/hooks/useDecodedName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
getDecodedName,
type GetDecodedNameParameters,
type GetDecodedNameReturnType,
} from '@ensdomains/ensjs/subgraph'
import type { ParamWithClients, QueryConfig } from '../client.js'
import { useQuery, type UseQueryReturnType } from './useQuery.js'

export type UseDecodedNameParams = ParamWithClients<GetDecodedNameParameters>

export type UseDecodedNameReturnType = GetDecodedNameReturnType

/**
* Decode names returned using the subgraph
*
* Performs network request only if the name needs to be decoded, otherwise transparent
*
* @param params - {@link UseDecodedNameParams}
* @returns - {@link GetDecodedNameReturnType}
*/
export const useDecodedName = (
params: UseDecodedNameParams,
query?: QueryConfig,
): UseQueryReturnType<UseDecodedNameReturnType> => {
const { client } = params

return useQuery(
['ensjs', 'decoded-subgraph-name', params.name],
{
queryFn: async () => {
const result = await getDecodedName(client, params)

return result
},
},
query,
)
}
39 changes: 39 additions & 0 deletions packages/react/src/hooks/useEnsAvailable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
getAvailable,
type GetAvailableParameters,
type GetAvailableReturnType,
} from '@ensdomains/ensjs/public'
import type { ParamWithClients, QueryConfig } from '../client.js'
import { useQuery, type UseQueryReturnType } from './useQuery.js'

export type UseEnsAvailableParams = ParamWithClients<GetAvailableParameters>

export type UseEnsAvailableReturnType = GetAvailableReturnType

/**
* Returns a list of names for an address
*
* Keep in mind that this function is limited to .eth names
*
* @param params - {@link UseEnsAvailableParams}
* @returns - {@link UseEnsAvailableReturnType}
*/
export const useEnsAvailable = (
params: UseEnsAvailableParams,
query?: QueryConfig,
): UseQueryReturnType<UseEnsAvailableReturnType> => {
const { client } = params

return useQuery(
['ensjs', 'eth-name-available', params.name],
{
queryKey: [],
queryFn: async () => {
const result = await getAvailable(client, params)

return result
},
},
query,
)
}
49 changes: 49 additions & 0 deletions packages/react/src/hooks/useEnsCredentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { getTextRecord } from '@ensdomains/ensjs/public'
import type { ParamWithClients, QueryConfig } from '../client.js'
import { useQuery, type UseQueryReturnType } from './useQuery.js'

export type UseEnsCredentialsParams = ParamWithClients<{ name: string }>

export type ExternalCredential = {
url: string
}

export type UseEnsCredentialsReturnType = ExternalCredential[]

/**
* Returns credentials from a name
*
* @param params - {@link UseEnsCredentialsParams}
* @returns - {@link UseEnsCredentialsReturnType}
*
* @beta
*/
export const useEnsCredentials = (
params: UseEnsCredentialsParams,
query?: QueryConfig,
): UseQueryReturnType<UseEnsCredentialsReturnType> => {
const { name, client } = params

return useQuery(
['ensjs', 'credentials', params.name],
{
queryFn: async () => {
const result = await getTextRecord(client, {
name,
key: 'verifications',
})

if (!result) return []

const credentials = (JSON.parse(result) as string[])
.filter((url) => new URL(url))
.map((url) => ({
url,
}))

return credentials
},
},
query,
)
}
34 changes: 34 additions & 0 deletions packages/react/src/hooks/useEnsExpiry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
getExpiry,
type GetExpiryParameters,
type GetExpiryReturnType,
} from '@ensdomains/ensjs/public'
import type { ParamWithClients, QueryConfig } from '../client.js'
import { useQuery, type UseQueryReturnType } from './useQuery.js'

export type UseEnsExpiryParams = ParamWithClients<GetExpiryParameters>

export type UseEnsExpiryReturnType = GetExpiryReturnType

/**
* Returns expiry of a name
*
* Keep in mind that this function is limited to second-level .eth names (luc.eth, nick.eth, etc)
*
* @param params - {@link UseEnsExpiryParams}
* @returns - {@link UseEnsExpiryReturnType}
*/
export const useEnsExpiry = (
params: UseEnsExpiryParams,
query?: QueryConfig,
): UseQueryReturnType<UseEnsExpiryReturnType> => {
const { client } = params

return useQuery(
['ensjs', 'ens-expiry', params.name],
{
queryFn: async () => getExpiry(client, params),
},
query,
)
}
27 changes: 27 additions & 0 deletions packages/react/src/hooks/useEnsRecordsWrite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useAccount } from 'wagmi'
import type { ParamWithClients } from '../client.js'

export type UseEnsRecordsWriteParams = ParamWithClients<{}>

/**
* Allows you to write records to a name, provided the name supports it and the signed in user has the required permissions
*
* You can use the {@link resolverInterfaces} shorthand, or manually specify a Hex value
*
* @param params - {@link UseEnsResolverInterfacesParams}
* @returns - {@link boolean[]}
*
* @alpha
*/
export const useEnsRecordsWrite = (
_params: UseEnsRecordsWriteParams,
config?: any,
) => {
const { address } = useAccount({ config })
// const client = useWalletClient()

console.log('Hello ', address)

return { data: undefined }
// return setRecords(client as any, params)
}
Loading

0 comments on commit 1c2aa83

Please sign in to comment.