@1001-digital/layers.evm 0.0.8 → 1.0.1
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/.env.example +4 -0
- package/.playground/app/app.config.ts +10 -3
- package/AGENTS.md +24 -10
- package/app/app.config.ts +20 -4
- package/app/components/EvmTransactionFlow.vue +123 -139
- package/app/composables/chainId.ts +20 -3
- package/app/plugins/wagmi.ts +32 -25
- package/app/utils/chains.ts +13 -0
- package/nuxt.config.ts +6 -7
- package/package.json +2 -1
- package/.playground/.env.example +0 -7
package/.env.example
ADDED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export default defineAppConfig({
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
evm: {
|
|
3
|
+
title: 'EVM Layer Playground',
|
|
4
|
+
defaultChain: 'mainnet',
|
|
5
|
+
chains: {
|
|
6
|
+
mainnet: {
|
|
7
|
+
id: 1,
|
|
8
|
+
blockExplorer: 'https://etherscan.io',
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
5
12
|
})
|
package/AGENTS.md
CHANGED
|
@@ -22,7 +22,7 @@ Uses modern wagmi 0.4.x patterns:
|
|
|
22
22
|
- `useConnectionEffect` (not deprecated `useAccountEffect`)
|
|
23
23
|
- `useSwitchConnection` (not deprecated `useSwitchAccount`)
|
|
24
24
|
|
|
25
|
-
Configured chains: mainnet, sepolia, holesky, localhost
|
|
25
|
+
Configured chains: resolved dynamically from `app.config.ts` via `evm.chains` map (supports mainnet, sepolia, holesky, optimism, arbitrum, base, polygon, localhost out of the box)
|
|
26
26
|
|
|
27
27
|
Connectors: injected, coinbaseWallet, metaMask, walletConnect
|
|
28
28
|
|
|
@@ -37,7 +37,9 @@ Connectors: injected, coinbaseWallet, metaMask, walletConnect
|
|
|
37
37
|
|
|
38
38
|
## Composables
|
|
39
39
|
|
|
40
|
-
- `
|
|
40
|
+
- `useChainConfig(key?)` - Get `{ id, blockExplorer }` for a named chain (defaults to `defaultChain`)
|
|
41
|
+
- `useMainChainId()` - Get main chain ID from app config
|
|
42
|
+
- `useBlockExplorer(key?)` - Get block explorer URL for a named chain
|
|
41
43
|
- `useEnsureChainIdCheck()` - Validate/switch chain before transactions
|
|
42
44
|
- `useBaseURL()` - Get base URL with trailing slash
|
|
43
45
|
- `useClipboard()` - Copy text to clipboard with copied state
|
|
@@ -46,17 +48,29 @@ Connectors: injected, coinbaseWallet, metaMask, walletConnect
|
|
|
46
48
|
|
|
47
49
|
- `shortAddress(address, length)` - Truncate address for display
|
|
48
50
|
- `formatETH(value, maxDecimals)` - Format ETH values
|
|
51
|
+
- `resolveChain(id)` - Resolve chain ID to viem Chain object
|
|
49
52
|
|
|
50
|
-
##
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
Static chain config lives in `app.config.ts` (safe to commit):
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
evm: {
|
|
59
|
+
title: 'My dApp',
|
|
60
|
+
defaultChain: 'mainnet',
|
|
61
|
+
chains: {
|
|
62
|
+
mainnet: { id: 1, blockExplorer: 'https://etherscan.io' },
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Sensitive RPC URLs live in `runtimeConfig.public.evm` (env-driven):
|
|
51
68
|
|
|
52
69
|
```bash
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
NUXT_PUBLIC_RPC2=""
|
|
58
|
-
NUXT_PUBLIC_RPC3=""
|
|
59
|
-
NUXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=""
|
|
70
|
+
NUXT_PUBLIC_EVM_WALLET_CONNECT_PROJECT_ID=""
|
|
71
|
+
NUXT_PUBLIC_EVM_CHAINS_MAINNET_RPC1=""
|
|
72
|
+
NUXT_PUBLIC_EVM_CHAINS_MAINNET_RPC2=""
|
|
73
|
+
NUXT_PUBLIC_EVM_CHAINS_MAINNET_RPC3=""
|
|
60
74
|
```
|
|
61
75
|
|
|
62
76
|
## Key directories
|
package/app/app.config.ts
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
export default defineAppConfig({
|
|
2
2
|
evm: {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
title: 'EVM Layer',
|
|
4
|
+
defaultChain: 'mainnet',
|
|
5
|
+
chains: {
|
|
6
|
+
mainnet: {
|
|
7
|
+
id: 1,
|
|
8
|
+
blockExplorer: 'https://etherscan.io',
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
},
|
|
5
12
|
})
|
|
6
13
|
|
|
14
|
+
interface EvmChainConfig {
|
|
15
|
+
id?: number
|
|
16
|
+
blockExplorer?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
declare module '@nuxt/schema' {
|
|
8
20
|
interface AppConfigInput {
|
|
9
21
|
evm?: {
|
|
10
|
-
/**
|
|
11
|
-
|
|
22
|
+
/** App title */
|
|
23
|
+
title?: string
|
|
24
|
+
/** Key into `chains` that serves as the app's primary chain */
|
|
25
|
+
defaultChain?: string
|
|
26
|
+
/** Named chain definitions */
|
|
27
|
+
chains?: Record<string, EvmChainConfig>
|
|
12
28
|
}
|
|
13
29
|
}
|
|
14
30
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<slot
|
|
2
|
+
<slot
|
|
3
|
+
:start="start"
|
|
4
|
+
:step="step"
|
|
5
|
+
:open="open"
|
|
6
|
+
name="start"
|
|
7
|
+
></slot>
|
|
3
8
|
|
|
4
9
|
<Dialog
|
|
5
10
|
v-model:open="open"
|
|
@@ -11,12 +16,21 @@
|
|
|
11
16
|
|
|
12
17
|
<h1 v-if="text.title[step]">{{ text.title[step] }}</h1>
|
|
13
18
|
|
|
19
|
+
<Loading
|
|
20
|
+
v-if="step === 'requesting' || step === 'waiting'"
|
|
21
|
+
spinner
|
|
22
|
+
txt=""
|
|
23
|
+
/>
|
|
24
|
+
|
|
14
25
|
<div class="text">
|
|
15
26
|
<p v-if="text.lead[step]">{{ text.lead[step] }}</p>
|
|
16
27
|
<p v-if="error">{{ error }}</p>
|
|
17
28
|
</div>
|
|
18
29
|
|
|
19
|
-
<slot
|
|
30
|
+
<slot
|
|
31
|
+
:name="step"
|
|
32
|
+
:cancel="cancel"
|
|
33
|
+
></slot>
|
|
20
34
|
|
|
21
35
|
<Button
|
|
22
36
|
v-if="step === 'waiting'"
|
|
@@ -24,59 +38,75 @@
|
|
|
24
38
|
target="_blank"
|
|
25
39
|
class="block-explorer"
|
|
26
40
|
>
|
|
27
|
-
|
|
28
|
-
<span>View on Block Explorer</span>
|
|
41
|
+
View on Block Explorer
|
|
29
42
|
</Button>
|
|
30
43
|
|
|
31
44
|
<Actions v-if="step === 'chain'">
|
|
32
|
-
<Button
|
|
45
|
+
<Button
|
|
46
|
+
@click="cancel"
|
|
47
|
+
class="secondary"
|
|
48
|
+
>Cancel</Button
|
|
49
|
+
>
|
|
33
50
|
</Actions>
|
|
34
51
|
|
|
35
52
|
<Actions v-if="step === 'confirm' || step === 'error'">
|
|
36
|
-
<Button
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
<Button
|
|
54
|
+
@click="cancel"
|
|
55
|
+
class="secondary"
|
|
56
|
+
>Cancel</Button
|
|
57
|
+
>
|
|
58
|
+
<Button @click="() => initializeRequest()">
|
|
59
|
+
{{ text.action[step] || 'Execute' }}
|
|
60
|
+
</Button>
|
|
40
61
|
</Actions>
|
|
41
62
|
</Dialog>
|
|
42
63
|
</template>
|
|
43
64
|
|
|
44
65
|
<script setup lang="ts">
|
|
45
|
-
import { waitForTransactionReceipt, watchChainId } from
|
|
46
|
-
import type { Config } from
|
|
47
|
-
import type { TransactionReceipt, Hash } from
|
|
66
|
+
import { waitForTransactionReceipt, watchChainId } from '@wagmi/core'
|
|
67
|
+
import type { Config } from '@wagmi/vue'
|
|
68
|
+
import type { TransactionReceipt, Hash } from 'viem'
|
|
48
69
|
|
|
49
70
|
interface TextConfig {
|
|
50
|
-
title: Record<string, string
|
|
51
|
-
lead: Record<string, string
|
|
52
|
-
action: Record<string, string
|
|
71
|
+
title: Record<string, string>
|
|
72
|
+
lead: Record<string, string>
|
|
73
|
+
action: Record<string, string>
|
|
53
74
|
}
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
type Step =
|
|
77
|
+
| 'idle'
|
|
78
|
+
| 'confirm'
|
|
79
|
+
| 'chain'
|
|
80
|
+
| 'requesting'
|
|
81
|
+
| 'waiting'
|
|
82
|
+
| 'complete'
|
|
83
|
+
| 'error'
|
|
84
|
+
|
|
85
|
+
const checkChain = useEnsureChainIdCheck()
|
|
56
86
|
|
|
57
|
-
const { $wagmi } = useNuxtApp()
|
|
58
|
-
const
|
|
87
|
+
const { $wagmi } = useNuxtApp()
|
|
88
|
+
const blockExplorer = useBlockExplorer()
|
|
59
89
|
|
|
60
90
|
const props = withDefaults(
|
|
61
91
|
defineProps<{
|
|
62
|
-
text?: TextConfig
|
|
63
|
-
request?: () => Promise<Hash
|
|
64
|
-
delayAfter?: number
|
|
65
|
-
delayAutoclose?: number
|
|
66
|
-
skipConfirmation?: boolean
|
|
67
|
-
autoCloseSuccess?: boolean
|
|
68
|
-
dismissable?: boolean
|
|
92
|
+
text?: TextConfig
|
|
93
|
+
request?: () => Promise<Hash>
|
|
94
|
+
delayAfter?: number
|
|
95
|
+
delayAutoclose?: number
|
|
96
|
+
skipConfirmation?: boolean
|
|
97
|
+
autoCloseSuccess?: boolean
|
|
98
|
+
dismissable?: boolean
|
|
69
99
|
}>(),
|
|
70
100
|
{
|
|
71
101
|
text: () => ({
|
|
72
102
|
title: {
|
|
73
|
-
confirm:
|
|
103
|
+
confirm: 'Confirm Transaction',
|
|
74
104
|
},
|
|
75
105
|
lead: {
|
|
76
|
-
confirm:
|
|
106
|
+
confirm: 'Please review and confirm this transaction.',
|
|
77
107
|
},
|
|
78
108
|
action: {
|
|
79
|
-
confirm:
|
|
109
|
+
confirm: 'Execute',
|
|
80
110
|
},
|
|
81
111
|
}),
|
|
82
112
|
delayAfter: 2000,
|
|
@@ -85,147 +115,112 @@ const props = withDefaults(
|
|
|
85
115
|
autoCloseSuccess: true,
|
|
86
116
|
dismissable: true,
|
|
87
117
|
},
|
|
88
|
-
)
|
|
118
|
+
)
|
|
89
119
|
|
|
90
120
|
const emit = defineEmits<{
|
|
91
|
-
complete: [receipt: TransactionReceipt]
|
|
92
|
-
cancel: []
|
|
93
|
-
}>()
|
|
121
|
+
complete: [receipt: TransactionReceipt]
|
|
122
|
+
cancel: []
|
|
123
|
+
}>()
|
|
124
|
+
|
|
125
|
+
const step = ref<Step>('idle')
|
|
94
126
|
|
|
95
|
-
const open =
|
|
127
|
+
const open = computed({
|
|
128
|
+
get: () => step.value !== 'idle',
|
|
129
|
+
set: (v) => {
|
|
130
|
+
if (!v) step.value = 'idle'
|
|
131
|
+
},
|
|
132
|
+
})
|
|
96
133
|
|
|
97
|
-
const switchChain = ref(false);
|
|
98
134
|
watchChainId($wagmi as Config, {
|
|
99
135
|
async onChange() {
|
|
100
|
-
if (
|
|
136
|
+
if (step.value !== 'chain') return
|
|
101
137
|
|
|
102
138
|
if (await checkChain()) {
|
|
103
|
-
|
|
104
|
-
initializeRequest();
|
|
105
|
-
} else {
|
|
106
|
-
switchChain.value = true;
|
|
139
|
+
initializeRequest()
|
|
107
140
|
}
|
|
108
141
|
},
|
|
109
|
-
})
|
|
142
|
+
})
|
|
110
143
|
|
|
111
|
-
const cachedRequest = ref(props.request)
|
|
144
|
+
const cachedRequest = ref(props.request)
|
|
112
145
|
watch(
|
|
113
146
|
() => props.request,
|
|
114
|
-
() => {
|
|
115
|
-
cachedRequest.value =
|
|
147
|
+
(v) => {
|
|
148
|
+
cachedRequest.value = v
|
|
116
149
|
},
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
const requesting = ref(false);
|
|
120
|
-
const waiting = ref(false);
|
|
121
|
-
const complete = ref(false);
|
|
122
|
-
const error = ref("");
|
|
123
|
-
const tx = ref<Hash | null>(null);
|
|
124
|
-
const receipt = ref<TransactionReceipt | null>(null);
|
|
125
|
-
const txLink = computed(() => `${config.public.blockExplorer}/tx/${tx.value}`);
|
|
126
|
-
|
|
127
|
-
const step = computed(() => {
|
|
128
|
-
if (
|
|
129
|
-
open.value &&
|
|
130
|
-
!requesting.value &&
|
|
131
|
-
!switchChain.value &&
|
|
132
|
-
!waiting.value &&
|
|
133
|
-
!complete.value
|
|
134
|
-
) {
|
|
135
|
-
return "confirm";
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (switchChain.value) {
|
|
139
|
-
return "chain";
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (requesting.value) {
|
|
143
|
-
return "requesting";
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (waiting.value) {
|
|
147
|
-
return "waiting";
|
|
148
|
-
}
|
|
150
|
+
)
|
|
149
151
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return "error";
|
|
155
|
-
});
|
|
152
|
+
const error = ref('')
|
|
153
|
+
const tx = ref<Hash | null>(null)
|
|
154
|
+
const receipt = ref<TransactionReceipt | null>(null)
|
|
155
|
+
const txLink = computed(() => `${blockExplorer}/tx/${tx.value}`)
|
|
156
156
|
|
|
157
157
|
const canDismiss = computed(
|
|
158
|
-
() =>
|
|
159
|
-
|
|
158
|
+
() =>
|
|
159
|
+
props.dismissable &&
|
|
160
|
+
step.value !== 'requesting' &&
|
|
161
|
+
step.value !== 'waiting',
|
|
162
|
+
)
|
|
160
163
|
|
|
161
164
|
const initializeRequest = async (request = cachedRequest.value) => {
|
|
162
|
-
cachedRequest.value = request
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
receipt.value = null;
|
|
165
|
+
cachedRequest.value = request
|
|
166
|
+
error.value = ''
|
|
167
|
+
tx.value = null
|
|
168
|
+
receipt.value = null
|
|
169
|
+
step.value = 'confirm'
|
|
168
170
|
|
|
169
171
|
if (!(await checkChain())) {
|
|
170
|
-
|
|
171
|
-
return
|
|
172
|
-
} else {
|
|
173
|
-
switchChain.value = false;
|
|
172
|
+
step.value = 'chain'
|
|
173
|
+
return
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
if (requesting.value) return;
|
|
177
|
-
|
|
178
176
|
try {
|
|
179
|
-
|
|
180
|
-
tx.value = await request!()
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
complete.value = true;
|
|
177
|
+
step.value = 'requesting'
|
|
178
|
+
tx.value = await request!()
|
|
179
|
+
step.value = 'waiting'
|
|
180
|
+
const receiptObject = await waitForTransactionReceipt($wagmi as Config, {
|
|
181
|
+
hash: tx.value,
|
|
182
|
+
})
|
|
183
|
+
await delay(props.delayAfter)
|
|
184
|
+
receipt.value = receiptObject
|
|
185
|
+
emit('complete', receiptObject)
|
|
186
|
+
step.value = 'complete'
|
|
190
187
|
} catch (e: unknown) {
|
|
191
|
-
const err = e as { cause?: { code?: number }; shortMessage?: string }
|
|
188
|
+
const err = e as { cause?: { code?: number }; shortMessage?: string }
|
|
192
189
|
if (err?.cause?.code === 4001) {
|
|
193
|
-
|
|
190
|
+
step.value = 'idle'
|
|
194
191
|
} else {
|
|
195
|
-
error.value = err.shortMessage ||
|
|
192
|
+
error.value = err.shortMessage || 'Error submitting transaction request.'
|
|
193
|
+
step.value = 'error'
|
|
196
194
|
}
|
|
197
|
-
console.log(e)
|
|
195
|
+
console.log(e)
|
|
198
196
|
}
|
|
199
197
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
await delay(props.delayAutoclose);
|
|
205
|
-
open.value = false;
|
|
206
|
-
await delay(300);
|
|
198
|
+
if (props.autoCloseSuccess && step.value === 'complete') {
|
|
199
|
+
await delay(props.delayAutoclose)
|
|
200
|
+
step.value = 'idle'
|
|
201
|
+
await delay(300)
|
|
207
202
|
}
|
|
208
203
|
|
|
209
|
-
return receipt.value
|
|
210
|
-
}
|
|
204
|
+
return receipt.value
|
|
205
|
+
}
|
|
211
206
|
|
|
212
207
|
const start = () => {
|
|
213
|
-
if (props.skipConfirmation &&
|
|
214
|
-
initializeRequest()
|
|
208
|
+
if (props.skipConfirmation && step.value === 'idle') {
|
|
209
|
+
initializeRequest()
|
|
210
|
+
return
|
|
215
211
|
}
|
|
216
212
|
|
|
217
|
-
|
|
218
|
-
}
|
|
213
|
+
step.value = 'confirm'
|
|
214
|
+
}
|
|
219
215
|
|
|
220
216
|
const cancel = () => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
};
|
|
217
|
+
step.value = 'idle'
|
|
218
|
+
emit('cancel')
|
|
219
|
+
}
|
|
225
220
|
|
|
226
221
|
defineExpose({
|
|
227
222
|
initializeRequest,
|
|
228
|
-
})
|
|
223
|
+
})
|
|
229
224
|
</script>
|
|
230
225
|
|
|
231
226
|
<style>
|
|
@@ -233,22 +228,11 @@ defineExpose({
|
|
|
233
228
|
display: grid;
|
|
234
229
|
gap: var(--spacer);
|
|
235
230
|
|
|
236
|
-
.spinner {
|
|
237
|
-
width: var(--size-7);
|
|
238
|
-
height: var(--size-7);
|
|
239
|
-
margin: calc(-1 * var(--size-4)) 0 var(--size-3);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
231
|
.text {
|
|
243
232
|
width: 100%;
|
|
244
233
|
height: min-content;
|
|
245
234
|
}
|
|
246
235
|
|
|
247
|
-
h1 {
|
|
248
|
-
font-size: var(--font-lg);
|
|
249
|
-
margin-bottom: var(--size-4);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
236
|
p {
|
|
253
237
|
white-space: pre-wrap;
|
|
254
238
|
width: 100%;
|
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
import { useConnection, useSwitchChain } from '@wagmi/vue'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
interface ChainConfig {
|
|
4
|
+
id?: number
|
|
5
|
+
blockExplorer?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const getDefaultChainKey = () => useAppConfig().evm?.defaultChain || 'mainnet'
|
|
9
|
+
|
|
10
|
+
export const useChainConfig = (key?: string) => {
|
|
11
|
+
const appConfig = useAppConfig()
|
|
12
|
+
const resolvedKey = key || getDefaultChainKey()
|
|
13
|
+
const chains = appConfig.evm?.chains as Record<string, ChainConfig> | undefined
|
|
14
|
+
const chain = chains?.[resolvedKey]
|
|
5
15
|
|
|
6
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
id: chain?.id ?? 1,
|
|
18
|
+
blockExplorer: chain?.blockExplorer ?? 'https://etherscan.io',
|
|
19
|
+
}
|
|
7
20
|
}
|
|
8
21
|
|
|
22
|
+
export const useMainChainId = () => useChainConfig().id
|
|
23
|
+
|
|
24
|
+
export const useBlockExplorer = (key?: string) => useChainConfig(key).blockExplorer
|
|
25
|
+
|
|
9
26
|
export const useEnsureChainIdCheck = () => {
|
|
10
27
|
const chainId = useMainChainId()
|
|
11
28
|
const { switchChain } = useSwitchChain()
|
package/app/plugins/wagmi.ts
CHANGED
|
@@ -9,14 +9,38 @@ import {
|
|
|
9
9
|
type Config,
|
|
10
10
|
type CreateConnectorFn,
|
|
11
11
|
} from '@wagmi/vue'
|
|
12
|
-
import { mainnet, sepolia, holesky, localhost } from '@wagmi/vue/chains'
|
|
13
12
|
import { coinbaseWallet, injected, metaMask, walletConnect } from '@wagmi/vue/connectors'
|
|
14
|
-
import type {
|
|
13
|
+
import type { Chain, Transport } from 'viem'
|
|
15
14
|
|
|
16
15
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
16
|
+
const appConfig = useAppConfig()
|
|
17
|
+
const runtimeConfig = nuxtApp.$config.public.evm as {
|
|
18
|
+
walletConnectProjectId: string
|
|
19
|
+
chains: Record<string, { rpc1?: string, rpc2?: string, rpc3?: string }>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const title = appConfig.evm?.title || 'EVM Layer'
|
|
23
|
+
const chainEntries = appConfig.evm?.chains || {}
|
|
24
|
+
|
|
25
|
+
// Build chains and transports from config
|
|
26
|
+
const chains: [Chain, ...Chain[]] = [] as unknown as [Chain, ...Chain[]]
|
|
27
|
+
const transports: Record<number, Transport> = {}
|
|
19
28
|
|
|
29
|
+
for (const [key, entry] of Object.entries(chainEntries)) {
|
|
30
|
+
const chain = resolveChain(entry.id!)
|
|
31
|
+
chains.push(chain)
|
|
32
|
+
|
|
33
|
+
const rpcs = runtimeConfig.chains?.[key]
|
|
34
|
+
const transportList = []
|
|
35
|
+
if (rpcs?.rpc1) transportList.push(http(rpcs.rpc1))
|
|
36
|
+
if (rpcs?.rpc2) transportList.push(http(rpcs.rpc2))
|
|
37
|
+
if (rpcs?.rpc3) transportList.push(http(rpcs.rpc3))
|
|
38
|
+
transportList.push(http())
|
|
39
|
+
|
|
40
|
+
transports[chain.id] = fallback(transportList)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Connectors
|
|
20
44
|
const connectors: CreateConnectorFn[] = [
|
|
21
45
|
injected(),
|
|
22
46
|
coinbaseWallet({
|
|
@@ -33,28 +57,16 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
33
57
|
}),
|
|
34
58
|
]
|
|
35
59
|
|
|
36
|
-
if (import.meta.client &&
|
|
60
|
+
if (import.meta.client && runtimeConfig.walletConnectProjectId)
|
|
37
61
|
connectors.push(
|
|
38
62
|
walletConnect({
|
|
39
|
-
projectId:
|
|
63
|
+
projectId: runtimeConfig.walletConnectProjectId,
|
|
40
64
|
showQrModal: false,
|
|
41
65
|
}),
|
|
42
66
|
)
|
|
43
67
|
|
|
44
|
-
const transportDefinitions: CustomTransport | Transport[] = []
|
|
45
|
-
|
|
46
|
-
if (nuxtApp.$config.public.rpc1)
|
|
47
|
-
transportDefinitions.push(http(nuxtApp.$config.public.rpc1 as string))
|
|
48
|
-
if (nuxtApp.$config.public.rpc2)
|
|
49
|
-
transportDefinitions.push(http(nuxtApp.$config.public.rpc2 as string))
|
|
50
|
-
if (nuxtApp.$config.public.rpc3)
|
|
51
|
-
transportDefinitions.push(http(nuxtApp.$config.public.rpc3 as string))
|
|
52
|
-
transportDefinitions.push(http())
|
|
53
|
-
|
|
54
|
-
const transports = fallback(transportDefinitions)
|
|
55
|
-
|
|
56
68
|
const wagmiConfig: Config = createConfig({
|
|
57
|
-
chains
|
|
69
|
+
chains,
|
|
58
70
|
batch: {
|
|
59
71
|
multicall: true,
|
|
60
72
|
},
|
|
@@ -63,12 +75,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
63
75
|
storage: cookieStorage,
|
|
64
76
|
}),
|
|
65
77
|
ssr: true,
|
|
66
|
-
transports
|
|
67
|
-
[mainnet.id]: mainChainId == 1 ? transports : http(),
|
|
68
|
-
[sepolia.id]: transports,
|
|
69
|
-
[holesky.id]: transports,
|
|
70
|
-
[localhost.id]: transports,
|
|
71
|
-
},
|
|
78
|
+
transports,
|
|
72
79
|
})
|
|
73
80
|
|
|
74
81
|
nuxtApp.vueApp.use(WagmiPlugin, { config: wagmiConfig }).use(VueQueryPlugin, {})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineChain, type Chain } from 'viem'
|
|
2
|
+
import { mainnet, sepolia, holesky, optimism, arbitrum, base, polygon, localhost } from 'viem/chains'
|
|
3
|
+
|
|
4
|
+
const KNOWN: Chain[] = [mainnet, sepolia, holesky, optimism, arbitrum, base, polygon, localhost]
|
|
5
|
+
const byId = new Map<number, Chain>(KNOWN.map(c => [c.id, c]))
|
|
6
|
+
|
|
7
|
+
export const resolveChain = (id: number): Chain =>
|
|
8
|
+
byId.get(id) ?? defineChain({
|
|
9
|
+
id,
|
|
10
|
+
name: `Chain ${id}`,
|
|
11
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
12
|
+
rpcUrls: { default: { http: [] } },
|
|
13
|
+
})
|
package/nuxt.config.ts
CHANGED
|
@@ -8,13 +8,12 @@ export default defineNuxtConfig({
|
|
|
8
8
|
|
|
9
9
|
runtimeConfig: {
|
|
10
10
|
public: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
walletConnectProjectId: '',
|
|
11
|
+
evm: {
|
|
12
|
+
walletConnectProjectId: '',
|
|
13
|
+
chains: {
|
|
14
|
+
mainnet: { rpc1: '', rpc2: '', rpc3: '' },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
18
17
|
},
|
|
19
18
|
},
|
|
20
19
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1001-digital/layers.evm",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.0.1",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"@nuxt/eslint": "latest",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"@types/qrcode": "^1.5.6",
|
|
20
20
|
"@metamask/sdk": "~0.34.0",
|
|
21
21
|
"@tanstack/vue-query": "^5.92.9",
|
|
22
|
+
"@wagmi/core": "^3.3.2",
|
|
22
23
|
"@wagmi/vue": "^0.4.15",
|
|
23
24
|
"@walletconnect/ethereum-provider": "~2.23.4",
|
|
24
25
|
"qrcode": "^1.5.4",
|