@1001-digital/layers.evm 1.0.7 → 1.0.8

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.
@@ -1,108 +0,0 @@
1
- <template>
2
- <p>
3
- <slot name="instruction">Scan the code in your wallet application</slot>
4
- </p>
5
- <div class="qr-frame">
6
- <canvas ref="qrCanvas"></canvas>
7
- </div>
8
- <p class="uri-label">Or copy the connection URI:</p>
9
- <div class="uri-display">
10
- <code>{{ uri }}</code>
11
- <Button @click="copyUri" class="copy-button" :class="{ copied: isCopied }">
12
- <Icon :type="isCopied ? 'check' : 'copy'" />
13
- </Button>
14
- </div>
15
- </template>
16
-
17
- <script setup lang="ts">
18
- import QRCode from 'qrcode'
19
-
20
- const props = defineProps<{
21
- uri: string
22
- }>()
23
-
24
- const qrCanvas = ref<HTMLCanvasElement | null>(null)
25
- const { copy, copied: isCopied } = useClipboard()
26
-
27
- const generateQR = async () => {
28
- if (!qrCanvas.value || !props.uri) return
29
-
30
- try {
31
- await QRCode.toCanvas(qrCanvas.value, props.uri, {
32
- width: 300,
33
- margin: 2,
34
- color: {
35
- dark: '#000000',
36
- light: '#FFFFFF',
37
- },
38
- })
39
- } catch (error) {
40
- console.error('Failed to generate QR code:', error)
41
- }
42
- }
43
-
44
- const copyUri = () => copy(props.uri)
45
-
46
- watch(() => props.uri, generateQR, { immediate: true })
47
-
48
- onMounted(() => {
49
- generateQR()
50
- })
51
- </script>
52
-
53
- <style scoped>
54
- p {
55
- text-align: center;
56
- @mixin ui-font;
57
- color: var(--muted);
58
- font-size: var(--font-sm);
59
- }
60
-
61
- .qr-frame {
62
- background: white;
63
- padding: var(--spacer-sm);
64
- max-width: 15rem;
65
- max-height: 15rem;
66
- border: var(--border);
67
- border-radius: var(--border-radius);
68
- margin: 0 auto;
69
-
70
- canvas {
71
- width: 100% !important;
72
- height: 100% !important;
73
- }
74
- }
75
-
76
- .uri-display {
77
- display: flex;
78
- align-items: center;
79
- gap: var(--spacer-xs);
80
- background: var(--color-bg-secondary);
81
- border: var(--border);
82
- border-radius: var(--border-radius-sm);
83
- overflow: hidden;
84
- height: min-content;
85
- padding: 0;
86
-
87
- code {
88
- flex: 1;
89
- font-size: var(--font-xs);
90
- font-family: monospace;
91
- white-space: nowrap;
92
- overflow: hidden;
93
- padding: 0 var(--spacer-sm);
94
- color: var(--muted);
95
- }
96
-
97
- .copy-button {
98
- flex-shrink: 0;
99
- padding: var(--spacer-xs);
100
- min-width: auto;
101
- margin: -1px;
102
-
103
- &.copied {
104
- color: var(--color-success);
105
- }
106
- }
107
- }
108
- </style>
@@ -1,13 +0,0 @@
1
- <template>
2
- <EvmConnectorQR :uri="uri">
3
- <template #instruction>
4
- Scan the code in your MetaMask mobile app
5
- </template>
6
- </EvmConnectorQR>
7
- </template>
8
-
9
- <script setup lang="ts">
10
- defineProps<{
11
- uri: string
12
- }>()
13
- </script>
@@ -1,293 +0,0 @@
1
- <template>
2
- <slot
3
- :start="start"
4
- :step="step"
5
- :open="open"
6
- name="start"
7
- ></slot>
8
-
9
- <Dialog
10
- v-model:open="open"
11
- :closable="canDismiss"
12
- :click-outside="canDismiss"
13
- :title="text.title[step]"
14
- class="transaction-flow"
15
- >
16
- <slot name="before" />
17
-
18
- <Loading
19
- v-if="step === 'requesting' || step === 'waiting'"
20
- spinner
21
- stacked
22
- :txt="text.lead[step] || ''"
23
- />
24
-
25
- <p
26
- v-if="
27
- step !== 'requesting' &&
28
- step !== 'waiting' &&
29
- step !== 'error' &&
30
- text.lead[step]
31
- "
32
- >
33
- {{ text.lead[step] }}
34
- </p>
35
-
36
- <Alert
37
- v-if="error"
38
- type="error"
39
- >
40
- <p v-if="text.lead[step]">{{ text.lead[step] }}</p>
41
- <p>{{ error }}</p>
42
- </Alert>
43
-
44
- <Button
45
- v-if="step === 'waiting'"
46
- :to="txLink"
47
- target="_blank"
48
- class="link muted small centered"
49
- >
50
- <Icon type="link" />
51
- <span>View on Block Explorer</span>
52
- </Button>
53
-
54
- <slot
55
- :name="step"
56
- :cancel="cancel"
57
- ></slot>
58
-
59
- <template #footer>
60
- <template v-if="step === 'chain'">
61
- <Button
62
- @click="cancel"
63
- class="secondary"
64
- >Cancel</Button
65
- >
66
- </template>
67
-
68
- <template v-if="step === 'confirm' || step === 'error'">
69
- <Button
70
- @click="cancel"
71
- class="secondary"
72
- >Cancel</Button
73
- >
74
- <Button @click="() => initializeRequest()">
75
- {{ text.action[step] || 'Execute' }}
76
- </Button>
77
- </template>
78
-
79
- <slot
80
- name="actions"
81
- :step="step"
82
- :cancel="cancel"
83
- :execute="() => initializeRequest()"
84
- :tx-link="txLink"
85
- />
86
- </template>
87
- </Dialog>
88
- </template>
89
-
90
- <script setup lang="ts">
91
- import { waitForTransactionReceipt, watchChainId } from '@wagmi/core'
92
- import type { Config } from '@wagmi/vue'
93
- import type { TransactionReceipt, Hash } from 'viem'
94
-
95
- interface TextConfig {
96
- title?: Record<string, string>
97
- lead?: Record<string, string>
98
- action?: Record<string, string>
99
- }
100
-
101
- type Step =
102
- | 'idle'
103
- | 'confirm'
104
- | 'chain'
105
- | 'requesting'
106
- | 'waiting'
107
- | 'complete'
108
- | 'error'
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
-
132
- const slots = useSlots()
133
- const checkChain = useEnsureChainIdCheck()
134
-
135
- const { $wagmi } = useNuxtApp()
136
- const blockExplorer = useBlockExplorer()
137
-
138
- const props = withDefaults(
139
- defineProps<{
140
- text?: TextConfig
141
- request?: () => Promise<Hash>
142
- delayAfter?: number
143
- delayAutoclose?: number
144
- skipConfirmation?: boolean
145
- autoCloseSuccess?: boolean
146
- dismissable?: boolean
147
- }>(),
148
- {
149
- delayAfter: 2000,
150
- delayAutoclose: 2000,
151
- skipConfirmation: false,
152
- autoCloseSuccess: true,
153
- dismissable: true,
154
- },
155
- )
156
-
157
- const emit = defineEmits<{
158
- complete: [receipt: TransactionReceipt]
159
- cancel: []
160
- }>()
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
-
168
- const step = ref<Step>('idle')
169
-
170
- const open = computed({
171
- get: () => step.value !== 'idle',
172
- set: (v) => {
173
- if (!v) {
174
- step.value = 'idle'
175
- error.value = ''
176
- }
177
- },
178
- })
179
-
180
- watchChainId($wagmi as Config, {
181
- async onChange() {
182
- if (step.value !== 'chain') return
183
-
184
- if (await checkChain()) {
185
- initializeRequest()
186
- }
187
- },
188
- })
189
-
190
- const cachedRequest = ref(props.request)
191
- watch(
192
- () => props.request,
193
- (v) => {
194
- cachedRequest.value = v
195
- },
196
- )
197
-
198
- const error = ref('')
199
- const tx = ref<Hash | null>(null)
200
- const receipt = ref<TransactionReceipt | null>(null)
201
- const txLink = computed(() => `${blockExplorer}/tx/${tx.value}`)
202
-
203
- const canDismiss = computed(
204
- () =>
205
- props.dismissable &&
206
- step.value !== 'requesting' &&
207
- step.value !== 'waiting',
208
- )
209
-
210
- const initializeRequest = async (request = cachedRequest.value) => {
211
- cachedRequest.value = request
212
- error.value = ''
213
- tx.value = null
214
- receipt.value = null
215
- step.value = 'confirm'
216
-
217
- if (!(await checkChain())) {
218
- step.value = 'chain'
219
- return
220
- }
221
-
222
- try {
223
- step.value = 'requesting'
224
- tx.value = await request!()
225
- step.value = 'waiting'
226
- const receiptObject = await waitForTransactionReceipt($wagmi as Config, {
227
- hash: tx.value,
228
- })
229
- await delay(props.delayAfter)
230
- receipt.value = receiptObject
231
- emit('complete', receiptObject)
232
- step.value = 'complete'
233
- } catch (e: unknown) {
234
- const err = e as { cause?: { code?: number }; shortMessage?: string }
235
- if (err?.cause?.code === 4001) {
236
- error.value = 'Transaction rejected by user.'
237
- step.value = 'error'
238
- } else {
239
- error.value = err.shortMessage || 'Error submitting transaction request.'
240
- step.value = 'error'
241
- }
242
- console.log(e)
243
- }
244
-
245
- if (props.autoCloseSuccess && step.value === 'complete') {
246
- await delay(props.delayAutoclose)
247
- step.value = 'idle'
248
- await delay(300)
249
- }
250
-
251
- return receipt.value
252
- }
253
-
254
- const start = () => {
255
- if (props.skipConfirmation && step.value === 'idle') {
256
- initializeRequest()
257
- return
258
- }
259
-
260
- step.value = 'confirm'
261
- }
262
-
263
- const cancel = () => {
264
- step.value = 'idle'
265
- error.value = ''
266
- emit('cancel')
267
- }
268
-
269
- defineExpose({
270
- initializeRequest,
271
- })
272
- </script>
273
-
274
- <style>
275
- .transaction-flow > section {
276
- display: grid;
277
- gap: var(--spacer);
278
-
279
- .text {
280
- width: 100%;
281
- height: min-content;
282
- }
283
-
284
- p {
285
- white-space: pre-wrap;
286
- width: 100%;
287
-
288
- a {
289
- text-decoration: underline;
290
- }
291
- }
292
- }
293
- </style>
@@ -1,13 +0,0 @@
1
- <template>
2
- <EvmConnectorQR :uri="uri">
3
- <template #instruction>
4
- Scan the code in your wallet application
5
- </template>
6
- </EvmConnectorQR>
7
- </template>
8
-
9
- <script setup lang="ts">
10
- defineProps<{
11
- uri: string
12
- }>()
13
- </script>
@@ -1,26 +0,0 @@
1
- export const useClipboard = () => {
2
- const copied = ref(false)
3
- let timeout: ReturnType<typeof setTimeout> | null = null
4
-
5
- const copy = async (text: string) => {
6
- try {
7
- await navigator.clipboard.writeText(text)
8
- copied.value = true
9
-
10
- if (timeout) clearTimeout(timeout)
11
- timeout = setTimeout(() => {
12
- copied.value = false
13
- }, 2000)
14
-
15
- return true
16
- } catch (error) {
17
- console.error('Failed to copy to clipboard:', error)
18
- return false
19
- }
20
- }
21
-
22
- return {
23
- copy,
24
- copied,
25
- }
26
- }
@@ -1,88 +0,0 @@
1
- import { getPublicClient } from '@wagmi/core'
2
- import type { Config } from '@wagmi/vue'
3
-
4
- type EnsMode = 'indexer' | 'chain'
5
-
6
- interface UseEnsOptions {
7
- mode?: MaybeRefOrGetter<EnsMode | undefined>
8
- }
9
-
10
- interface EnsRuntimeConfig {
11
- ens?: { indexer1?: string, indexer2?: string, indexer3?: string }
12
- }
13
-
14
- function getIndexerUrls(config: EnsRuntimeConfig): string[] {
15
- if (!config.ens) return []
16
- return [config.ens.indexer1, config.ens.indexer2, config.ens.indexer3].filter(Boolean) as string[]
17
- }
18
-
19
- async function resolve(
20
- identifier: string,
21
- strategies: EnsMode[],
22
- indexerUrls: string[],
23
- wagmi: Config,
24
- chainKeys: string[],
25
- ): Promise<EnsProfile> {
26
- for (const strategy of strategies) {
27
- try {
28
- if (strategy === 'indexer') {
29
- if (!indexerUrls.length) continue
30
- return await fetchEnsFromIndexer(identifier, indexerUrls)
31
- }
32
-
33
- if (strategy === 'chain') {
34
- const client = getPublicClient(wagmi, { chainId: 1 })
35
- if (!client) continue
36
- return await fetchEnsFromChain(identifier, client, chainKeys)
37
- }
38
- } catch {
39
- continue
40
- }
41
- }
42
-
43
- return { address: identifier, ens: null, data: null }
44
- }
45
-
46
- function useEnsBase(
47
- tier: string,
48
- identifier: MaybeRefOrGetter<string | undefined>,
49
- chainKeys: string[],
50
- options: UseEnsOptions = {},
51
- ) {
52
- const { $wagmi } = useNuxtApp()
53
- const appConfig = useAppConfig()
54
- const runtimeConfig = useRuntimeConfig()
55
-
56
- const mode = computed<EnsMode>(() => toValue(options.mode) || appConfig.evm?.ens?.mode || 'indexer')
57
- const indexerUrls = computed(() => getIndexerUrls(runtimeConfig.public.evm as EnsRuntimeConfig))
58
- const cacheKey = computed(() => `ens-${tier}-${toValue(identifier)}`)
59
-
60
- return useAsyncData(
61
- cacheKey.value,
62
- async () => {
63
- const id = toValue(identifier)
64
- if (!id) return null
65
-
66
- const strategies: EnsMode[] = mode.value === 'indexer'
67
- ? ['indexer', 'chain']
68
- : ['chain', 'indexer']
69
-
70
- return ensCache.fetch(cacheKey.value, () =>
71
- resolve(id, strategies, indexerUrls.value, $wagmi as Config, chainKeys),
72
- )
73
- },
74
- {
75
- watch: [() => toValue(identifier)],
76
- getCachedData: () => ensCache.get(cacheKey.value) ?? undefined,
77
- },
78
- )
79
- }
80
-
81
- export const useEns = (identifier: MaybeRefOrGetter<string | undefined>, options?: UseEnsOptions) =>
82
- useEnsBase('resolve', identifier, [], options)
83
-
84
- export const useEnsWithAvatar = (identifier: MaybeRefOrGetter<string | undefined>, options?: UseEnsOptions) =>
85
- useEnsBase('avatar', identifier, [...ENS_KEYS_AVATAR], options)
86
-
87
- export const useEnsProfile = (identifier: MaybeRefOrGetter<string | undefined>, options?: UseEnsOptions) =>
88
- useEnsBase('profile', identifier, [...ENS_KEYS_PROFILE], options)
@@ -1,36 +0,0 @@
1
- import type { WatchStopHandle } from 'vue'
2
- import { formatEther, formatGwei } from 'viem'
3
- import { getGasPrice } from '@wagmi/core'
4
- import { useConfig, useBlockNumber } from '@wagmi/vue'
5
-
6
- let priceWatcher: WatchStopHandle | null = null
7
- const price: Ref<bigint> = ref(0n)
8
-
9
- export const useGasPrice = () => {
10
- const config = useConfig()
11
- const { data: blockNumber } = useBlockNumber()
12
-
13
- const updatePrice = async () => {
14
- price.value = await getGasPrice(config)
15
- }
16
-
17
- if (!priceWatcher) {
18
- updatePrice()
19
- priceWatcher = watch(blockNumber, () => updatePrice())
20
- }
21
-
22
- const unitPrice = computed(() => ({
23
- wei: price.value,
24
- gwei: formatGwei(price.value),
25
- eth: formatEther(price.value),
26
-
27
- formatted: {
28
- gwei: price.value > 2_000_000_000_000n
29
- ? Math.round(parseFloat(formatGwei(price.value)))
30
- : parseFloat(formatGwei(price.value)).toFixed(1),
31
- eth: formatEther(price.value),
32
- },
33
- }))
34
-
35
- return unitPrice
36
- }
@@ -1 +0,0 @@
1
- export const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
@@ -1,103 +0,0 @@
1
- import { readContract } from '@wagmi/core'
2
-
3
- const CHAINLINK_ETH_USD_ABI = [
4
- {
5
- inputs: [],
6
- name: 'latestRoundData',
7
- outputs: [
8
- { internalType: 'uint80', name: 'roundId', type: 'uint80' },
9
- { internalType: 'int256', name: 'answer', type: 'int256' },
10
- { internalType: 'uint256', name: 'startedAt', type: 'uint256' },
11
- { internalType: 'uint256', name: 'updatedAt', type: 'uint256' },
12
- { internalType: 'uint80', name: 'answeredInRound', type: 'uint80' },
13
- ],
14
- stateMutability: 'view',
15
- type: 'function',
16
- },
17
- ] as const
18
-
19
- const CHAINLINK_ETH_USD = '0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419'
20
- const STORAGE_KEY = 'evm:price-feed'
21
- const CACHE_TTL = 3_600 // 1 hour in seconds
22
-
23
- interface PriceFeedState {
24
- ethUSDRaw: bigint | null
25
- lastUpdated: number
26
- }
27
-
28
- const state = reactive<PriceFeedState>({
29
- ethUSDRaw: null,
30
- lastUpdated: 0,
31
- })
32
-
33
- function loadFromStorage() {
34
- if (!import.meta.client) return
35
-
36
- try {
37
- const stored = localStorage.getItem(STORAGE_KEY)
38
- if (!stored) return
39
-
40
- const parsed = parseJSON(stored) as PriceFeedState
41
- if (parsed.ethUSDRaw) state.ethUSDRaw = parsed.ethUSDRaw
42
- if (parsed.lastUpdated) state.lastUpdated = parsed.lastUpdated
43
- } catch {
44
- // Ignore corrupted storage
45
- }
46
- }
47
-
48
- function saveToStorage() {
49
- if (!import.meta.client) return
50
-
51
- try {
52
- localStorage.setItem(STORAGE_KEY, stringifyJSON({
53
- ethUSDRaw: state.ethUSDRaw,
54
- lastUpdated: state.lastUpdated,
55
- }))
56
- } catch {
57
- // Ignore storage errors
58
- }
59
- }
60
-
61
- export const usePriceFeed = () => {
62
- const { $wagmi } = useNuxtApp()
63
-
64
- // Load cached data on first use
65
- if (!state.lastUpdated) loadFromStorage()
66
-
67
- const ethUSD = computed(() => state.ethUSDRaw ? state.ethUSDRaw / BigInt(1e8) : 0n)
68
- const ethUSC = computed(() => state.ethUSDRaw ? state.ethUSDRaw / BigInt(1e6) : 0n)
69
- const ethUSDFormatted = computed(() => formatPrice(Number(ethUSC.value) / 100, 2))
70
-
71
- const weiToUSD = (wei: bigint) => {
72
- const cents = (wei * (state.ethUSDRaw || 0n)) / (10n ** 18n) / (10n ** 6n)
73
- return formatPrice(Number(cents) / 100, 2)
74
- }
75
-
76
- async function fetchPrice() {
77
- if (nowInSeconds() - state.lastUpdated < CACHE_TTL) return
78
-
79
- try {
80
- const [, answer] = await readContract($wagmi, {
81
- address: CHAINLINK_ETH_USD,
82
- abi: CHAINLINK_ETH_USD_ABI,
83
- functionName: 'latestRoundData',
84
- chainId: 1,
85
- })
86
-
87
- state.ethUSDRaw = answer
88
- state.lastUpdated = nowInSeconds()
89
- saveToStorage()
90
- } catch (error) {
91
- console.warn('Error fetching ETH/USD price:', error)
92
- }
93
- }
94
-
95
- return {
96
- ethUSDRaw: computed(() => state.ethUSDRaw),
97
- ethUSD,
98
- ethUSC,
99
- ethUSDFormatted,
100
- weiToUSD,
101
- fetchPrice,
102
- }
103
- }
@@ -1,7 +0,0 @@
1
- export default defineNuxtPlugin(() => {
2
- const priceFeed = usePriceFeed()
3
-
4
- priceFeed.fetchPrice()
5
-
6
- setInterval(() => priceFeed.fetchPrice(), 60 * 60 * 1000)
7
- })