@1001-digital/layers.evm 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/app/app.config.ts CHANGED
@@ -8,6 +8,9 @@ export default defineAppConfig({
8
8
  blockExplorer: 'https://etherscan.io',
9
9
  },
10
10
  },
11
+ ens: {
12
+ mode: 'indexer',
13
+ },
11
14
  },
12
15
  })
13
16
 
@@ -25,6 +28,11 @@ declare module '@nuxt/schema' {
25
28
  defaultChain?: string
26
29
  /** Named chain definitions */
27
30
  chains?: Record<string, EvmChainConfig>
31
+ /** ENS resolution configuration */
32
+ ens?: {
33
+ /** Resolution strategy: 'indexer' queries a ponder-ens API, 'chain' resolves on-chain */
34
+ mode?: 'indexer' | 'chain'
35
+ }
28
36
  }
29
37
  }
30
38
  }
@@ -6,10 +6,11 @@
6
6
 
7
7
  <script setup lang="ts">
8
8
  import type { Address } from 'viem'
9
- import { useConnection, useEnsName } from '@wagmi/vue'
9
+ import { useConnection } from '@wagmi/vue'
10
10
 
11
11
  const props = defineProps<{
12
12
  address?: Address
13
+ mode?: 'indexer' | 'chain'
13
14
  }>()
14
15
  const address = computed(() => props.address)
15
16
 
@@ -19,10 +20,7 @@ const isCurrent = computed<boolean>(
19
20
  () => currentAddress.value?.toLowerCase() === address.value?.toLowerCase(),
20
21
  )
21
22
 
22
- const { data: ens } = useEnsName({
23
- address,
24
- chainId: 1,
25
- })
23
+ const { data: profile } = useEns(address, { mode: computed(() => props.mode) })
26
24
 
27
- const display = computed<string>(() => ens.value || shortAddress(address.value!))
25
+ const display = computed<string>(() => profile.value?.ens || shortAddress(address.value!))
28
26
  </script>
@@ -0,0 +1,68 @@
1
+ import { getPublicClient } from '@wagmi/core'
2
+ import type { Config } from '@wagmi/vue'
3
+
4
+ interface UseEnsOptions {
5
+ mode?: MaybeRefOrGetter<'indexer' | 'chain' | undefined>
6
+ }
7
+
8
+ function useEnsBase(
9
+ key: string,
10
+ identifier: MaybeRefOrGetter<string | undefined>,
11
+ chainKeys: string[],
12
+ options: UseEnsOptions = {},
13
+ ) {
14
+ const { $wagmi } = useNuxtApp()
15
+ const appConfig = useAppConfig()
16
+ const runtimeConfig = useRuntimeConfig()
17
+
18
+ const mode = computed(() => toValue(options.mode) || appConfig.evm?.ens?.mode || 'indexer')
19
+
20
+ const indexerUrls = computed(() => {
21
+ const ens = (runtimeConfig.public.evm as { ens?: { indexer1?: string, indexer2?: string, indexer3?: string } }).ens
22
+ if (!ens) return []
23
+ return [ens.indexer1, ens.indexer2, ens.indexer3].filter(Boolean) as string[]
24
+ })
25
+
26
+ const strategies = computed(() => {
27
+ const primary = mode.value
28
+ const fallback = primary === 'indexer' ? 'chain' : 'indexer'
29
+ return [primary, fallback] as const
30
+ })
31
+
32
+ return useAsyncData(
33
+ `ens-${key}-${toValue(identifier)}`,
34
+ async () => {
35
+ const id = toValue(identifier)
36
+ if (!id) return null
37
+
38
+ for (const strategy of strategies.value) {
39
+ try {
40
+ if (strategy === 'indexer') {
41
+ if (!indexerUrls.value.length) continue
42
+ return await fetchEnsFromIndexer(id, indexerUrls.value)
43
+ }
44
+
45
+ if (strategy === 'chain') {
46
+ const client = getPublicClient($wagmi as Config, { chainId: 1 })
47
+ if (!client) continue
48
+ return await fetchEnsFromChain(id, client, chainKeys)
49
+ }
50
+ } catch {
51
+ continue
52
+ }
53
+ }
54
+
55
+ return null
56
+ },
57
+ { watch: [() => toValue(identifier)] },
58
+ )
59
+ }
60
+
61
+ export const useEns = (identifier: MaybeRefOrGetter<string | undefined>, options?: UseEnsOptions) =>
62
+ useEnsBase('resolve', identifier, [], options)
63
+
64
+ export const useEnsWithAvatar = (identifier: MaybeRefOrGetter<string | undefined>, options?: UseEnsOptions) =>
65
+ useEnsBase('avatar', identifier, [...ENS_KEYS_AVATAR], options)
66
+
67
+ export const useEnsProfile = (identifier: MaybeRefOrGetter<string | undefined>, options?: UseEnsOptions) =>
68
+ useEnsBase('profile', identifier, [...ENS_KEYS_PROFILE], options)
@@ -0,0 +1,89 @@
1
+ import { isAddress, type PublicClient, type Address } from 'viem'
2
+ import { normalize } from 'viem/ens'
3
+
4
+ export interface EnsProfile {
5
+ address: string
6
+ ens: string | null
7
+ data: {
8
+ avatar: string
9
+ header: string
10
+ description: string
11
+ links: {
12
+ url: string
13
+ email: string
14
+ twitter: string
15
+ github: string
16
+ }
17
+ } | null
18
+ }
19
+
20
+ const TEXT_RECORD_KEYS = ['avatar', 'header', 'description', 'url', 'email', 'com.twitter', 'com.github'] as const
21
+
22
+ function buildData(keys: string[], results: string[]): EnsProfile['data'] {
23
+ const get = (key: string) => {
24
+ const i = keys.indexOf(key)
25
+ return i >= 0 ? results[i] || '' : ''
26
+ }
27
+
28
+ return {
29
+ avatar: get('avatar'),
30
+ header: get('header'),
31
+ description: get('description'),
32
+ links: {
33
+ url: get('url'),
34
+ email: get('email'),
35
+ twitter: get('com.twitter'),
36
+ github: get('com.github'),
37
+ },
38
+ }
39
+ }
40
+
41
+ export async function fetchEnsFromIndexer(
42
+ identifier: string,
43
+ urls: string[],
44
+ ): Promise<EnsProfile> {
45
+ let lastError: Error | undefined
46
+
47
+ for (const url of urls) {
48
+ try {
49
+ return await $fetch<EnsProfile>(`${url}/${identifier}`)
50
+ } catch (err) {
51
+ lastError = err as Error
52
+ }
53
+ }
54
+
55
+ throw lastError ?? new Error('No indexer URLs provided')
56
+ }
57
+
58
+ export async function fetchEnsFromChain(
59
+ identifier: string,
60
+ client: PublicClient,
61
+ keys: string[] = [],
62
+ ): Promise<EnsProfile> {
63
+ const isAddr = isAddress(identifier)
64
+
65
+ let address: string
66
+ let ens: string | null
67
+
68
+ if (isAddr) {
69
+ address = identifier
70
+ ens = await client.getEnsName({ address: identifier as Address }) ?? null
71
+ } else {
72
+ ens = identifier
73
+ const resolved = await client.getEnsAddress({ name: normalize(identifier) })
74
+ if (!resolved) return { address: '', ens, data: null }
75
+ address = resolved
76
+ }
77
+
78
+ if (!ens || !keys.length) return { address, ens: ens ?? null, data: null }
79
+
80
+ const name = normalize(ens)
81
+ const results = await Promise.all(
82
+ keys.map(key => client.getEnsText({ name, key }).catch(() => null)),
83
+ )
84
+
85
+ return { address, ens, data: buildData(keys, results.map(r => r || '')) }
86
+ }
87
+
88
+ export const ENS_KEYS_AVATAR = ['avatar'] as const
89
+ export const ENS_KEYS_PROFILE = [...TEXT_RECORD_KEYS]
package/nuxt.config.ts CHANGED
@@ -13,6 +13,11 @@ export default defineNuxtConfig({
13
13
  chains: {
14
14
  mainnet: { rpc1: '', rpc2: '', rpc3: '' },
15
15
  },
16
+ ens: {
17
+ indexer1: '',
18
+ indexer2: '',
19
+ indexer3: '',
20
+ },
16
21
  },
17
22
  },
18
23
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@1001-digital/layers.evm",
3
3
  "type": "module",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "main": "./nuxt.config.ts",
6
6
  "devDependencies": {
7
7
  "@nuxt/eslint": "latest",
@@ -10,10 +10,10 @@
10
10
  "nuxt": "^4.3.0",
11
11
  "typescript": "^5.9.3",
12
12
  "vue": "latest",
13
- "@1001-digital/layers.base": "^0.0.19"
13
+ "@1001-digital/layers.base": "^0.0.26"
14
14
  },
15
15
  "peerDependencies": {
16
- "@1001-digital/layers.base": "^0.0.19"
16
+ "@1001-digital/layers.base": "^0.0.26"
17
17
  },
18
18
  "dependencies": {
19
19
  "@types/qrcode": "^1.5.6",