@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 +8 -0
- package/app/components/EvmAccount.client.vue +4 -6
- package/app/composables/ens.ts +68 -0
- package/app/utils/ens.ts +89 -0
- package/nuxt.config.ts +5 -0
- package/package.json +3 -3
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
|
|
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:
|
|
23
|
-
address,
|
|
24
|
-
chainId: 1,
|
|
25
|
-
})
|
|
23
|
+
const { data: profile } = useEns(address, { mode: computed(() => props.mode) })
|
|
26
24
|
|
|
27
|
-
const display = computed<string>(() =>
|
|
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)
|
package/app/utils/ens.ts
ADDED
|
@@ -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
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.
|
|
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.
|
|
13
|
+
"@1001-digital/layers.base": "^0.0.26"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"@1001-digital/layers.base": "^0.0.
|
|
16
|
+
"@1001-digital/layers.base": "^0.0.26"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@types/qrcode": "^1.5.6",
|