@marigoldlabs/web3-tester 0.1.2 → 0.4.2
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 +26 -17
- package/LICENSE +21 -0
- package/README.md +480 -48
- package/dist/anvil.d.ts +90 -2
- package/dist/anvil.d.ts.map +1 -1
- package/dist/anvil.js +215 -13
- package/dist/anvil.js.map +1 -1
- package/dist/benchmark.d.ts +55 -0
- package/dist/benchmark.d.ts.map +1 -0
- package/dist/benchmark.js +168 -0
- package/dist/benchmark.js.map +1 -0
- package/dist/contracts/test-erc20.d.ts +227 -0
- package/dist/contracts/test-erc20.d.ts.map +1 -0
- package/dist/contracts/test-erc20.js +8 -0
- package/dist/contracts/test-erc20.js.map +1 -0
- package/dist/erc20.d.ts +38 -0
- package/dist/erc20.d.ts.map +1 -0
- package/dist/erc20.js +229 -0
- package/dist/erc20.js.map +1 -0
- package/dist/fixtures.d.ts +44 -2
- package/dist/fixtures.d.ts.map +1 -1
- package/dist/fixtures.js +162 -17
- package/dist/fixtures.js.map +1 -1
- package/dist/index.d.ts +26 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/injected-provider.d.ts.map +1 -1
- package/dist/injected-provider.js +863 -77
- package/dist/injected-provider.js.map +1 -1
- package/dist/live-fixtures.d.ts +32 -3
- package/dist/live-fixtures.d.ts.map +1 -1
- package/dist/live-fixtures.js +64 -27
- package/dist/live-fixtures.js.map +1 -1
- package/dist/matchers.d.ts +90 -0
- package/dist/matchers.d.ts.map +1 -0
- package/dist/matchers.js +268 -0
- package/dist/matchers.js.map +1 -0
- package/dist/metamask-extension.d.ts +34 -0
- package/dist/metamask-extension.d.ts.map +1 -0
- package/dist/metamask-extension.js +97 -0
- package/dist/metamask-extension.js.map +1 -0
- package/dist/mock-wallet-controller.d.ts +354 -4
- package/dist/mock-wallet-controller.d.ts.map +1 -1
- package/dist/mock-wallet-controller.js +1444 -58
- package/dist/mock-wallet-controller.js.map +1 -1
- package/dist/private-key-rpc-client.d.ts +1744 -2
- package/dist/private-key-rpc-client.d.ts.map +1 -1
- package/dist/private-key-rpc-client.js +245 -30
- package/dist/private-key-rpc-client.js.map +1 -1
- package/dist/real-wallet-cache.d.ts +103 -0
- package/dist/real-wallet-cache.d.ts.map +1 -0
- package/dist/real-wallet-cache.js +331 -0
- package/dist/real-wallet-cache.js.map +1 -0
- package/dist/real-wallet-extension-fixtures.d.ts +35 -0
- package/dist/real-wallet-extension-fixtures.d.ts.map +1 -0
- package/dist/real-wallet-extension-fixtures.js +96 -0
- package/dist/real-wallet-extension-fixtures.js.map +1 -0
- package/dist/real-wallet-extension.d.ts +86 -0
- package/dist/real-wallet-extension.d.ts.map +1 -0
- package/dist/real-wallet-extension.js +245 -0
- package/dist/real-wallet-extension.js.map +1 -0
- package/dist/real-wallet-fixtures.d.ts +52 -0
- package/dist/real-wallet-fixtures.d.ts.map +1 -0
- package/dist/real-wallet-fixtures.js +88 -0
- package/dist/real-wallet-fixtures.js.map +1 -0
- package/dist/real-wallet.d.ts +119 -19
- package/dist/real-wallet.d.ts.map +1 -1
- package/dist/real-wallet.js +1656 -144
- package/dist/real-wallet.js.map +1 -1
- package/dist/safe.d.ts +311 -0
- package/dist/safe.d.ts.map +1 -0
- package/dist/safe.js +743 -0
- package/dist/safe.js.map +1 -0
- package/dist/transactions.d.ts +118 -0
- package/dist/transactions.d.ts.map +1 -0
- package/dist/transactions.js +207 -0
- package/dist/transactions.js.map +1 -0
- package/dist/types.d.ts +36 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/wallet-personas.d.ts +99 -0
- package/dist/wallet-personas.d.ts.map +1 -0
- package/dist/wallet-personas.js +666 -0
- package/dist/wallet-personas.js.map +1 -0
- package/dist/walletconnect.d.ts +373 -0
- package/dist/walletconnect.d.ts.map +1 -0
- package/dist/walletconnect.js +799 -0
- package/dist/walletconnect.js.map +1 -0
- package/examples/live-sepolia.spec.ts +20 -1
- package/examples/playwright.config.ts +8 -0
- package/package.json +90 -8
- package/docs/API.md +0 -223
- package/docs/ARCHITECTURE.md +0 -81
- package/docs/CONSUMING_FROM_FJORD.md +0 -123
- package/docs/FJORD_LIVE_QA.md +0 -87
- package/docs/RELEASE_CHECKLIST.md +0 -55
|
@@ -5,35 +5,112 @@ export const emitterName = EMITTER_NAME;
|
|
|
5
5
|
export const buildInjectedProviderScript = (config) => `
|
|
6
6
|
(() => {
|
|
7
7
|
const config = ${JSON.stringify(config)};
|
|
8
|
-
const listeners = new Map();
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
// Origin-scoped wallets never install in out-of-scope frames, so blocked
|
|
10
|
+
// pages cannot even read the account address or chain off the provider.
|
|
11
|
+
// window.origin (unlike location.origin or the frame URL) is the security
|
|
12
|
+
// origin, which about:blank/srcdoc children inherit from their parent —
|
|
13
|
+
// matching the bridge-side ancestor walk.
|
|
14
|
+
if (config.allowedOrigins && !config.allowedOrigins.includes(window.origin || location.origin)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const toNetworkVersion = (chainId) => String(Number(BigInt(chainId)));
|
|
19
|
+
|
|
20
|
+
const walletError = (code, message) => {
|
|
21
|
+
const error = new Error(message);
|
|
22
|
+
error.code = code;
|
|
23
|
+
return error;
|
|
15
24
|
};
|
|
16
25
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
const createPublicKey = (value) => Object.freeze({
|
|
27
|
+
toString: () => value,
|
|
28
|
+
toBase58: () => value,
|
|
29
|
+
equals: (other) => {
|
|
30
|
+
const otherValue =
|
|
31
|
+
typeof other === 'string'
|
|
32
|
+
? other
|
|
33
|
+
: typeof other?.toBase58 === 'function'
|
|
34
|
+
? other.toBase58()
|
|
35
|
+
: typeof other?.toString === 'function'
|
|
36
|
+
? other.toString()
|
|
37
|
+
: '';
|
|
38
|
+
return otherValue === value;
|
|
39
|
+
},
|
|
40
|
+
toBytes: () => base58Decode(value),
|
|
41
|
+
toJSON: () => value,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const base58Decode = (value) => {
|
|
45
|
+
const alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
46
|
+
const bytes = [0];
|
|
47
|
+
for (const char of String(value)) {
|
|
48
|
+
const carryStart = alphabet.indexOf(char);
|
|
49
|
+
if (carryStart === -1) {
|
|
50
|
+
return new TextEncoder().encode(String(value)).slice(0, 32);
|
|
51
|
+
}
|
|
52
|
+
let carry = carryStart;
|
|
53
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
54
|
+
const next = bytes[index] * 58 + carry;
|
|
55
|
+
bytes[index] = next & 0xff;
|
|
56
|
+
carry = next >> 8;
|
|
57
|
+
}
|
|
58
|
+
while (carry > 0) {
|
|
59
|
+
bytes.push(carry & 0xff);
|
|
60
|
+
carry >>= 8;
|
|
61
|
+
}
|
|
20
62
|
}
|
|
63
|
+
for (const char of String(value)) {
|
|
64
|
+
if (char !== '1') break;
|
|
65
|
+
bytes.push(0);
|
|
66
|
+
}
|
|
67
|
+
return new Uint8Array(bytes.reverse());
|
|
68
|
+
};
|
|
21
69
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
70
|
+
const bytesFrom = (input) => {
|
|
71
|
+
if (input instanceof Uint8Array) {
|
|
72
|
+
return [...input];
|
|
73
|
+
}
|
|
74
|
+
if (input instanceof ArrayBuffer) {
|
|
75
|
+
return [...new Uint8Array(input)];
|
|
76
|
+
}
|
|
77
|
+
if (ArrayBuffer.isView(input)) {
|
|
78
|
+
return [...new Uint8Array(input.buffer, input.byteOffset, input.byteLength)];
|
|
79
|
+
}
|
|
80
|
+
if (Array.isArray(input)) {
|
|
81
|
+
return input.map((value) => Number(value) & 255);
|
|
25
82
|
}
|
|
83
|
+
if (typeof input === 'string') {
|
|
84
|
+
return [...new TextEncoder().encode(input)];
|
|
85
|
+
}
|
|
86
|
+
return [...new TextEncoder().encode(JSON.stringify(input ?? null))];
|
|
87
|
+
};
|
|
26
88
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
89
|
+
const deterministicSignature = (publicKey, payload) => {
|
|
90
|
+
const seed = [...new TextEncoder().encode(publicKey), ...bytesFrom(payload)];
|
|
91
|
+
const signature = new Uint8Array(64);
|
|
92
|
+
for (let index = 0; index < signature.length; index += 1) {
|
|
93
|
+
const a = seed[index % seed.length] ?? 0;
|
|
94
|
+
const b = seed[(index * 7 + 13) % seed.length] ?? 0;
|
|
95
|
+
signature[index] = (a + b + index * 17) & 255;
|
|
30
96
|
}
|
|
97
|
+
return signature;
|
|
98
|
+
};
|
|
31
99
|
|
|
32
|
-
|
|
33
|
-
|
|
100
|
+
const hexToBytes = (hex) => {
|
|
101
|
+
const value = String(hex).replace(/^0x/u, '');
|
|
102
|
+
const bytes = new Uint8Array(Math.floor(value.length / 2));
|
|
103
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
104
|
+
bytes[index] = Number.parseInt(value.slice(index * 2, index * 2 + 2), 16);
|
|
34
105
|
}
|
|
106
|
+
return bytes;
|
|
35
107
|
};
|
|
36
108
|
|
|
109
|
+
// EIP-1193 connectivity means "can the provider reach the chain", which is
|
|
110
|
+
// independent of account authorization. The controller's disconnect()
|
|
111
|
+
// simulates losing the chain via the 'disconnect' event.
|
|
112
|
+
let chainDisconnected = false;
|
|
113
|
+
|
|
37
114
|
const request = async (args) => {
|
|
38
115
|
if (!args || typeof args.method !== 'string') {
|
|
39
116
|
const error = new Error('Invalid EIP-1193 request');
|
|
@@ -56,78 +133,760 @@ export const buildInjectedProviderScript = (config) => `
|
|
|
56
133
|
return response.result;
|
|
57
134
|
};
|
|
58
135
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return provider;
|
|
86
|
-
},
|
|
87
|
-
once: (event, handler) => {
|
|
88
|
-
const wrapped = (payload) => {
|
|
89
|
-
provider.removeListener(event, wrapped);
|
|
90
|
-
handler(payload);
|
|
136
|
+
// One distinct provider object per announced wallet so selector tests can
|
|
137
|
+
// detect a dapp talking to the wrong provider. They share request handling
|
|
138
|
+
// and state, but each has its own identity and listener registry.
|
|
139
|
+
const createProviderEntry = (providerConfig) => {
|
|
140
|
+
const { evm = true, flags = {}, aliases = [], solana, ...info } = providerConfig;
|
|
141
|
+
const providerInfo = Object.freeze({ ...info });
|
|
142
|
+
const listeners = new Map();
|
|
143
|
+
|
|
144
|
+
const getListeners = (event) => {
|
|
145
|
+
if (!listeners.has(event)) {
|
|
146
|
+
listeners.set(event, new Set());
|
|
147
|
+
}
|
|
148
|
+
return listeners.get(event);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const responseForPayload = (payload, result) => ({
|
|
152
|
+
id: payload.id,
|
|
153
|
+
jsonrpc: payload.jsonrpc ?? '2.0',
|
|
154
|
+
result,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const errorResponseForPayload = (payload, error) => {
|
|
158
|
+
const rpcError = {
|
|
159
|
+
code: typeof error?.code === 'number' ? error.code : -32603,
|
|
160
|
+
message:
|
|
161
|
+
typeof error?.message === 'string' ? error.message : 'Internal JSON-RPC error',
|
|
91
162
|
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
},
|
|
95
|
-
removeListener: (event, handler) => {
|
|
96
|
-
listeners.get(event)?.delete(handler);
|
|
97
|
-
return provider;
|
|
98
|
-
},
|
|
99
|
-
removeAllListeners: (event) => {
|
|
100
|
-
if (event) {
|
|
101
|
-
listeners.delete(event);
|
|
102
|
-
} else {
|
|
103
|
-
listeners.clear();
|
|
163
|
+
if (error?.data !== undefined) {
|
|
164
|
+
rpcError.data = error.data;
|
|
104
165
|
}
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
166
|
+
return {
|
|
167
|
+
id: payload.id,
|
|
168
|
+
jsonrpc: payload.jsonrpc ?? '2.0',
|
|
169
|
+
error: rpcError,
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const requestBatch = (payloads) =>
|
|
174
|
+
Promise.all(payloads.map((payload) => request(payload)));
|
|
175
|
+
|
|
176
|
+
const requestBatchResponses = (payloads) =>
|
|
177
|
+
Promise.all(
|
|
178
|
+
payloads.map(async (payload) => {
|
|
179
|
+
try {
|
|
180
|
+
return responseForPayload(payload, await request(payload));
|
|
181
|
+
} catch (error) {
|
|
182
|
+
return errorResponseForPayload(payload, error);
|
|
183
|
+
}
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const provider = {
|
|
188
|
+
info: providerInfo,
|
|
189
|
+
selectedAddress:
|
|
190
|
+
config.connected && config.unlocked && config.accounts.length > 0 ? config.accounts[0] : null,
|
|
191
|
+
chainId: config.chainId,
|
|
192
|
+
networkVersion: toNetworkVersion(config.chainId),
|
|
193
|
+
request,
|
|
194
|
+
isConnected: () => !chainDisconnected,
|
|
195
|
+
enable: () => request({ method: 'eth_requestAccounts' }),
|
|
196
|
+
send: (methodOrPayload, paramsOrCallback) => {
|
|
197
|
+
if (typeof methodOrPayload === 'string') {
|
|
198
|
+
return request({ method: methodOrPayload, params: paramsOrCallback });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const payload = methodOrPayload;
|
|
202
|
+
const callback = paramsOrCallback;
|
|
203
|
+
if (typeof callback !== 'function') {
|
|
204
|
+
return Array.isArray(payload) ? requestBatch(payload) : request(payload);
|
|
205
|
+
}
|
|
206
|
+
const promise = Array.isArray(payload)
|
|
207
|
+
? requestBatchResponses(payload)
|
|
208
|
+
: request(payload).then((result) => responseForPayload(payload, result));
|
|
209
|
+
promise
|
|
210
|
+
.then((response) => callback?.(null, response))
|
|
211
|
+
.catch((error) => callback?.(error, null));
|
|
212
|
+
return undefined;
|
|
213
|
+
},
|
|
214
|
+
sendAsync: (payload, callback) => {
|
|
215
|
+
const promise = Array.isArray(payload)
|
|
216
|
+
? requestBatchResponses(payload)
|
|
217
|
+
: request(payload).then((result) => responseForPayload(payload, result));
|
|
218
|
+
promise
|
|
219
|
+
.then((response) => callback(null, response))
|
|
220
|
+
.catch((error) => callback(error, null));
|
|
221
|
+
},
|
|
222
|
+
on: (event, handler) => {
|
|
223
|
+
getListeners(event).add(handler);
|
|
224
|
+
if (event === 'connect' && config.connected && !chainDisconnected) {
|
|
225
|
+
setTimeout(() => {
|
|
226
|
+
if (listeners.get(event)?.has(handler)) {
|
|
227
|
+
handler({ chainId: provider.chainId });
|
|
228
|
+
}
|
|
229
|
+
}, 0);
|
|
230
|
+
}
|
|
231
|
+
return provider;
|
|
232
|
+
},
|
|
233
|
+
addListener: (event, handler) => provider.on(event, handler),
|
|
234
|
+
once: (event, handler) => {
|
|
235
|
+
const wrapped = (payload) => {
|
|
236
|
+
provider.removeListener(event, wrapped);
|
|
237
|
+
handler(payload);
|
|
238
|
+
};
|
|
239
|
+
provider.on(event, wrapped);
|
|
240
|
+
return provider;
|
|
241
|
+
},
|
|
242
|
+
removeListener: (event, handler) => {
|
|
243
|
+
listeners.get(event)?.delete(handler);
|
|
244
|
+
return provider;
|
|
245
|
+
},
|
|
246
|
+
off: (event, handler) => provider.removeListener(event, handler),
|
|
247
|
+
removeAllListeners: (event) => {
|
|
248
|
+
if (event) {
|
|
249
|
+
listeners.delete(event);
|
|
250
|
+
} else {
|
|
251
|
+
listeners.clear();
|
|
252
|
+
}
|
|
253
|
+
return provider;
|
|
254
|
+
},
|
|
255
|
+
listeners: (event) => [...(listeners.get(event) ?? [])],
|
|
256
|
+
listenerCount: (event) => listeners.get(event)?.size ?? 0,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
for (const [flag, value] of Object.entries(flags)) {
|
|
260
|
+
Object.defineProperty(provider, flag, {
|
|
261
|
+
value: Boolean(value),
|
|
262
|
+
configurable: true,
|
|
263
|
+
enumerable: true,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (flags.isMetaMask) {
|
|
268
|
+
Object.defineProperty(provider, '_metamask', {
|
|
269
|
+
value: {
|
|
270
|
+
isUnlocked: async () => {
|
|
271
|
+
try {
|
|
272
|
+
const state = await request({ method: 'metamask_getProviderState' });
|
|
273
|
+
return state.isUnlocked === true;
|
|
274
|
+
} catch {
|
|
275
|
+
return config.unlocked === true;
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
configurable: true,
|
|
280
|
+
enumerable: true,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return { info: providerInfo, provider, listeners, aliases, solana, evm: evm !== false };
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const createSolanaProvider = (entry) => {
|
|
288
|
+
const solanaConfig = entry.solana;
|
|
289
|
+
if (!solanaConfig) {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const listeners = new Map();
|
|
294
|
+
const standardListeners = new Map();
|
|
295
|
+
const publicKeyValue =
|
|
296
|
+
solanaConfig.publicKey ?? '26qv4GCcx98RihuK3c4T6ozB3J7L6VwCuFVc7Ta2A3Uo';
|
|
297
|
+
const publicKey = createPublicKey(publicKeyValue);
|
|
298
|
+
const solanaChains = Object.freeze(
|
|
299
|
+
solanaConfig.chains ?? ['solana:mainnet', 'solana:devnet', 'solana:testnet'],
|
|
300
|
+
);
|
|
301
|
+
const solanaAccountFeatures = Object.freeze([
|
|
302
|
+
'solana:signAndSendTransaction',
|
|
303
|
+
'solana:signIn',
|
|
304
|
+
'solana:signMessage',
|
|
305
|
+
'solana:signTransaction',
|
|
306
|
+
]);
|
|
307
|
+
const standardAccount = Object.freeze({
|
|
308
|
+
address: publicKeyValue,
|
|
309
|
+
publicKey: base58Decode(publicKeyValue),
|
|
310
|
+
chains: solanaChains,
|
|
311
|
+
features: solanaAccountFeatures,
|
|
312
|
+
label: entry.info.name,
|
|
313
|
+
icon: entry.info.icon,
|
|
314
|
+
});
|
|
315
|
+
let connected = false;
|
|
316
|
+
let trusted = false;
|
|
317
|
+
let hiddenByController = false;
|
|
318
|
+
|
|
319
|
+
const getListeners = (event) => {
|
|
320
|
+
if (!listeners.has(event)) {
|
|
321
|
+
listeners.set(event, new Set());
|
|
322
|
+
}
|
|
323
|
+
return listeners.get(event);
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const emitSolana = (event, payload) => {
|
|
327
|
+
const callbacks = listeners.get(event);
|
|
328
|
+
if (!callbacks) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
for (const callback of [...callbacks]) {
|
|
332
|
+
callback(payload);
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const emitStandardChange = (properties) => {
|
|
337
|
+
const callbacks = standardListeners.get('change');
|
|
338
|
+
if (!callbacks) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
for (const callback of [...callbacks]) {
|
|
342
|
+
callback(properties);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const standardOn = (event, handler) => {
|
|
347
|
+
if (!standardListeners.has(event)) {
|
|
348
|
+
standardListeners.set(event, new Set());
|
|
349
|
+
}
|
|
350
|
+
standardListeners.get(event).add(handler);
|
|
351
|
+
return () => standardListeners.get(event)?.delete(handler);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const ensureConnected = () => {
|
|
355
|
+
if (!connected) {
|
|
356
|
+
throw walletError(4100, 'The Solana wallet is not connected.');
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const visibleAccounts = () => (connected ? [publicKeyValue] : []);
|
|
361
|
+
const visibleAccountObjects = () =>
|
|
362
|
+
connected
|
|
363
|
+
? [
|
|
364
|
+
Object.freeze({
|
|
365
|
+
publicKey,
|
|
366
|
+
pubkey: publicKeyValue,
|
|
367
|
+
address: publicKeyValue,
|
|
368
|
+
}),
|
|
369
|
+
]
|
|
370
|
+
: [];
|
|
371
|
+
|
|
372
|
+
const ensureStandardAccount = (account) => {
|
|
373
|
+
if (account?.address && account.address !== publicKeyValue) {
|
|
374
|
+
throw walletError(4100, 'The Solana wallet cannot use the requested account.');
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const transactionsFromParams = (params) => {
|
|
379
|
+
if (Array.isArray(params)) {
|
|
380
|
+
return params;
|
|
381
|
+
}
|
|
382
|
+
if (Array.isArray(params?.transactions)) {
|
|
383
|
+
return params.transactions;
|
|
384
|
+
}
|
|
385
|
+
if (Array.isArray(params?.message)) {
|
|
386
|
+
return params.message;
|
|
387
|
+
}
|
|
388
|
+
if (Array.isArray(params?.[0])) {
|
|
389
|
+
return params[0];
|
|
390
|
+
}
|
|
391
|
+
return [];
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
const buildSignInMessage = (input = {}) => {
|
|
395
|
+
const address = input.address ?? publicKeyValue;
|
|
396
|
+
const domain = input.domain ?? window.location.host ?? 'localhost';
|
|
397
|
+
const statement = input.statement ?? 'Sign in with Solana.';
|
|
398
|
+
const lines = [
|
|
399
|
+
\`\${domain} wants you to sign in with your Solana account:\`,
|
|
400
|
+
address,
|
|
401
|
+
'',
|
|
402
|
+
statement,
|
|
403
|
+
];
|
|
404
|
+
const fields = [
|
|
405
|
+
['URI', input.uri],
|
|
406
|
+
['Version', input.version],
|
|
407
|
+
['Chain ID', input.chainId],
|
|
408
|
+
['Nonce', input.nonce],
|
|
409
|
+
['Issued At', input.issuedAt],
|
|
410
|
+
['Expiration Time', input.expirationTime],
|
|
411
|
+
['Not Before', input.notBefore],
|
|
412
|
+
['Request ID', input.requestId],
|
|
413
|
+
];
|
|
414
|
+
for (const [label, value] of fields) {
|
|
415
|
+
if (value !== undefined) {
|
|
416
|
+
lines.push(\`\${label}: \${value}\`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (Array.isArray(input.resources) && input.resources.length > 0) {
|
|
420
|
+
lines.push('Resources:');
|
|
421
|
+
for (const resource of input.resources) {
|
|
422
|
+
lines.push(\`- \${resource}\`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return new TextEncoder().encode(lines.join('\\n'));
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
const provider = {
|
|
429
|
+
info: entry.info,
|
|
430
|
+
publicKey: null,
|
|
431
|
+
isConnected: false,
|
|
432
|
+
connect: async (options = {}) => {
|
|
433
|
+
if (options?.onlyIfTrusted && !trusted) {
|
|
434
|
+
throw walletError(4001, 'User rejected the request.');
|
|
435
|
+
}
|
|
436
|
+
if (!trusted) {
|
|
437
|
+
await request({
|
|
438
|
+
method: 'solana_requestAccounts',
|
|
439
|
+
params: { publicKey: publicKeyValue },
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const wasConnected = connected;
|
|
443
|
+
connected = true;
|
|
444
|
+
trusted = true;
|
|
445
|
+
provider.isConnected = true;
|
|
446
|
+
provider.publicKey = publicKey;
|
|
447
|
+
if (!wasConnected) {
|
|
448
|
+
emitSolana('connect', publicKey);
|
|
449
|
+
emitStandardChange({ accounts: standardWallet.accounts });
|
|
450
|
+
}
|
|
451
|
+
return { publicKey };
|
|
452
|
+
},
|
|
453
|
+
disconnect: async () => {
|
|
454
|
+
const wasConnected = connected;
|
|
455
|
+
connected = false;
|
|
456
|
+
hiddenByController = false;
|
|
457
|
+
provider.isConnected = false;
|
|
458
|
+
provider.publicKey = null;
|
|
459
|
+
if (wasConnected) {
|
|
460
|
+
emitSolana('disconnect');
|
|
461
|
+
emitStandardChange({ accounts: standardWallet.accounts });
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
request: async ({ method, params } = {}) => {
|
|
465
|
+
switch (method) {
|
|
466
|
+
case 'connect':
|
|
467
|
+
return provider.connect(params ?? {});
|
|
468
|
+
case 'disconnect':
|
|
469
|
+
await provider.disconnect();
|
|
470
|
+
return null;
|
|
471
|
+
case 'getAccounts':
|
|
472
|
+
return visibleAccounts();
|
|
473
|
+
case 'requestAccounts':
|
|
474
|
+
await provider.connect(params ?? {});
|
|
475
|
+
return visibleAccounts();
|
|
476
|
+
case 'solana_getAccounts':
|
|
477
|
+
return visibleAccountObjects();
|
|
478
|
+
case 'solana_requestAccounts':
|
|
479
|
+
await provider.connect(params ?? {});
|
|
480
|
+
return visibleAccountObjects();
|
|
481
|
+
case 'signMessage':
|
|
482
|
+
case 'solana_signMessage':
|
|
483
|
+
return provider.signMessage(params?.message ?? params?.[0] ?? new Uint8Array());
|
|
484
|
+
case 'signTransaction':
|
|
485
|
+
case 'solana_signTransaction':
|
|
486
|
+
return provider.signTransaction(params?.transaction ?? params?.message ?? params?.[0]);
|
|
487
|
+
case 'signAllTransactions':
|
|
488
|
+
case 'solana_signAllTransactions':
|
|
489
|
+
return provider.signAllTransactions(transactionsFromParams(params));
|
|
490
|
+
case 'signAndSendTransaction':
|
|
491
|
+
case 'solana_signAndSendTransaction':
|
|
492
|
+
return provider.signAndSendTransaction(params?.transaction ?? params?.message ?? params?.[0]);
|
|
493
|
+
case 'signAndSendAllTransactions':
|
|
494
|
+
case 'solana_signAndSendAllTransactions':
|
|
495
|
+
return provider.signAndSendAllTransactions(transactionsFromParams(params));
|
|
496
|
+
case 'signIn':
|
|
497
|
+
case 'solana_signIn':
|
|
498
|
+
return provider.signIn(params ?? {});
|
|
499
|
+
default:
|
|
500
|
+
throw walletError(4200, \`The Solana wallet does not support the method "\${String(method)}".\`);
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
signIn: async (input = {}) => {
|
|
504
|
+
if (!connected) {
|
|
505
|
+
await provider.connect();
|
|
506
|
+
}
|
|
507
|
+
const signedMessage = buildSignInMessage(input);
|
|
508
|
+
await request({
|
|
509
|
+
method: 'solana_signIn',
|
|
510
|
+
params: { input, publicKey: publicKeyValue, message: [...signedMessage] },
|
|
511
|
+
});
|
|
512
|
+
return {
|
|
513
|
+
address: input.address ?? publicKeyValue,
|
|
514
|
+
publicKey,
|
|
515
|
+
signedMessage,
|
|
516
|
+
signature: deterministicSignature(publicKeyValue, signedMessage),
|
|
517
|
+
};
|
|
518
|
+
},
|
|
519
|
+
signMessage: async (message) => {
|
|
520
|
+
ensureConnected();
|
|
521
|
+
await request({
|
|
522
|
+
method: 'solana_signMessage',
|
|
523
|
+
params: { message, pubkey: publicKeyValue },
|
|
524
|
+
});
|
|
525
|
+
return {
|
|
526
|
+
publicKey,
|
|
527
|
+
signature: deterministicSignature(publicKeyValue, message),
|
|
528
|
+
};
|
|
529
|
+
},
|
|
530
|
+
signTransaction: async (transaction) => {
|
|
531
|
+
ensureConnected();
|
|
532
|
+
await request({
|
|
533
|
+
method: 'solana_signTransaction',
|
|
534
|
+
params: { transaction, pubkey: publicKeyValue },
|
|
535
|
+
});
|
|
536
|
+
return transaction;
|
|
537
|
+
},
|
|
538
|
+
signAllTransactions: async (transactions) => {
|
|
539
|
+
ensureConnected();
|
|
540
|
+
await request({
|
|
541
|
+
method: 'solana_signAllTransactions',
|
|
542
|
+
params: { transactions, pubkey: publicKeyValue },
|
|
543
|
+
});
|
|
544
|
+
return transactions;
|
|
545
|
+
},
|
|
546
|
+
signAndSendTransaction: async (transaction) => {
|
|
547
|
+
ensureConnected();
|
|
548
|
+
await request({
|
|
549
|
+
method: 'solana_signAndSendTransaction',
|
|
550
|
+
params: { transaction, pubkey: publicKeyValue },
|
|
551
|
+
});
|
|
552
|
+
const signature = deterministicSignature(publicKeyValue, transaction);
|
|
553
|
+
return {
|
|
554
|
+
signature: Array.from(signature)
|
|
555
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
556
|
+
.join(''),
|
|
557
|
+
};
|
|
558
|
+
},
|
|
559
|
+
signAndSendAllTransactions: async (transactions) => {
|
|
560
|
+
ensureConnected();
|
|
561
|
+
const list = Array.isArray(transactions) ? transactions : [];
|
|
562
|
+
await request({
|
|
563
|
+
method: 'solana_signAndSendAllTransactions',
|
|
564
|
+
params: { transactions: list, pubkey: publicKeyValue },
|
|
565
|
+
});
|
|
566
|
+
return {
|
|
567
|
+
publicKey,
|
|
568
|
+
signatures: list.map((transaction) =>
|
|
569
|
+
Array.from(deterministicSignature(publicKeyValue, transaction))
|
|
570
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
571
|
+
.join(''),
|
|
572
|
+
),
|
|
573
|
+
};
|
|
574
|
+
},
|
|
575
|
+
on: (event, handler) => {
|
|
576
|
+
getListeners(event).add(handler);
|
|
577
|
+
return provider;
|
|
578
|
+
},
|
|
579
|
+
addListener: (event, handler) => provider.on(event, handler),
|
|
580
|
+
once: (event, handler) => {
|
|
581
|
+
const wrapped = (payload) => {
|
|
582
|
+
provider.removeListener(event, wrapped);
|
|
583
|
+
handler(payload);
|
|
584
|
+
};
|
|
585
|
+
provider.on(event, wrapped);
|
|
586
|
+
return provider;
|
|
587
|
+
},
|
|
588
|
+
removeListener: (event, handler) => {
|
|
589
|
+
listeners.get(event)?.delete(handler);
|
|
590
|
+
return provider;
|
|
591
|
+
},
|
|
592
|
+
off: (event, handler) => provider.removeListener(event, handler),
|
|
593
|
+
removeAllListeners: (event) => {
|
|
594
|
+
if (event) {
|
|
595
|
+
listeners.delete(event);
|
|
596
|
+
} else {
|
|
597
|
+
listeners.clear();
|
|
598
|
+
}
|
|
599
|
+
return provider;
|
|
600
|
+
},
|
|
601
|
+
listeners: (event) => [...(listeners.get(event) ?? [])],
|
|
602
|
+
listenerCount: (event) => listeners.get(event)?.size ?? 0,
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
const hideFromController = () => {
|
|
606
|
+
if (!connected) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
connected = false;
|
|
611
|
+
hiddenByController = true;
|
|
612
|
+
provider.isConnected = false;
|
|
613
|
+
provider.publicKey = null;
|
|
614
|
+
emitSolana('accountChanged', null);
|
|
615
|
+
emitStandardChange({ accounts: standardWallet.accounts });
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
const restoreFromController = () => {
|
|
619
|
+
if (!hiddenByController) {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
hiddenByController = false;
|
|
624
|
+
connected = true;
|
|
625
|
+
trusted = true;
|
|
626
|
+
provider.isConnected = true;
|
|
627
|
+
provider.publicKey = publicKey;
|
|
628
|
+
emitSolana('accountChanged', publicKey);
|
|
629
|
+
emitStandardChange({ accounts: standardWallet.accounts });
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
const disconnectFromController = () => {
|
|
633
|
+
const wasVisible = connected;
|
|
634
|
+
const shouldEmitDisconnect = connected || hiddenByController;
|
|
635
|
+
connected = false;
|
|
636
|
+
hiddenByController = false;
|
|
637
|
+
provider.isConnected = false;
|
|
638
|
+
provider.publicKey = null;
|
|
639
|
+
if (wasVisible) {
|
|
640
|
+
emitSolana('accountChanged', null);
|
|
641
|
+
emitStandardChange({ accounts: standardWallet.accounts });
|
|
642
|
+
}
|
|
643
|
+
if (shouldEmitDisconnect) {
|
|
644
|
+
emitSolana('disconnect');
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
const handleControllerEvent = (event, payload) => {
|
|
649
|
+
if (event === 'accountsChanged') {
|
|
650
|
+
if (Array.isArray(payload) && payload.length > 0) {
|
|
651
|
+
restoreFromController();
|
|
652
|
+
} else {
|
|
653
|
+
hideFromController();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (event === 'disconnect') {
|
|
657
|
+
disconnectFromController();
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
const standardWallet = Object.freeze({
|
|
662
|
+
version: '1.0.0',
|
|
663
|
+
name: entry.info.name,
|
|
664
|
+
icon: entry.info.icon,
|
|
665
|
+
chains: solanaChains,
|
|
666
|
+
get accounts() {
|
|
667
|
+
return connected ? Object.freeze([standardAccount]) : Object.freeze([]);
|
|
668
|
+
},
|
|
669
|
+
features: Object.freeze({
|
|
670
|
+
'standard:connect': Object.freeze({
|
|
671
|
+
version: '1.0.0',
|
|
672
|
+
connect: async (input = {}) => {
|
|
673
|
+
await provider.connect({ onlyIfTrusted: input?.silent === true });
|
|
674
|
+
return { accounts: standardWallet.accounts };
|
|
675
|
+
},
|
|
676
|
+
}),
|
|
677
|
+
'standard:disconnect': Object.freeze({
|
|
678
|
+
version: '1.0.0',
|
|
679
|
+
disconnect: async () => {
|
|
680
|
+
await provider.disconnect();
|
|
681
|
+
},
|
|
682
|
+
}),
|
|
683
|
+
'standard:events': Object.freeze({
|
|
684
|
+
version: '1.0.0',
|
|
685
|
+
on: standardOn,
|
|
686
|
+
}),
|
|
687
|
+
'solana:signIn': Object.freeze({
|
|
688
|
+
version: '1.0.0',
|
|
689
|
+
signIn: async (...inputs) => {
|
|
690
|
+
const signInInputs = inputs.length > 0 ? inputs : [{}];
|
|
691
|
+
return Promise.all(signInInputs.map(async (input) => {
|
|
692
|
+
const result = await provider.signIn(input);
|
|
693
|
+
return {
|
|
694
|
+
account: standardAccount,
|
|
695
|
+
signedMessage: result.signedMessage,
|
|
696
|
+
signature: result.signature,
|
|
697
|
+
};
|
|
698
|
+
}));
|
|
699
|
+
},
|
|
700
|
+
}),
|
|
701
|
+
'solana:signMessage': Object.freeze({
|
|
702
|
+
version: '1.1.0',
|
|
703
|
+
signMessage: async (...inputs) => {
|
|
704
|
+
return Promise.all(inputs.map(async (input) => {
|
|
705
|
+
ensureStandardAccount(input.account);
|
|
706
|
+
const result = await provider.signMessage(input.message);
|
|
707
|
+
return {
|
|
708
|
+
signedMessage: input.message,
|
|
709
|
+
signature: result.signature,
|
|
710
|
+
};
|
|
711
|
+
}));
|
|
712
|
+
},
|
|
713
|
+
}),
|
|
714
|
+
'solana:signTransaction': Object.freeze({
|
|
715
|
+
version: '1.0.0',
|
|
716
|
+
supportedTransactionVersions: Object.freeze(['legacy', 0]),
|
|
717
|
+
signTransaction: async (...inputs) => {
|
|
718
|
+
return Promise.all(inputs.map(async (input) => {
|
|
719
|
+
ensureStandardAccount(input.account);
|
|
720
|
+
const signedTransaction = await provider.signTransaction(input.transaction);
|
|
721
|
+
return {
|
|
722
|
+
signedTransaction,
|
|
723
|
+
};
|
|
724
|
+
}));
|
|
725
|
+
},
|
|
726
|
+
}),
|
|
727
|
+
'solana:signAndSendTransaction': Object.freeze({
|
|
728
|
+
version: '1.0.0',
|
|
729
|
+
supportedTransactionVersions: Object.freeze(['legacy', 0]),
|
|
730
|
+
signAndSendTransaction: async (...inputs) => {
|
|
731
|
+
return Promise.all(inputs.map(async (input) => {
|
|
732
|
+
ensureStandardAccount(input.account);
|
|
733
|
+
const result = await provider.signAndSendTransaction(input.transaction);
|
|
734
|
+
return {
|
|
735
|
+
signature: hexToBytes(result.signature),
|
|
736
|
+
};
|
|
737
|
+
}));
|
|
738
|
+
},
|
|
739
|
+
}),
|
|
740
|
+
}),
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
for (const [flag, value] of Object.entries(solanaConfig.flags ?? {})) {
|
|
744
|
+
Object.defineProperty(provider, flag, {
|
|
745
|
+
value: Boolean(value),
|
|
746
|
+
configurable: true,
|
|
747
|
+
enumerable: true,
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return { provider, aliases: solanaConfig.aliases ?? [], standardWallet, handleControllerEvent };
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
const registerStandardWallet = (wallet) => {
|
|
755
|
+
const register = (api) => {
|
|
756
|
+
if (api && typeof api.register === 'function') {
|
|
757
|
+
api.register(wallet);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
window.addEventListener('wallet-standard:app-ready', (event) => register(event.detail));
|
|
761
|
+
window.dispatchEvent(
|
|
762
|
+
new CustomEvent('wallet-standard:register-wallet', {
|
|
763
|
+
detail: register,
|
|
764
|
+
}),
|
|
765
|
+
);
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
const entries = config.providers.map(createProviderEntry);
|
|
769
|
+
const evmEntries = entries.filter((entry) => entry.evm);
|
|
770
|
+
const solanaEntries = entries
|
|
771
|
+
.map((entry) => createSolanaProvider(entry))
|
|
772
|
+
.filter(Boolean);
|
|
773
|
+
if (evmEntries.length > 1) {
|
|
774
|
+
const providers = evmEntries.map((entry) => entry.provider);
|
|
775
|
+
for (const entry of evmEntries) {
|
|
776
|
+
Object.defineProperty(entry.provider, 'providers', {
|
|
777
|
+
value: providers,
|
|
778
|
+
configurable: true,
|
|
779
|
+
enumerable: true,
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const emit = (event, payload) => {
|
|
785
|
+
if (event === 'connect') {
|
|
786
|
+
chainDisconnected = false;
|
|
787
|
+
}
|
|
788
|
+
if (event === 'disconnect') {
|
|
789
|
+
chainDisconnected = true;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
for (const entry of evmEntries) {
|
|
793
|
+
if (event === 'accountsChanged') {
|
|
794
|
+
entry.provider.selectedAddress =
|
|
795
|
+
Array.isArray(payload) && payload.length > 0 ? payload[0] : null;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (event === 'chainChanged') {
|
|
799
|
+
entry.provider.chainId = payload;
|
|
800
|
+
entry.provider.networkVersion = toNetworkVersion(payload);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const callbacks = entry.listeners.get(event);
|
|
804
|
+
if (callbacks) {
|
|
805
|
+
for (const callback of [...callbacks]) {
|
|
806
|
+
callback(payload);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
if (event === 'chainChanged') {
|
|
811
|
+
const networkCallbacks = entry.listeners.get('networkChanged');
|
|
812
|
+
if (networkCallbacks) {
|
|
813
|
+
for (const callback of [...networkCallbacks]) {
|
|
814
|
+
callback(entry.provider.networkVersion);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
for (const entry of solanaEntries) {
|
|
821
|
+
entry.handleControllerEvent(event, payload);
|
|
822
|
+
}
|
|
110
823
|
};
|
|
111
824
|
|
|
112
825
|
const announceProviders = () => {
|
|
113
|
-
for (const
|
|
826
|
+
for (const entry of evmEntries) {
|
|
114
827
|
window.dispatchEvent(
|
|
115
828
|
new CustomEvent('eip6963:announceProvider', {
|
|
116
|
-
detail: {
|
|
117
|
-
info,
|
|
118
|
-
provider,
|
|
119
|
-
},
|
|
829
|
+
detail: Object.freeze({
|
|
830
|
+
info: Object.freeze({ ...entry.info }),
|
|
831
|
+
provider: entry.provider,
|
|
832
|
+
}),
|
|
120
833
|
}),
|
|
121
834
|
);
|
|
122
835
|
}
|
|
123
836
|
};
|
|
124
837
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
838
|
+
if (evmEntries[0]) {
|
|
839
|
+
Object.defineProperty(window, 'ethereum', {
|
|
840
|
+
value: evmEntries[0].provider,
|
|
841
|
+
configurable: true,
|
|
842
|
+
enumerable: true,
|
|
843
|
+
writable: true,
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
const assignProviderAlias = (path, provider) => {
|
|
848
|
+
const parts = String(path).split('.').filter(Boolean);
|
|
849
|
+
if (parts.length === 0 || (parts.length === 1 && parts[0] === 'ethereum')) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
let target = window;
|
|
854
|
+
for (const part of parts.slice(0, -1)) {
|
|
855
|
+
const current = target[part];
|
|
856
|
+
if (
|
|
857
|
+
current === null ||
|
|
858
|
+
(typeof current !== 'object' && typeof current !== 'function')
|
|
859
|
+
) {
|
|
860
|
+
Object.defineProperty(target, part, {
|
|
861
|
+
value: {},
|
|
862
|
+
configurable: true,
|
|
863
|
+
enumerable: true,
|
|
864
|
+
writable: true,
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
target = target[part];
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
Object.defineProperty(target, parts[parts.length - 1], {
|
|
871
|
+
value: provider,
|
|
872
|
+
configurable: true,
|
|
873
|
+
enumerable: true,
|
|
874
|
+
writable: true,
|
|
875
|
+
});
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
for (const entry of evmEntries) {
|
|
879
|
+
for (const alias of entry.aliases) {
|
|
880
|
+
assignProviderAlias(alias, entry.provider);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
for (const entry of solanaEntries) {
|
|
885
|
+
for (const alias of entry.aliases) {
|
|
886
|
+
assignProviderAlias(alias, entry.provider);
|
|
887
|
+
}
|
|
888
|
+
registerStandardWallet(entry.standardWallet);
|
|
889
|
+
}
|
|
131
890
|
|
|
132
891
|
Object.defineProperty(window, '${EMITTER_NAME}', {
|
|
133
892
|
value: emit,
|
|
@@ -136,6 +895,33 @@ export const buildInjectedProviderScript = (config) => `
|
|
|
136
895
|
|
|
137
896
|
window.addEventListener('eip6963:requestProvider', announceProviders);
|
|
138
897
|
queueMicrotask(announceProviders);
|
|
898
|
+
|
|
899
|
+
// The serialized config snapshot goes stale once the controller mutates
|
|
900
|
+
// accounts or chain and the page navigates; refresh the synchronous mirror
|
|
901
|
+
// properties from the wallet's current state as soon as the bridge is up.
|
|
902
|
+
const refresh = async () => {
|
|
903
|
+
try {
|
|
904
|
+
const response = await window.${BRIDGE_NAME}({
|
|
905
|
+
method: 'metamask_getProviderState',
|
|
906
|
+
params: [],
|
|
907
|
+
});
|
|
908
|
+
if (!response.ok) {
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const state = response.result;
|
|
913
|
+
for (const entry of evmEntries) {
|
|
914
|
+
entry.provider.selectedAddress =
|
|
915
|
+
state.isUnlocked && state.accounts.length > 0 ? state.accounts[0] : null;
|
|
916
|
+
entry.provider.chainId = state.chainId;
|
|
917
|
+
entry.provider.networkVersion = toNetworkVersion(state.chainId);
|
|
918
|
+
}
|
|
919
|
+
} catch {
|
|
920
|
+
// Bridge not available yet (e.g. detached frame) — sync values fall
|
|
921
|
+
// back to the injected config snapshot.
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
refresh();
|
|
139
925
|
})();
|
|
140
926
|
`;
|
|
141
927
|
//# sourceMappingURL=injected-provider.js.map
|