Skip to content

Commit 08f15c5

Browse files
authored
fix: production build lazyload walletconnect (#260)
* fix: production build lazyload walletconnect split from other deploy PR * chore: address code review comments * fix: update to add config matched other PR
1 parent 391a7ed commit 08f15c5

File tree

2 files changed

+129
-25
lines changed

2 files changed

+129
-25
lines changed

packages/auth-server/app.vue

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,7 @@
55
</template>
66

77
<script lang="ts" setup>
8-
import { createAppKit } from "@reown/appkit/vue";
9-
10-
const { defaultChain } = useClientStore();
11-
const { metadata, projectId, wagmiAdapter } = useAppKit();
12-
13-
createAppKit({
14-
adapters: [wagmiAdapter],
15-
networks: [defaultChain],
16-
projectId,
17-
metadata,
18-
});
19-
20-
// BigInt polyfill
8+
// BigInt polyfill for JSON serialization
219
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2210
(BigInt.prototype as any).toJSON = function () {
2311
return this.toString();
Lines changed: 128 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,144 @@
1+
import { createAppKit } from "@reown/appkit/vue";
12
import { WagmiAdapter } from "@reown/appkit-adapter-wagmi";
23

4+
// Singleton initialization state
5+
let initializationPromise: Promise<void> | null = null;
6+
let wagmiAdapterInstance: WagmiAdapter | null = null;
7+
8+
/**
9+
* Creates metadata configuration for AppKit.
10+
*/
11+
function createMetadata(origin: string) {
12+
return {
13+
name: "ZKsync SSO Auth Server",
14+
description: "ZKsync SSO Auth Server",
15+
url: origin,
16+
icons: [`${origin}/icon-512.png`],
17+
};
18+
}
19+
20+
/**
21+
* Creates plain chain object to avoid Viem Proxy serialization issues.
22+
*/
23+
function createPlainChain(defaultChain: ReturnType<typeof useClientStore>["defaultChain"]) {
24+
return {
25+
id: defaultChain.id,
26+
name: defaultChain.name,
27+
nativeCurrency: {
28+
name: defaultChain.nativeCurrency.name,
29+
symbol: defaultChain.nativeCurrency.symbol,
30+
decimals: defaultChain.nativeCurrency.decimals,
31+
},
32+
rpcUrls: {
33+
default: {
34+
http: [...defaultChain.rpcUrls.default.http],
35+
},
36+
},
37+
blockExplorers: defaultChain.blockExplorers
38+
? {
39+
default: {
40+
name: defaultChain.blockExplorers.default.name,
41+
url: defaultChain.blockExplorers.default.url,
42+
},
43+
}
44+
: undefined,
45+
};
46+
}
47+
48+
/**
49+
* Initializes AppKit with proper singleton pattern to prevent race conditions.
50+
* Uses Promise-based locking to ensure only one initialization occurs.
51+
*/
52+
async function initializeAppKit(
53+
projectId: string,
54+
metadata: ReturnType<typeof createMetadata>,
55+
plainChain: ReturnType<typeof createPlainChain>,
56+
) {
57+
if (initializationPromise) {
58+
// Another initialization is in progress, wait for it
59+
await initializationPromise;
60+
return;
61+
}
62+
63+
if (wagmiAdapterInstance) {
64+
// Already initialized
65+
return;
66+
}
67+
68+
// Create new initialization promise
69+
initializationPromise = (async () => {
70+
try {
71+
wagmiAdapterInstance = new WagmiAdapter({
72+
networks: [plainChain],
73+
projectId,
74+
});
75+
76+
createAppKit({
77+
adapters: [wagmiAdapterInstance],
78+
networks: [plainChain],
79+
projectId,
80+
metadata,
81+
});
82+
} catch (error) {
83+
console.warn("Failed to initialize AppKit:", error);
84+
wagmiAdapterInstance = null;
85+
throw error;
86+
} finally {
87+
initializationPromise = null;
88+
}
89+
})();
90+
91+
await initializationPromise;
92+
}
93+
94+
/**
95+
* Composable for accessing AppKit functionality.
96+
* Implements lazy initialization on first call to avoid SSR issues.
97+
*
98+
* IMPORTANT: Due to lazy 'fire-and-forget' initialization:
99+
* - wagmiConfig and wagmiAdapter will be null/undefined during SSR
100+
* - They will also be null/undefined on the first client-side call
101+
* - They will only be available after async initialization completes
102+
* - All consuming components MUST handle null values gracefully
103+
* - Components should reactively watch these values or check for null before use
104+
*/
3105
export const useAppKit = () => {
4106
const runtimeConfig = useRuntimeConfig();
5107
const { defaultChain } = useClientStore();
6108

7109
const projectId = runtimeConfig.public.appKitProjectId;
8-
const metadata = {
9-
name: "ZKsync SSO Auth Server",
10-
description: "ZKsync SSO Auth Server",
11-
url: window.location.origin,
12-
icons: [new URL("/icon-512.png", window.location.origin).toString()],
13-
};
110+
const origin
111+
= typeof window !== "undefined"
112+
? window.location.origin
113+
: runtimeConfig.public?.authServerOrigin
114+
?? process.env.NUXT_PUBLIC_AUTH_SERVER_ORIGIN
115+
?? "https://auth.zksync.dev";
14116

15-
const wagmiAdapter = new WagmiAdapter({
16-
networks: [defaultChain],
17-
projectId,
18-
});
117+
const metadata = createMetadata(origin);
118+
const plainChain = createPlainChain(defaultChain);
119+
120+
// Lazy initialization - only create AppKit when first used on client
121+
if (typeof window !== "undefined" && !wagmiAdapterInstance && !initializationPromise) {
122+
// Fire and forget - initialization happens asynchronously
123+
initializeAppKit(projectId, metadata, plainChain).catch((error) => {
124+
console.warn("AppKit initialization failed:", error);
125+
});
126+
}
19127

20-
const wagmiConfig = wagmiAdapter.wagmiConfig;
128+
const wagmiConfig = wagmiAdapterInstance?.wagmiConfig;
21129

22130
return {
23131
metadata,
24132
projectId,
25-
wagmiAdapter,
133+
wagmiAdapter: wagmiAdapterInstance,
26134
wagmiConfig,
27135
};
28136
};
137+
138+
// HMR cleanup - reset state on hot module replacement
139+
if (import.meta.hot) {
140+
import.meta.hot.dispose(() => {
141+
initializationPromise = null;
142+
wagmiAdapterInstance = null;
143+
});
144+
}

0 commit comments

Comments
 (0)