@1001-digital/layers.evm 1.0.1 → 1.0.3
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,11 +1,11 @@
|
|
|
1
1
|
export default defineAppConfig({
|
|
2
2
|
evm: {
|
|
3
3
|
title: 'EVM Layer Playground',
|
|
4
|
-
defaultChain: '
|
|
4
|
+
defaultChain: 'sepolia',
|
|
5
5
|
chains: {
|
|
6
|
-
|
|
7
|
-
id:
|
|
8
|
-
blockExplorer: 'https://etherscan.io',
|
|
6
|
+
sepolia: {
|
|
7
|
+
id: 11155111,
|
|
8
|
+
blockExplorer: 'https://sepolia.etherscan.io',
|
|
9
9
|
},
|
|
10
10
|
},
|
|
11
11
|
},
|
|
@@ -14,26 +14,19 @@
|
|
|
14
14
|
<h2>Transaction Flow Example</h2>
|
|
15
15
|
<p>Send 0 ETH to your own address</p>
|
|
16
16
|
|
|
17
|
-
<EvmTransactionFlow
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
error: 'An error occurred while processing your transaction.',
|
|
31
|
-
},
|
|
32
|
-
action: {
|
|
33
|
-
confirm: 'Send Transaction',
|
|
34
|
-
error: 'Try Again',
|
|
35
|
-
},
|
|
36
|
-
}" @complete="onTransactionComplete" @cancel="onTransactionCancel">
|
|
17
|
+
<EvmTransactionFlow
|
|
18
|
+
:request="sendTransaction"
|
|
19
|
+
:text="{
|
|
20
|
+
title: { confirm: 'Send Transaction' },
|
|
21
|
+
lead: {
|
|
22
|
+
confirm:
|
|
23
|
+
'This will send 0 ETH to your address as a test transaction.',
|
|
24
|
+
},
|
|
25
|
+
action: { confirm: 'Send Transaction' },
|
|
26
|
+
}"
|
|
27
|
+
@complete="onTransactionComplete"
|
|
28
|
+
@cancel="onTransactionCancel"
|
|
29
|
+
>
|
|
37
30
|
<template #start="{ start }">
|
|
38
31
|
<Actions>
|
|
39
32
|
<Button @click="start">Start Transaction</Button>
|
|
@@ -47,10 +40,6 @@
|
|
|
47
40
|
<p><strong>Chain:</strong> {{ chainId }}</p>
|
|
48
41
|
</div>
|
|
49
42
|
</template>
|
|
50
|
-
|
|
51
|
-
<template #complete>
|
|
52
|
-
<p class="success">Transaction confirmed successfully!</p>
|
|
53
|
-
</template>
|
|
54
43
|
</EvmTransactionFlow>
|
|
55
44
|
</Card>
|
|
56
45
|
</template>
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url'
|
|
2
2
|
|
|
3
|
+
const layerDir = fileURLToPath(new URL('..', import.meta.url))
|
|
4
|
+
|
|
3
5
|
export default defineNuxtConfig({
|
|
4
6
|
extends: ['@1001-digital/layers.base', '..'],
|
|
5
7
|
modules: ['@nuxt/eslint'],
|
|
6
8
|
eslint: {
|
|
7
9
|
config: {
|
|
8
10
|
// Use the generated ESLint config for lint root project as well
|
|
9
|
-
rootDir:
|
|
11
|
+
rootDir: layerDir,
|
|
10
12
|
}
|
|
11
|
-
}
|
|
13
|
+
},
|
|
14
|
+
hooks: {
|
|
15
|
+
'vite:serverCreated': (server) => {
|
|
16
|
+
server.watcher.add(layerDir)
|
|
17
|
+
},
|
|
18
|
+
},
|
|
12
19
|
})
|
|
@@ -1,35 +1,81 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<Button
|
|
2
|
+
<Button
|
|
3
|
+
v-if="showConnect"
|
|
4
|
+
@click="chooseModalOpen = true"
|
|
5
|
+
:class="className"
|
|
6
|
+
>
|
|
3
7
|
<slot>Connect Wallet</slot>
|
|
4
8
|
</Button>
|
|
5
|
-
<slot
|
|
9
|
+
<slot
|
|
10
|
+
v-else
|
|
11
|
+
name="connected"
|
|
12
|
+
:address="address"
|
|
13
|
+
>
|
|
6
14
|
<EvmAccount :address="address" />
|
|
7
15
|
</slot>
|
|
8
16
|
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
<Dialog
|
|
18
|
+
v-if="showConnect"
|
|
19
|
+
title="Connect Wallet"
|
|
20
|
+
v-model:open="chooseModalOpen"
|
|
21
|
+
@closed="onModalClosed"
|
|
22
|
+
>
|
|
23
|
+
<Alert
|
|
24
|
+
v-if="errorMessage"
|
|
25
|
+
type="error"
|
|
26
|
+
>
|
|
12
27
|
{{ errorMessage }}
|
|
13
28
|
</Alert>
|
|
14
|
-
<EvmWalletConnectQR
|
|
15
|
-
|
|
29
|
+
<EvmWalletConnectQR
|
|
30
|
+
v-if="walletConnectUri"
|
|
31
|
+
:uri="walletConnectUri"
|
|
32
|
+
/>
|
|
33
|
+
<EvmMetaMaskQR
|
|
34
|
+
v-else-if="metaMaskUri"
|
|
35
|
+
:uri="metaMaskUri"
|
|
36
|
+
/>
|
|
16
37
|
<template v-else-if="isConnecting">
|
|
17
|
-
<Loading
|
|
38
|
+
<Loading
|
|
39
|
+
txt="Waiting for wallet confirmation..."
|
|
40
|
+
spinner
|
|
41
|
+
stacked
|
|
42
|
+
/>
|
|
18
43
|
</template>
|
|
19
|
-
<div
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
44
|
+
<div
|
|
45
|
+
v-else
|
|
46
|
+
class="wallet-options"
|
|
47
|
+
>
|
|
48
|
+
<Button
|
|
49
|
+
v-for="connector in shownConnectors"
|
|
50
|
+
:key="connector.uid"
|
|
51
|
+
@click="() => login(connector)"
|
|
52
|
+
class="choose-connector"
|
|
53
|
+
>
|
|
54
|
+
<img
|
|
55
|
+
v-if="ICONS[connector.name]"
|
|
56
|
+
:src="
|
|
57
|
+
connector.icon || `${base}icons/wallets/${ICONS[connector.name]}`
|
|
58
|
+
"
|
|
59
|
+
:alt="connector.name"
|
|
60
|
+
/>
|
|
61
|
+
<div
|
|
62
|
+
v-else
|
|
63
|
+
class="default-wallet-icon"
|
|
64
|
+
>
|
|
65
|
+
<Icon type="wallet" />
|
|
66
|
+
</div>
|
|
24
67
|
<span>{{ connector.name }}</span>
|
|
25
68
|
</Button>
|
|
26
|
-
<Button
|
|
69
|
+
<Button
|
|
70
|
+
to="https://ethereum.org/wallets/"
|
|
71
|
+
target="_blank"
|
|
72
|
+
class="link muted small"
|
|
73
|
+
>
|
|
27
74
|
<Icon type="help" />
|
|
28
75
|
<span>New to wallets?</span>
|
|
29
76
|
</Button>
|
|
30
77
|
</div>
|
|
31
|
-
|
|
32
|
-
</Teleport>
|
|
78
|
+
</Dialog>
|
|
33
79
|
</template>
|
|
34
80
|
|
|
35
81
|
<script setup lang="ts">
|
|
@@ -46,7 +92,7 @@ const ICONS: Record<string, string> = {
|
|
|
46
92
|
}
|
|
47
93
|
|
|
48
94
|
const PRIORITY: Record<string, number> = {
|
|
49
|
-
|
|
95
|
+
WalletConnect: 20,
|
|
50
96
|
'Coinbase Wallet': 10,
|
|
51
97
|
}
|
|
52
98
|
|
|
@@ -66,10 +112,13 @@ const { address, isConnected } = useConnection()
|
|
|
66
112
|
const showConnect = computed(() => !isConnected.value)
|
|
67
113
|
const shownConnectors = computed(() => {
|
|
68
114
|
const unique = Array.from(
|
|
69
|
-
new Map(
|
|
115
|
+
new Map(
|
|
116
|
+
connectors?.map((connector) => [connector.name, connector]),
|
|
117
|
+
).values(),
|
|
70
118
|
)
|
|
71
119
|
|
|
72
|
-
const filtered =
|
|
120
|
+
const filtered =
|
|
121
|
+
unique.length > 1 ? unique.filter((c) => c.id !== 'injected') : unique
|
|
73
122
|
|
|
74
123
|
return filtered.sort((a, b) => {
|
|
75
124
|
const priorityA = PRIORITY[a.name] ?? 5
|
|
@@ -119,7 +168,11 @@ const login = async (connector: Connector) => {
|
|
|
119
168
|
metaMaskUri.value = ''
|
|
120
169
|
|
|
121
170
|
const errorMsg = error instanceof Error ? error.message : ''
|
|
122
|
-
if (
|
|
171
|
+
if (
|
|
172
|
+
errorMsg.includes('User rejected') ||
|
|
173
|
+
errorMsg.includes('rejected') ||
|
|
174
|
+
errorMsg.includes('denied')
|
|
175
|
+
) {
|
|
123
176
|
errorMessage.value = 'Connection cancelled. Please try again.'
|
|
124
177
|
} else {
|
|
125
178
|
errorMessage.value = 'Failed to connect. Please try again.'
|
|
@@ -140,7 +193,9 @@ const onModalClosed = () => {
|
|
|
140
193
|
}
|
|
141
194
|
|
|
142
195
|
const check = () =>
|
|
143
|
-
isConnected.value
|
|
196
|
+
isConnected.value
|
|
197
|
+
? emit('connected', { address: address.value })
|
|
198
|
+
: emit('disconnected')
|
|
144
199
|
watch(isConnected, () => check())
|
|
145
200
|
onMounted(() => check())
|
|
146
201
|
</script>
|
|
@@ -155,18 +210,25 @@ onMounted(() => check())
|
|
|
155
210
|
inline-size: auto;
|
|
156
211
|
justify-content: flex-start;
|
|
157
212
|
|
|
158
|
-
img
|
|
213
|
+
img,
|
|
214
|
+
.default-wallet-icon {
|
|
159
215
|
margin: -1rem 0 -1rem -0.6rem;
|
|
160
216
|
width: var(--size-5);
|
|
161
217
|
height: var(--size-5);
|
|
162
218
|
}
|
|
163
219
|
|
|
164
|
-
|
|
220
|
+
.default-wallet-icon {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
justify-content: center;
|
|
224
|
+
background: var(--gray-z-2);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
span:last-child {
|
|
165
228
|
border-left: var(--border);
|
|
166
229
|
padding-left: var(--spacer-sm);
|
|
167
230
|
}
|
|
168
231
|
}
|
|
169
|
-
|
|
170
232
|
}
|
|
171
233
|
|
|
172
234
|
.link.muted {
|
|
@@ -8,57 +8,82 @@
|
|
|
8
8
|
|
|
9
9
|
<Dialog
|
|
10
10
|
v-model:open="open"
|
|
11
|
-
:
|
|
11
|
+
:closable="canDismiss"
|
|
12
12
|
:click-outside="canDismiss"
|
|
13
|
+
:title="text.title[step]"
|
|
13
14
|
class="transaction-flow"
|
|
14
15
|
>
|
|
15
16
|
<slot name="before" />
|
|
16
17
|
|
|
17
|
-
<h1 v-if="text.title[step]">{{ text.title[step] }}</h1>
|
|
18
|
-
|
|
19
18
|
<Loading
|
|
20
19
|
v-if="step === 'requesting' || step === 'waiting'"
|
|
21
20
|
spinner
|
|
22
|
-
|
|
21
|
+
stacked
|
|
22
|
+
:txt="text.lead[step] || ''"
|
|
23
23
|
/>
|
|
24
24
|
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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>
|
|
29
35
|
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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>
|
|
34
43
|
|
|
35
44
|
<Button
|
|
36
45
|
v-if="step === 'waiting'"
|
|
37
46
|
:to="txLink"
|
|
38
47
|
target="_blank"
|
|
39
|
-
class="
|
|
48
|
+
class="link muted small centered"
|
|
40
49
|
>
|
|
41
|
-
|
|
50
|
+
<Icon type="link" />
|
|
51
|
+
<span>View on Block Explorer</span>
|
|
42
52
|
</Button>
|
|
43
53
|
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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>
|
|
62
87
|
</Dialog>
|
|
63
88
|
</template>
|
|
64
89
|
|
|
@@ -68,9 +93,9 @@ import type { Config } from '@wagmi/vue'
|
|
|
68
93
|
import type { TransactionReceipt, Hash } from 'viem'
|
|
69
94
|
|
|
70
95
|
interface TextConfig {
|
|
71
|
-
title
|
|
72
|
-
lead
|
|
73
|
-
action
|
|
96
|
+
title?: Record<string, string>
|
|
97
|
+
lead?: Record<string, string>
|
|
98
|
+
action?: Record<string, string>
|
|
74
99
|
}
|
|
75
100
|
|
|
76
101
|
type Step =
|
|
@@ -82,6 +107,29 @@ type Step =
|
|
|
82
107
|
| 'complete'
|
|
83
108
|
| 'error'
|
|
84
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()
|
|
85
133
|
const checkChain = useEnsureChainIdCheck()
|
|
86
134
|
|
|
87
135
|
const { $wagmi } = useNuxtApp()
|
|
@@ -98,17 +146,6 @@ const props = withDefaults(
|
|
|
98
146
|
dismissable?: boolean
|
|
99
147
|
}>(),
|
|
100
148
|
{
|
|
101
|
-
text: () => ({
|
|
102
|
-
title: {
|
|
103
|
-
confirm: 'Confirm Transaction',
|
|
104
|
-
},
|
|
105
|
-
lead: {
|
|
106
|
-
confirm: 'Please review and confirm this transaction.',
|
|
107
|
-
},
|
|
108
|
-
action: {
|
|
109
|
-
confirm: 'Execute',
|
|
110
|
-
},
|
|
111
|
-
}),
|
|
112
149
|
delayAfter: 2000,
|
|
113
150
|
delayAutoclose: 2000,
|
|
114
151
|
skipConfirmation: false,
|
|
@@ -122,12 +159,21 @@ const emit = defineEmits<{
|
|
|
122
159
|
cancel: []
|
|
123
160
|
}>()
|
|
124
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
|
+
|
|
125
168
|
const step = ref<Step>('idle')
|
|
126
169
|
|
|
127
170
|
const open = computed({
|
|
128
171
|
get: () => step.value !== 'idle',
|
|
129
172
|
set: (v) => {
|
|
130
|
-
if (!v)
|
|
173
|
+
if (!v) {
|
|
174
|
+
step.value = 'idle'
|
|
175
|
+
error.value = ''
|
|
176
|
+
}
|
|
131
177
|
},
|
|
132
178
|
})
|
|
133
179
|
|
|
@@ -187,7 +233,8 @@ const initializeRequest = async (request = cachedRequest.value) => {
|
|
|
187
233
|
} catch (e: unknown) {
|
|
188
234
|
const err = e as { cause?: { code?: number }; shortMessage?: string }
|
|
189
235
|
if (err?.cause?.code === 4001) {
|
|
190
|
-
|
|
236
|
+
error.value = 'Transaction rejected by user.'
|
|
237
|
+
step.value = 'error'
|
|
191
238
|
} else {
|
|
192
239
|
error.value = err.shortMessage || 'Error submitting transaction request.'
|
|
193
240
|
step.value = 'error'
|
|
@@ -215,6 +262,7 @@ const start = () => {
|
|
|
215
262
|
|
|
216
263
|
const cancel = () => {
|
|
217
264
|
step.value = 'idle'
|
|
265
|
+
error.value = ''
|
|
218
266
|
emit('cancel')
|
|
219
267
|
}
|
|
220
268
|
|
|
@@ -224,7 +272,7 @@ defineExpose({
|
|
|
224
272
|
</script>
|
|
225
273
|
|
|
226
274
|
<style>
|
|
227
|
-
.transaction-flow {
|
|
275
|
+
.transaction-flow > section {
|
|
228
276
|
display: grid;
|
|
229
277
|
gap: var(--spacer);
|
|
230
278
|
|
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.3",
|
|
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.19"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"@1001-digital/layers.base": "^0.0.
|
|
16
|
+
"@1001-digital/layers.base": "^0.0.19"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@types/qrcode": "^1.5.6",
|