@worldcoin/minikit-js 2.0.0-dev.0 → 2.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/README.md +4 -1
- package/build/chunk-6SCI6OTQ.js +272 -0
- package/build/{chunk-LHHKY77D.js → chunk-IYL4VCWR.js} +7 -40
- package/build/chunk-QOFVDR5F.js +279 -0
- package/build/{chunk-OTAA7OOI.js → chunk-QOLIACKU.js} +30 -269
- package/build/{chunk-DIACPBCB.js → chunk-XHYUUG6Y.js} +248 -301
- package/build/command-exports.cjs +238 -297
- package/build/command-exports.d.cts +14 -11
- package/build/command-exports.d.ts +14 -11
- package/build/command-exports.js +10 -2
- package/build/connector/index.cjs +339 -122
- package/build/connector/index.js +9 -5
- package/build/index.cjs +250 -309
- package/build/index.d.cts +16 -7
- package/build/index.d.ts +16 -7
- package/build/index.js +6 -4
- package/build/minikit-provider.cjs +318 -125
- package/build/minikit-provider.js +9 -2040
- package/build/siwe-exports.cjs +6 -38
- package/build/siwe-exports.d.cts +1 -1
- package/build/siwe-exports.d.ts +1 -1
- package/build/siwe-exports.js +1 -1
- package/build/{types-DO2UGrgp.d.cts → types-CC2x79HX.d.ts} +125 -38
- package/build/{types-CKn5C-Ro.d.cts → types-CSyzFDPt.d.cts} +4 -1
- package/build/{types-CKn5C-Ro.d.ts → types-CSyzFDPt.d.ts} +4 -1
- package/build/{types-CGVVuGiN.d.ts → types-_jfLbcJW.d.cts} +125 -38
- package/package.json +12 -13
package/README.md
CHANGED
|
@@ -86,7 +86,10 @@ import {
|
|
|
86
86
|
SendTransactionErrorCodes,
|
|
87
87
|
} from '@worldcoin/minikit-js/commands';
|
|
88
88
|
import { getIsUserVerified } from '@worldcoin/minikit-js/address-book';
|
|
89
|
-
import {
|
|
89
|
+
import {
|
|
90
|
+
parseSiweMessage,
|
|
91
|
+
verifySiweMessage,
|
|
92
|
+
} from '@worldcoin/minikit-js/siwe';
|
|
90
93
|
```
|
|
91
94
|
|
|
92
95
|
You can still import `MiniKit` itself from the package root:
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setFallbackAdapter
|
|
3
|
+
} from "./chunk-XHYUUG6Y.js";
|
|
4
|
+
|
|
5
|
+
// src/commands/wagmi-fallback.ts
|
|
6
|
+
var SIWE_NONCE_REGEX = /^[a-zA-Z0-9]{8,}$/;
|
|
7
|
+
var WAGMI_KEY = "__minikit_wagmi_config__";
|
|
8
|
+
function setWagmiConfig(config) {
|
|
9
|
+
globalThis[WAGMI_KEY] = config;
|
|
10
|
+
registerWagmiFallbacks();
|
|
11
|
+
}
|
|
12
|
+
function getWagmiConfig() {
|
|
13
|
+
return globalThis[WAGMI_KEY];
|
|
14
|
+
}
|
|
15
|
+
function hasWagmiConfig() {
|
|
16
|
+
return globalThis[WAGMI_KEY] !== void 0;
|
|
17
|
+
}
|
|
18
|
+
function registerWagmiFallbacks() {
|
|
19
|
+
setFallbackAdapter({
|
|
20
|
+
walletAuth: wagmiWalletAuth,
|
|
21
|
+
signMessage: wagmiSignMessage,
|
|
22
|
+
signTypedData: wagmiSignTypedData,
|
|
23
|
+
sendTransaction: wagmiSendTransaction
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
async function loadWagmiActions() {
|
|
27
|
+
console.log("[MiniKit WagmiFallback] loadWagmiActions:start", {
|
|
28
|
+
hasWindow: typeof window !== "undefined",
|
|
29
|
+
hasWagmiConfig: hasWagmiConfig()
|
|
30
|
+
});
|
|
31
|
+
try {
|
|
32
|
+
const actions = await import(
|
|
33
|
+
/* webpackIgnore: true */
|
|
34
|
+
"wagmi/actions"
|
|
35
|
+
);
|
|
36
|
+
console.log("[MiniKit WagmiFallback] loadWagmiActions:success");
|
|
37
|
+
return actions;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log("[MiniKit WagmiFallback] loadWagmiActions:error", {
|
|
40
|
+
message: error instanceof Error ? error.message : String(error)
|
|
41
|
+
});
|
|
42
|
+
const wrappedError = new Error(
|
|
43
|
+
'Wagmi fallback requires the "wagmi" package. Install wagmi or provide a custom fallback.'
|
|
44
|
+
);
|
|
45
|
+
wrappedError.cause = error;
|
|
46
|
+
throw wrappedError;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function loadSiwe() {
|
|
50
|
+
try {
|
|
51
|
+
return await import(
|
|
52
|
+
/* webpackIgnore: true */
|
|
53
|
+
"siwe"
|
|
54
|
+
);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const wrappedError = new Error(
|
|
57
|
+
'Wagmi walletAuth fallback requires the "siwe" package. Install siwe or provide a custom fallback.'
|
|
58
|
+
);
|
|
59
|
+
wrappedError.cause = error;
|
|
60
|
+
throw wrappedError;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async function checksumAddress(addr) {
|
|
64
|
+
try {
|
|
65
|
+
const { getAddress } = await import(
|
|
66
|
+
/* webpackIgnore: true */
|
|
67
|
+
"viem"
|
|
68
|
+
);
|
|
69
|
+
return getAddress(addr);
|
|
70
|
+
} catch {
|
|
71
|
+
return addr;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async function ensureConnected(config) {
|
|
75
|
+
const { connect, getConnections } = await loadWagmiActions();
|
|
76
|
+
const isWorldApp = typeof window !== "undefined" && Boolean(window.WorldApp);
|
|
77
|
+
const existingConnection = getConnections(config).find(
|
|
78
|
+
(connection) => connection.accounts && connection.accounts.length > 0 && (isWorldApp || connection.connector?.id !== "worldApp")
|
|
79
|
+
);
|
|
80
|
+
if (existingConnection && existingConnection.accounts) {
|
|
81
|
+
return checksumAddress(existingConnection.accounts[0]);
|
|
82
|
+
}
|
|
83
|
+
const connectors = config.connectors;
|
|
84
|
+
if (!connectors || connectors.length === 0) {
|
|
85
|
+
throw new Error("No Wagmi connectors configured");
|
|
86
|
+
}
|
|
87
|
+
const candidateConnectors = isWorldApp ? connectors : connectors.filter(
|
|
88
|
+
(connector) => connector.id !== "worldApp"
|
|
89
|
+
);
|
|
90
|
+
if (!isWorldApp && candidateConnectors.length === 0) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"No web Wagmi connectors configured. Add a web connector (e.g. injected or walletConnect) after worldApp()."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const selectedConnector = candidateConnectors[0];
|
|
96
|
+
try {
|
|
97
|
+
const result = await connect(config, { connector: selectedConnector });
|
|
98
|
+
if (result.accounts.length > 0) {
|
|
99
|
+
const account = result.accounts[0];
|
|
100
|
+
const address = typeof account === "string" ? account : account.address;
|
|
101
|
+
if (address) {
|
|
102
|
+
return checksumAddress(address);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
const connectorId = selectedConnector.id ?? "unknown";
|
|
107
|
+
const wrappedError = new Error(
|
|
108
|
+
`Failed to connect with connector "${connectorId}". Reorder connectors to change the default connector.`
|
|
109
|
+
);
|
|
110
|
+
wrappedError.cause = error;
|
|
111
|
+
throw wrappedError;
|
|
112
|
+
}
|
|
113
|
+
throw new Error("Failed to connect wallet");
|
|
114
|
+
}
|
|
115
|
+
async function wagmiWalletAuth(params) {
|
|
116
|
+
console.log("[MiniKit WagmiFallback] walletAuth:start", {
|
|
117
|
+
hasWagmiConfig: hasWagmiConfig(),
|
|
118
|
+
nonceLength: params.nonce?.length ?? 0
|
|
119
|
+
});
|
|
120
|
+
const config = getWagmiConfig();
|
|
121
|
+
if (!config) {
|
|
122
|
+
console.log("[MiniKit WagmiFallback] walletAuth:error:no-config");
|
|
123
|
+
throw new Error(
|
|
124
|
+
"Wagmi config not available. Pass wagmiConfig to MiniKitProvider."
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const { signMessage } = await loadWagmiActions();
|
|
128
|
+
const { SiweMessage } = await loadSiwe();
|
|
129
|
+
const address = await ensureConnected(config);
|
|
130
|
+
if (!SIWE_NONCE_REGEX.test(params.nonce)) {
|
|
131
|
+
throw new Error(
|
|
132
|
+
"Invalid nonce: must be alphanumeric and at least 8 characters (EIP-4361)"
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
const siweMessage = new SiweMessage({
|
|
136
|
+
domain: typeof window !== "undefined" ? window.location.host : "localhost",
|
|
137
|
+
address,
|
|
138
|
+
statement: params.statement,
|
|
139
|
+
uri: typeof window !== "undefined" ? window.location.origin : "http://localhost",
|
|
140
|
+
version: "1",
|
|
141
|
+
chainId: 480,
|
|
142
|
+
// World Chain
|
|
143
|
+
nonce: params.nonce,
|
|
144
|
+
expirationTime: params.expirationTime?.toISOString()
|
|
145
|
+
});
|
|
146
|
+
const message = siweMessage.prepareMessage();
|
|
147
|
+
const signature = await signMessage(config, { message });
|
|
148
|
+
return {
|
|
149
|
+
address,
|
|
150
|
+
message,
|
|
151
|
+
signature
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
async function wagmiSignMessage(params) {
|
|
155
|
+
console.log("[MiniKit WagmiFallback] signMessage:start", {
|
|
156
|
+
hasWagmiConfig: hasWagmiConfig()
|
|
157
|
+
});
|
|
158
|
+
const config = getWagmiConfig();
|
|
159
|
+
if (!config) {
|
|
160
|
+
console.log("[MiniKit WagmiFallback] signMessage:error:no-config");
|
|
161
|
+
throw new Error(
|
|
162
|
+
"Wagmi config not available. Pass wagmiConfig to MiniKitProvider."
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const { signMessage } = await loadWagmiActions();
|
|
166
|
+
const address = await ensureConnected(config);
|
|
167
|
+
const signature = await signMessage(config, {
|
|
168
|
+
account: address,
|
|
169
|
+
message: params.message
|
|
170
|
+
});
|
|
171
|
+
return {
|
|
172
|
+
status: "success",
|
|
173
|
+
version: 1,
|
|
174
|
+
signature,
|
|
175
|
+
address
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function wagmiSignTypedData(params) {
|
|
179
|
+
console.log("[MiniKit WagmiFallback] signTypedData:start", {
|
|
180
|
+
hasWagmiConfig: hasWagmiConfig(),
|
|
181
|
+
hasChainId: params.chainId !== void 0
|
|
182
|
+
});
|
|
183
|
+
const config = getWagmiConfig();
|
|
184
|
+
if (!config) {
|
|
185
|
+
console.log("[MiniKit WagmiFallback] signTypedData:error:no-config");
|
|
186
|
+
throw new Error(
|
|
187
|
+
"Wagmi config not available. Pass wagmiConfig to MiniKitProvider."
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
const { getChainId, signTypedData, switchChain } = await loadWagmiActions();
|
|
191
|
+
const address = await ensureConnected(config);
|
|
192
|
+
if (params.chainId !== void 0) {
|
|
193
|
+
const currentChainId = await getChainId(config);
|
|
194
|
+
if (currentChainId !== params.chainId) {
|
|
195
|
+
await switchChain(config, { chainId: params.chainId });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
const signature = await signTypedData(config, {
|
|
199
|
+
account: address,
|
|
200
|
+
types: params.types,
|
|
201
|
+
primaryType: params.primaryType,
|
|
202
|
+
domain: params.domain,
|
|
203
|
+
message: params.message
|
|
204
|
+
});
|
|
205
|
+
return {
|
|
206
|
+
status: "success",
|
|
207
|
+
version: 1,
|
|
208
|
+
signature,
|
|
209
|
+
address
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function isChainMismatchError(error) {
|
|
213
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
214
|
+
return message.includes("does not match the target chain");
|
|
215
|
+
}
|
|
216
|
+
async function wagmiSendTransaction(params) {
|
|
217
|
+
console.log("[MiniKit WagmiFallback] sendTransaction:start", {
|
|
218
|
+
hasWagmiConfig: hasWagmiConfig(),
|
|
219
|
+
chainId: params.chainId,
|
|
220
|
+
hasData: Boolean(params.transaction.data)
|
|
221
|
+
});
|
|
222
|
+
const config = getWagmiConfig();
|
|
223
|
+
if (!config) {
|
|
224
|
+
console.log("[MiniKit WagmiFallback] sendTransaction:error:no-config");
|
|
225
|
+
throw new Error(
|
|
226
|
+
"Wagmi config not available. Pass wagmiConfig to MiniKitProvider."
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const { getChainId, getWalletClient, sendTransaction, switchChain } = await loadWagmiActions();
|
|
230
|
+
await ensureConnected(config);
|
|
231
|
+
const targetChainId = params.chainId ?? config.chains?.[0]?.id;
|
|
232
|
+
const ensureTargetChain = async () => {
|
|
233
|
+
if (targetChainId === void 0) return;
|
|
234
|
+
const currentChainId = await getChainId(config);
|
|
235
|
+
if (currentChainId !== targetChainId) {
|
|
236
|
+
await switchChain(config, { chainId: targetChainId });
|
|
237
|
+
}
|
|
238
|
+
const walletClient = await getWalletClient(config);
|
|
239
|
+
const providerChainId = walletClient ? await walletClient.getChainId() : await getChainId(config);
|
|
240
|
+
if (providerChainId !== targetChainId) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`Wallet network mismatch: expected chain ${targetChainId}, got ${providerChainId}. Please switch networks in your wallet and retry.`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
await ensureTargetChain();
|
|
247
|
+
let transactionHash;
|
|
248
|
+
try {
|
|
249
|
+
transactionHash = await sendTransaction(config, {
|
|
250
|
+
chainId: targetChainId,
|
|
251
|
+
to: params.transaction.address,
|
|
252
|
+
data: params.transaction.data,
|
|
253
|
+
value: params.transaction.value ? BigInt(params.transaction.value) : void 0
|
|
254
|
+
});
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if (targetChainId === void 0 || !isChainMismatchError(error)) {
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
await ensureTargetChain();
|
|
260
|
+
transactionHash = await sendTransaction(config, {
|
|
261
|
+
chainId: targetChainId,
|
|
262
|
+
to: params.transaction.address,
|
|
263
|
+
data: params.transaction.data,
|
|
264
|
+
value: params.transaction.value ? BigInt(params.transaction.value) : void 0
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return { transactionHash };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export {
|
|
271
|
+
setWagmiConfig
|
|
272
|
+
};
|
|
@@ -3,8 +3,7 @@ import {
|
|
|
3
3
|
createPublicClient,
|
|
4
4
|
getContract,
|
|
5
5
|
hashMessage,
|
|
6
|
-
http
|
|
7
|
-
recoverAddress
|
|
6
|
+
http
|
|
8
7
|
} from "viem";
|
|
9
8
|
import { worldchain } from "viem/chains";
|
|
10
9
|
var PREAMBLE = " wants you to sign in with your Ethereum account:";
|
|
@@ -16,7 +15,6 @@ var IAT_TAG = "Issued At: ";
|
|
|
16
15
|
var EXP_TAG = "Expiration Time: ";
|
|
17
16
|
var NBF_TAG = "Not Before: ";
|
|
18
17
|
var RID_TAG = "Request ID: ";
|
|
19
|
-
var ERC_191_PREFIX = "Ethereum Signed Message:\n";
|
|
20
18
|
var EIP1271_MAGICVALUE = "0x1626ba7e";
|
|
21
19
|
var SAFE_CONTRACT_ABI = [
|
|
22
20
|
{
|
|
@@ -160,14 +158,8 @@ var generateSiweMessage = (siweMessageData) => {
|
|
|
160
158
|
return siweMessage;
|
|
161
159
|
};
|
|
162
160
|
var verifySiweMessage = (payload, nonce, statement, requestId, userProvider) => {
|
|
163
|
-
if (payload.version
|
|
164
|
-
|
|
165
|
-
payload,
|
|
166
|
-
nonce,
|
|
167
|
-
statement,
|
|
168
|
-
requestId,
|
|
169
|
-
userProvider
|
|
170
|
-
);
|
|
161
|
+
if (payload.version !== 2) {
|
|
162
|
+
throw new Error("Unsupported version returned");
|
|
171
163
|
} else {
|
|
172
164
|
return verifySiweMessageV2(
|
|
173
165
|
payload,
|
|
@@ -208,39 +200,14 @@ var validateMessage = (siweMessageData, nonce, statement, requestId) => {
|
|
|
208
200
|
}
|
|
209
201
|
return true;
|
|
210
202
|
};
|
|
211
|
-
var verifySiweMessageV1 = async (payload, nonce, statement, requestId, userProvider) => {
|
|
212
|
-
if (typeof window !== "undefined") {
|
|
213
|
-
throw new Error("Wallet auth payload can only be verified in the backend");
|
|
214
|
-
}
|
|
215
|
-
const { message, signature, address } = payload;
|
|
216
|
-
const siweMessageData = parseSiweMessage(message);
|
|
217
|
-
validateMessage(siweMessageData, nonce, statement, requestId);
|
|
218
|
-
let provider = userProvider || createPublicClient({ chain: worldchain, transport: http() });
|
|
219
|
-
const signedMessage = `${ERC_191_PREFIX}${message.length}${message}`;
|
|
220
|
-
const hashedMessage = hashMessage(signedMessage);
|
|
221
|
-
const contract = getContract({
|
|
222
|
-
address,
|
|
223
|
-
abi: SAFE_CONTRACT_ABI,
|
|
224
|
-
client: provider
|
|
225
|
-
});
|
|
226
|
-
try {
|
|
227
|
-
const recoveredAddress = await recoverAddress({
|
|
228
|
-
hash: hashedMessage,
|
|
229
|
-
signature: `0x${signature}`
|
|
230
|
-
});
|
|
231
|
-
const isOwner = await contract.read.isOwner([recoveredAddress]);
|
|
232
|
-
if (!isOwner) {
|
|
233
|
-
throw new Error("Signature verification failed, invalid owner");
|
|
234
|
-
}
|
|
235
|
-
} catch (error) {
|
|
236
|
-
throw new Error("Signature verification failed");
|
|
237
|
-
}
|
|
238
|
-
return { isValid: true, siweMessageData };
|
|
239
|
-
};
|
|
240
203
|
var verifySiweMessageV2 = async (payload, nonce, statement, requestId, userProvider) => {
|
|
241
204
|
if (typeof window !== "undefined") {
|
|
242
205
|
throw new Error("Wallet auth payload can only be verified in the backend");
|
|
243
206
|
}
|
|
207
|
+
const NONCE_REGEX = /^[a-zA-Z0-9]+$/;
|
|
208
|
+
if (!NONCE_REGEX.test(nonce)) {
|
|
209
|
+
throw new Error("Invalid nonce: must be alphanumeric only (per ERC-4361)");
|
|
210
|
+
}
|
|
244
211
|
const { message, signature, address } = payload;
|
|
245
212
|
const siweMessageData = parseSiweMessage(message);
|
|
246
213
|
if (!validateMessage(siweMessageData, nonce, statement, requestId)) {
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MiniKit
|
|
3
|
+
} from "./chunk-QOLIACKU.js";
|
|
4
|
+
|
|
5
|
+
// src/provider.ts
|
|
6
|
+
import { getAddress } from "viem";
|
|
7
|
+
function _getAddress() {
|
|
8
|
+
if (typeof window === "undefined") return void 0;
|
|
9
|
+
return window.__worldapp_eip1193_address__;
|
|
10
|
+
}
|
|
11
|
+
function _setAddress(addr) {
|
|
12
|
+
if (typeof window === "undefined") return;
|
|
13
|
+
try {
|
|
14
|
+
window.__worldapp_eip1193_address__ = getAddress(addr);
|
|
15
|
+
} catch {
|
|
16
|
+
window.__worldapp_eip1193_address__ = addr;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function _clearAddress() {
|
|
20
|
+
if (typeof window === "undefined") return;
|
|
21
|
+
window.__worldapp_eip1193_address__ = void 0;
|
|
22
|
+
}
|
|
23
|
+
function rpcError(code, message) {
|
|
24
|
+
return Object.assign(new Error(message), { code });
|
|
25
|
+
}
|
|
26
|
+
function isHexString(value) {
|
|
27
|
+
return /^0x[0-9a-fA-F]*$/.test(value);
|
|
28
|
+
}
|
|
29
|
+
function isAddressString(value) {
|
|
30
|
+
return /^0x[0-9a-fA-F]{40}$/.test(value);
|
|
31
|
+
}
|
|
32
|
+
function decodeHexToUtf8(hex) {
|
|
33
|
+
const raw = hex.slice(2);
|
|
34
|
+
if (raw.length % 2 !== 0) {
|
|
35
|
+
throw new Error("Invalid hex string length");
|
|
36
|
+
}
|
|
37
|
+
const bytes = new Uint8Array(raw.length / 2);
|
|
38
|
+
for (let i = 0; i < raw.length; i += 2) {
|
|
39
|
+
bytes[i / 2] = parseInt(raw.slice(i, i + 2), 16);
|
|
40
|
+
}
|
|
41
|
+
return new TextDecoder().decode(bytes);
|
|
42
|
+
}
|
|
43
|
+
function asArrayParams(params) {
|
|
44
|
+
if (params === void 0) return [];
|
|
45
|
+
return Array.isArray(params) ? params : [params];
|
|
46
|
+
}
|
|
47
|
+
function decodeMaybeHexMessage(value) {
|
|
48
|
+
if (!isHexString(value)) {
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
return decodeHexToUtf8(value);
|
|
53
|
+
} catch {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function extractPersonalSignMessage(params) {
|
|
58
|
+
const items = asArrayParams(params);
|
|
59
|
+
if (items.length === 0) {
|
|
60
|
+
throw new Error("Missing personal_sign params");
|
|
61
|
+
}
|
|
62
|
+
const [first, second] = items;
|
|
63
|
+
const maybeMessage = typeof first === "string" && isAddressString(first) && typeof second === "string" ? second : first;
|
|
64
|
+
if (typeof maybeMessage !== "string") {
|
|
65
|
+
throw new Error("Invalid personal_sign message payload");
|
|
66
|
+
}
|
|
67
|
+
return decodeMaybeHexMessage(maybeMessage);
|
|
68
|
+
}
|
|
69
|
+
function extractEthSignMessage(params) {
|
|
70
|
+
const items = asArrayParams(params);
|
|
71
|
+
if (items.length === 0) {
|
|
72
|
+
throw new Error("Missing eth_sign params");
|
|
73
|
+
}
|
|
74
|
+
const [first, second] = items;
|
|
75
|
+
const maybeMessage = typeof second === "string" ? second : typeof first === "string" && !isAddressString(first) ? first : void 0;
|
|
76
|
+
if (typeof maybeMessage !== "string") {
|
|
77
|
+
throw new Error("Invalid eth_sign message payload");
|
|
78
|
+
}
|
|
79
|
+
return decodeMaybeHexMessage(maybeMessage);
|
|
80
|
+
}
|
|
81
|
+
function parseTypedDataInput(params) {
|
|
82
|
+
const items = asArrayParams(params);
|
|
83
|
+
const candidate = items.length > 1 ? items[1] : items[0];
|
|
84
|
+
if (!candidate) {
|
|
85
|
+
throw new Error("Missing typed data payload");
|
|
86
|
+
}
|
|
87
|
+
const parsed = typeof candidate === "string" ? JSON.parse(candidate) : candidate;
|
|
88
|
+
if (!parsed || typeof parsed !== "object" || typeof parsed.primaryType !== "string" || typeof parsed.message !== "object" || !parsed.message || typeof parsed.types !== "object" || !parsed.types) {
|
|
89
|
+
throw new Error("Invalid typed data payload");
|
|
90
|
+
}
|
|
91
|
+
const domainValue = parsed.domain;
|
|
92
|
+
const chainIdValue = domainValue?.chainId ?? parsed.chainId;
|
|
93
|
+
const parsedChainId = typeof chainIdValue === "string" ? Number(chainIdValue) : typeof chainIdValue === "number" ? chainIdValue : void 0;
|
|
94
|
+
return {
|
|
95
|
+
types: parsed.types,
|
|
96
|
+
primaryType: parsed.primaryType,
|
|
97
|
+
domain: domainValue,
|
|
98
|
+
message: parsed.message,
|
|
99
|
+
...Number.isFinite(parsedChainId) ? { chainId: parsedChainId } : {}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function normalizeRpcValue(value) {
|
|
103
|
+
if (value === void 0 || value === null) return void 0;
|
|
104
|
+
if (typeof value === "string") return value;
|
|
105
|
+
if (typeof value === "bigint") return `0x${value.toString(16)}`;
|
|
106
|
+
if (typeof value === "number") return `0x${value.toString(16)}`;
|
|
107
|
+
return String(value);
|
|
108
|
+
}
|
|
109
|
+
function extractTransactionParams(params) {
|
|
110
|
+
const items = asArrayParams(params);
|
|
111
|
+
const tx = items[0] ?? {};
|
|
112
|
+
if (typeof tx.to !== "string" || !isAddressString(tx.to)) {
|
|
113
|
+
throw new Error('Invalid transaction "to" address');
|
|
114
|
+
}
|
|
115
|
+
const chainId = typeof tx.chainId === "string" ? Number(tx.chainId) : typeof tx.chainId === "number" ? tx.chainId : void 0;
|
|
116
|
+
const normalizedValue = normalizeRpcValue(tx.value);
|
|
117
|
+
return {
|
|
118
|
+
to: tx.to,
|
|
119
|
+
...typeof tx.data === "string" ? { data: tx.data } : {},
|
|
120
|
+
...normalizedValue !== void 0 ? { value: normalizedValue } : {},
|
|
121
|
+
...Number.isFinite(chainId) ? { chainId } : {}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function extractSwitchChainId(params) {
|
|
125
|
+
const items = asArrayParams(params);
|
|
126
|
+
const payload = items[0] ?? {};
|
|
127
|
+
const rawChainId = payload.chainId;
|
|
128
|
+
const chainId = typeof rawChainId === "string" ? Number(rawChainId) : typeof rawChainId === "number" ? rawChainId : NaN;
|
|
129
|
+
if (!Number.isFinite(chainId)) {
|
|
130
|
+
throw new Error("Invalid chainId for wallet_switchEthereumChain");
|
|
131
|
+
}
|
|
132
|
+
return chainId;
|
|
133
|
+
}
|
|
134
|
+
function createProvider() {
|
|
135
|
+
const listeners = {};
|
|
136
|
+
function emit(event, ...args) {
|
|
137
|
+
listeners[event]?.forEach((fn) => fn(...args));
|
|
138
|
+
}
|
|
139
|
+
let authInFlight;
|
|
140
|
+
async function doAuth() {
|
|
141
|
+
if (!MiniKit.isInWorldApp() || !MiniKit.isInstalled()) {
|
|
142
|
+
throw rpcError(
|
|
143
|
+
4900,
|
|
144
|
+
"World App provider only works inside World App and must be installed"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const result = await MiniKit.walletAuth({
|
|
149
|
+
nonce: crypto.randomUUID().replace(/-/g, ""),
|
|
150
|
+
statement: "Sign in with World App"
|
|
151
|
+
});
|
|
152
|
+
_setAddress(result.data.address);
|
|
153
|
+
const addr = _getAddress();
|
|
154
|
+
emit("accountsChanged", [addr]);
|
|
155
|
+
return [addr];
|
|
156
|
+
} catch (e) {
|
|
157
|
+
throw rpcError(4001, `World App wallet auth failed: ${e.message}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
async request({ method, params }) {
|
|
162
|
+
switch (method) {
|
|
163
|
+
case "eth_requestAccounts": {
|
|
164
|
+
const existing = _getAddress();
|
|
165
|
+
if (existing) return [existing];
|
|
166
|
+
if (!authInFlight) {
|
|
167
|
+
authInFlight = doAuth().finally(() => {
|
|
168
|
+
authInFlight = void 0;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return authInFlight;
|
|
172
|
+
}
|
|
173
|
+
case "eth_accounts": {
|
|
174
|
+
const addr = _getAddress();
|
|
175
|
+
return addr ? [addr] : [];
|
|
176
|
+
}
|
|
177
|
+
case "eth_chainId":
|
|
178
|
+
return "0x1e0";
|
|
179
|
+
// 480 = World Chain
|
|
180
|
+
case "personal_sign": {
|
|
181
|
+
const message = extractPersonalSignMessage(params);
|
|
182
|
+
try {
|
|
183
|
+
const result = await MiniKit.signMessage({ message });
|
|
184
|
+
return result.data.signature;
|
|
185
|
+
} catch (e) {
|
|
186
|
+
throw rpcError(4001, `Sign message failed: ${e.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
case "eth_sign": {
|
|
190
|
+
const message = extractEthSignMessage(params);
|
|
191
|
+
try {
|
|
192
|
+
const result = await MiniKit.signMessage({ message });
|
|
193
|
+
return result.data.signature;
|
|
194
|
+
} catch (e) {
|
|
195
|
+
throw rpcError(4001, `Sign message failed: ${e.message}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
case "eth_signTypedData":
|
|
199
|
+
case "eth_signTypedData_v3":
|
|
200
|
+
case "eth_signTypedData_v4": {
|
|
201
|
+
try {
|
|
202
|
+
const typedData = parseTypedDataInput(params);
|
|
203
|
+
const result = await MiniKit.signTypedData({
|
|
204
|
+
types: typedData.types,
|
|
205
|
+
primaryType: typedData.primaryType,
|
|
206
|
+
domain: typedData.domain,
|
|
207
|
+
message: typedData.message,
|
|
208
|
+
chainId: typedData.chainId
|
|
209
|
+
});
|
|
210
|
+
if (result.data.status === "error") {
|
|
211
|
+
throw rpcError(
|
|
212
|
+
4001,
|
|
213
|
+
`Sign typed data failed: ${result.data.error_code}`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
return result.data.signature;
|
|
217
|
+
} catch (e) {
|
|
218
|
+
throw rpcError(4001, `Sign typed data failed: ${e.message}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
case "eth_sendTransaction": {
|
|
222
|
+
const tx = extractTransactionParams(params);
|
|
223
|
+
if (tx.chainId !== void 0 && tx.chainId !== 480) {
|
|
224
|
+
throw rpcError(4902, "World App only supports World Chain (480)");
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
const result = await MiniKit.sendTransaction({
|
|
228
|
+
chainId: tx.chainId ?? 480,
|
|
229
|
+
transactions: [
|
|
230
|
+
{
|
|
231
|
+
to: tx.to,
|
|
232
|
+
...tx.data && tx.data !== "0x" ? { data: tx.data } : {},
|
|
233
|
+
value: tx.value
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
});
|
|
237
|
+
return result.data.userOpHash;
|
|
238
|
+
} catch (e) {
|
|
239
|
+
throw rpcError(4001, `Send transaction failed: ${e.message}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
case "wallet_switchEthereumChain": {
|
|
243
|
+
const chainId = extractSwitchChainId(params);
|
|
244
|
+
if (chainId !== 480) {
|
|
245
|
+
throw rpcError(4902, "World App only supports World Chain (480)");
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
case "wallet_addEthereumChain": {
|
|
250
|
+
throw rpcError(4200, "World App only supports World Chain (480)");
|
|
251
|
+
}
|
|
252
|
+
default:
|
|
253
|
+
throw rpcError(4200, `Unsupported method: ${method}`);
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
on(event, fn) {
|
|
257
|
+
(listeners[event] ?? (listeners[event] = /* @__PURE__ */ new Set())).add(fn);
|
|
258
|
+
},
|
|
259
|
+
removeListener(event, fn) {
|
|
260
|
+
listeners[event]?.delete(fn);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function getWorldAppProvider() {
|
|
265
|
+
if (typeof window === "undefined") {
|
|
266
|
+
return createProvider();
|
|
267
|
+
}
|
|
268
|
+
if (!window.__worldapp_eip1193_provider__) {
|
|
269
|
+
window.__worldapp_eip1193_provider__ = createProvider();
|
|
270
|
+
}
|
|
271
|
+
return window.__worldapp_eip1193_provider__;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export {
|
|
275
|
+
_getAddress,
|
|
276
|
+
_setAddress,
|
|
277
|
+
_clearAddress,
|
|
278
|
+
getWorldAppProvider
|
|
279
|
+
};
|