@1001-digital/layers.evm 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.playground/app/components/SignedIn.client.vue +2 -15
- package/app/app.config.ts +8 -0
- package/app/components/EvmAccount.client.vue +4 -6
- package/app/components/EvmTransactionFlow.vue +31 -14
- 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
|
@@ -17,25 +17,12 @@
|
|
|
17
17
|
<EvmTransactionFlow
|
|
18
18
|
:request="sendTransaction"
|
|
19
19
|
:text="{
|
|
20
|
-
title: {
|
|
21
|
-
confirm: 'Send Transaction',
|
|
22
|
-
requesting: 'Requesting...',
|
|
23
|
-
waiting: 'Waiting for confirmation...',
|
|
24
|
-
complete: 'Transaction Complete!',
|
|
25
|
-
error: 'Transaction Error',
|
|
26
|
-
},
|
|
20
|
+
title: { confirm: 'Send Transaction' },
|
|
27
21
|
lead: {
|
|
28
22
|
confirm:
|
|
29
23
|
'This will send 0 ETH to your address as a test transaction.',
|
|
30
|
-
requesting: 'Please confirm the transaction in your wallet.',
|
|
31
|
-
waiting: 'Your transaction is being processed...',
|
|
32
|
-
complete: 'Your transaction has been confirmed on-chain.',
|
|
33
|
-
error: 'An error occurred while processing your transaction.',
|
|
34
|
-
},
|
|
35
|
-
action: {
|
|
36
|
-
confirm: 'Send Transaction',
|
|
37
|
-
error: 'Try Again',
|
|
38
24
|
},
|
|
25
|
+
action: { confirm: 'Send Transaction' },
|
|
39
26
|
}"
|
|
40
27
|
@complete="onTransactionComplete"
|
|
41
28
|
@cancel="onTransactionCancel"
|
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>
|
|
@@ -93,9 +93,9 @@ import type { Config } from '@wagmi/vue'
|
|
|
93
93
|
import type { TransactionReceipt, Hash } from 'viem'
|
|
94
94
|
|
|
95
95
|
interface TextConfig {
|
|
96
|
-
title
|
|
97
|
-
lead
|
|
98
|
-
action
|
|
96
|
+
title?: Record<string, string>
|
|
97
|
+
lead?: Record<string, string>
|
|
98
|
+
action?: Record<string, string>
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
type Step =
|
|
@@ -107,6 +107,28 @@ type Step =
|
|
|
107
107
|
| 'complete'
|
|
108
108
|
| 'error'
|
|
109
109
|
|
|
110
|
+
const defaultText = {
|
|
111
|
+
title: {
|
|
112
|
+
confirm: 'Confirm Transaction',
|
|
113
|
+
chain: 'Switch Network',
|
|
114
|
+
requesting: 'Requesting',
|
|
115
|
+
waiting: 'Processing',
|
|
116
|
+
complete: 'Complete',
|
|
117
|
+
error: 'Error',
|
|
118
|
+
},
|
|
119
|
+
lead: {
|
|
120
|
+
confirm: 'Please review and confirm this transaction.',
|
|
121
|
+
chain: 'Please switch to the correct network to continue.',
|
|
122
|
+
requesting: 'Requesting transaction signature...',
|
|
123
|
+
waiting: 'Waiting for transaction confirmation...',
|
|
124
|
+
complete: 'Transaction confirmed successfully.',
|
|
125
|
+
},
|
|
126
|
+
action: {
|
|
127
|
+
confirm: 'Execute',
|
|
128
|
+
error: 'Try Again',
|
|
129
|
+
},
|
|
130
|
+
} satisfies TextConfig
|
|
131
|
+
|
|
110
132
|
const slots = useSlots()
|
|
111
133
|
const checkChain = useEnsureChainIdCheck()
|
|
112
134
|
|
|
@@ -124,17 +146,6 @@ const props = withDefaults(
|
|
|
124
146
|
dismissable?: boolean
|
|
125
147
|
}>(),
|
|
126
148
|
{
|
|
127
|
-
text: () => ({
|
|
128
|
-
title: {
|
|
129
|
-
confirm: 'Confirm Transaction',
|
|
130
|
-
},
|
|
131
|
-
lead: {
|
|
132
|
-
confirm: 'Please review and confirm this transaction.',
|
|
133
|
-
},
|
|
134
|
-
action: {
|
|
135
|
-
confirm: 'Execute',
|
|
136
|
-
},
|
|
137
|
-
}),
|
|
138
149
|
delayAfter: 2000,
|
|
139
150
|
delayAutoclose: 2000,
|
|
140
151
|
skipConfirmation: false,
|
|
@@ -148,6 +159,12 @@ const emit = defineEmits<{
|
|
|
148
159
|
cancel: []
|
|
149
160
|
}>()
|
|
150
161
|
|
|
162
|
+
const text = computed<Required<TextConfig>>(() => ({
|
|
163
|
+
title: { ...defaultText.title, ...props.text?.title },
|
|
164
|
+
lead: { ...defaultText.lead, ...props.text?.lead },
|
|
165
|
+
action: { ...defaultText.action, ...props.text?.action },
|
|
166
|
+
}))
|
|
167
|
+
|
|
151
168
|
const step = ref<Step>('idle')
|
|
152
169
|
|
|
153
170
|
const open = computed({
|
|
@@ -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 useEnsAvatar = (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 type { PublicClient, Address } from 'viem'
|
|
2
|
+
import { isAddress, 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(() => '')),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return { address, ens, data: buildData(keys, results) }
|
|
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.4",
|
|
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",
|