@1001-digital/layers.evm 0.0.8 → 1.0.0
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 +2 -2
- 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
|
}
|
|
@@ -55,7 +55,7 @@ interface TextConfig {
|
|
|
55
55
|
const checkChain = useEnsureChainIdCheck();
|
|
56
56
|
|
|
57
57
|
const { $wagmi } = useNuxtApp();
|
|
58
|
-
const
|
|
58
|
+
const blockExplorer = useBlockExplorer();
|
|
59
59
|
|
|
60
60
|
const props = withDefaults(
|
|
61
61
|
defineProps<{
|
|
@@ -122,7 +122,7 @@ const complete = ref(false);
|
|
|
122
122
|
const error = ref("");
|
|
123
123
|
const tx = ref<Hash | null>(null);
|
|
124
124
|
const receipt = ref<TransactionReceipt | null>(null);
|
|
125
|
-
const txLink = computed(() => `${
|
|
125
|
+
const txLink = computed(() => `${blockExplorer}/tx/${tx.value}`);
|
|
126
126
|
|
|
127
127
|
const step = computed(() => {
|
|
128
128
|
if (
|
|
@@ -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": "0.0
|
|
4
|
+
"version": "1.0.0",
|
|
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",
|