@marigoldlabs/web3-tester 0.1.1 → 0.4.1
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 +167 -41
- 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/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 +17 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/injected-provider.d.ts.map +1 -1
- package/dist/injected-provider.js +142 -79
- 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 +205 -3
- package/dist/mock-wallet-controller.d.ts.map +1 -1
- package/dist/mock-wallet-controller.js +843 -46
- package/dist/mock-wallet-controller.js.map +1 -1
- package/dist/private-key-rpc-client.d.ts +1730 -0
- package/dist/private-key-rpc-client.d.ts.map +1 -1
- package/dist/private-key-rpc-client.js +105 -12
- package/dist/private-key-rpc-client.js.map +1 -1
- package/dist/real-wallet-cache.d.ts +65 -0
- package/dist/real-wallet-cache.d.ts.map +1 -0
- package/dist/real-wallet-cache.js +245 -0
- package/dist/real-wallet-cache.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 +73 -0
- package/dist/real-wallet-fixtures.js.map +1 -0
- package/dist/real-wallet-setup.d.ts +4 -0
- package/dist/real-wallet-setup.d.ts.map +1 -0
- package/dist/real-wallet-setup.js +5 -0
- package/dist/real-wallet-setup.js.map +1 -0
- package/dist/real-wallet.d.ts +124 -15
- package/dist/real-wallet.d.ts.map +1 -1
- package/dist/real-wallet.js +1682 -138
- package/dist/real-wallet.js.map +1 -1
- 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 +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/walletconnect.d.ts +206 -0
- package/dist/walletconnect.d.ts.map +1 -0
- package/dist/walletconnect.js +359 -0
- package/dist/walletconnect.js.map +1 -0
- package/examples/live-sepolia.spec.ts +21 -2
- package/examples/local-wallet.spec.ts +1 -1
- package/package.json +62 -6
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-key-rpc-client.d.ts","sourceRoot":"","sources":["../src/private-key-rpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"private-key-rpc-client.d.ts","sourceRoot":"","sources":["../src/private-key-rpc-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +1,58 @@
|
|
|
1
|
-
import { createPublicClient, createWalletClient,
|
|
1
|
+
import { createPublicClient, createWalletClient, http, isAddress, } from 'viem';
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
3
|
import { sepolia } from 'viem/chains';
|
|
4
|
+
// Anvil/Hardhat chains are safe targets but viem does not mark them
|
|
5
|
+
// `testnet: true`, so they need their own allowlist entry.
|
|
6
|
+
const LOCAL_DEV_CHAIN_IDS = new Set([1337, 31337]);
|
|
7
|
+
const isTestChain = (chain) => chain.testnet === true || LOCAL_DEV_CHAIN_IDS.has(chain.id);
|
|
4
8
|
const normalizePrivateKey = (privateKey) => {
|
|
5
9
|
const trimmed = privateKey.trim();
|
|
6
10
|
return trimmed.startsWith('0x') ? trimmed : `0x${trimmed}`;
|
|
7
11
|
};
|
|
8
12
|
const asHex = (value) => typeof value === 'string' && value.startsWith('0x') ? value : undefined;
|
|
13
|
+
// Hex payloads must be signed as raw bytes: decoding to UTF-8 first corrupts
|
|
14
|
+
// binary messages (TextDecoder substitutes U+FFFD instead of throwing), and for
|
|
15
|
+
// valid UTF-8 text the EIP-191 digest over the raw bytes is identical anyway.
|
|
9
16
|
const normalizeMessage = (message) => {
|
|
10
17
|
const hex = asHex(message);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
return hex ? { raw: hex } : String(message ?? '');
|
|
19
|
+
};
|
|
20
|
+
const parseParams = (request) => Array.isArray(request.params) ? [...request.params] : [];
|
|
21
|
+
// Dapp-supplied authorization entries are RPC-shaped (hex chainId/nonce/
|
|
22
|
+
// yParity) while viem's sendTransaction expects numbers — a silent
|
|
23
|
+
// passthrough would RLP-encode garbage, so malformed entries fail loudly.
|
|
24
|
+
const parseAuthorizationList = (value) => {
|
|
25
|
+
if (value === undefined) {
|
|
26
|
+
return undefined;
|
|
16
27
|
}
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
if (!Array.isArray(value)) {
|
|
29
|
+
throw new Error('authorizationList must be an array.');
|
|
19
30
|
}
|
|
31
|
+
return value.map((entry, index) => {
|
|
32
|
+
const item = (entry ?? {});
|
|
33
|
+
const address = (item.address ?? item.contractAddress);
|
|
34
|
+
if (typeof address !== 'string' || !address.startsWith('0x')) {
|
|
35
|
+
throw new Error(`authorizationList[${index}] needs an address.`);
|
|
36
|
+
}
|
|
37
|
+
const toNumber = (raw, name) => {
|
|
38
|
+
if (typeof raw === 'number') {
|
|
39
|
+
return raw;
|
|
40
|
+
}
|
|
41
|
+
if (typeof raw === 'string' && raw.startsWith('0x')) {
|
|
42
|
+
return Number(BigInt(raw));
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`authorizationList[${index}].${name} must be a number or 0x-hex string.`);
|
|
45
|
+
};
|
|
46
|
+
return {
|
|
47
|
+
address: address,
|
|
48
|
+
chainId: toNumber(item.chainId, 'chainId'),
|
|
49
|
+
nonce: toNumber(item.nonce, 'nonce'),
|
|
50
|
+
r: item.r,
|
|
51
|
+
s: item.s,
|
|
52
|
+
yParity: toNumber(item.yParity ?? 0, 'yParity'),
|
|
53
|
+
};
|
|
54
|
+
});
|
|
20
55
|
};
|
|
21
|
-
const parseParams = (request) => Array.isArray(request.params) ? [...request.params] : [];
|
|
22
56
|
export class PrivateKeyRpcClient {
|
|
23
57
|
account;
|
|
24
58
|
chain;
|
|
@@ -26,8 +60,14 @@ export class PrivateKeyRpcClient {
|
|
|
26
60
|
sentTransactionRequests = [];
|
|
27
61
|
publicClient;
|
|
28
62
|
walletClient;
|
|
63
|
+
rpcChainVerified = false;
|
|
29
64
|
constructor(options) {
|
|
30
65
|
this.chain = options.chain ?? sepolia;
|
|
66
|
+
if (!options.allowMainnet && !isTestChain(this.chain)) {
|
|
67
|
+
throw new Error(`PrivateKeyRpcClient refuses chain "${this.chain.name}" (id ${this.chain.id}) because it is not marked as a testnet. ` +
|
|
68
|
+
'This client signs and broadcasts without confirmation prompts. Pass allowMainnet: true to target a production chain, ' +
|
|
69
|
+
'or use a chain definition with testnet: true.');
|
|
70
|
+
}
|
|
31
71
|
this.account = privateKeyToAccount(normalizePrivateKey(options.privateKey));
|
|
32
72
|
const transport = http(options.rpcUrl);
|
|
33
73
|
this.publicClient = createPublicClient({
|
|
@@ -43,11 +83,20 @@ export class PrivateKeyRpcClient {
|
|
|
43
83
|
async request(request) {
|
|
44
84
|
const params = parseParams(request);
|
|
45
85
|
switch (request.method) {
|
|
86
|
+
// Answered locally: the client holds exactly one key, and remote RPC
|
|
87
|
+
// nodes answer [] (a needless network roundtrip that would also blind
|
|
88
|
+
// MockWalletController's account validation in live mode).
|
|
89
|
+
case 'eth_accounts':
|
|
90
|
+
return [this.account.address];
|
|
46
91
|
case 'personal_sign': {
|
|
92
|
+
// Standard order is [message, address]; some legacy callers send
|
|
93
|
+
// [address, message]. When both params are addresses the request is
|
|
94
|
+
// ambiguous, so prefer the standard order and treat the first as the
|
|
95
|
+
// message.
|
|
47
96
|
const [first, second] = params;
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
97
|
+
const firstIsAddress = typeof first === 'string' && isAddress(first);
|
|
98
|
+
const secondIsAddress = typeof second === 'string' && isAddress(second);
|
|
99
|
+
const message = firstIsAddress && !secondIsAddress && second !== undefined ? second : first;
|
|
51
100
|
return this.walletClient.signMessage({
|
|
52
101
|
account: this.account,
|
|
53
102
|
message: normalizeMessage(message),
|
|
@@ -60,6 +109,9 @@ export class PrivateKeyRpcClient {
|
|
|
60
109
|
message: normalizeMessage(message),
|
|
61
110
|
});
|
|
62
111
|
}
|
|
112
|
+
// v3 payloads (no arrays or recursive structs) hash identically under
|
|
113
|
+
// v4 rules, so both versions share the same signing path.
|
|
114
|
+
case 'eth_signTypedData_v3':
|
|
63
115
|
case 'eth_signTypedData_v4': {
|
|
64
116
|
const [, typedData] = params;
|
|
65
117
|
const parsed = typeof typedData === 'string'
|
|
@@ -73,11 +125,23 @@ export class PrivateKeyRpcClient {
|
|
|
73
125
|
types: parsed.types,
|
|
74
126
|
});
|
|
75
127
|
}
|
|
128
|
+
case 'eth_signTypedData':
|
|
129
|
+
throw new Error('eth_signTypedData (legacy v1) is not supported by PrivateKeyRpcClient. Use eth_signTypedData_v4.');
|
|
130
|
+
// Also a broadcast — it must not slip past the chain check through the
|
|
131
|
+
// default passthrough.
|
|
132
|
+
case 'eth_sendRawTransaction': {
|
|
133
|
+
await this.assertRpcChainMatches();
|
|
134
|
+
const hash = (await this.publicClient.request(request));
|
|
135
|
+
this.sentTransactions.push(hash);
|
|
136
|
+
this.sentTransactionRequests.push({ hash });
|
|
137
|
+
return hash;
|
|
138
|
+
}
|
|
76
139
|
case 'eth_sendTransaction': {
|
|
77
140
|
const [transaction] = params;
|
|
78
141
|
if (!transaction) {
|
|
79
142
|
throw new Error('eth_sendTransaction requires a transaction object.');
|
|
80
143
|
}
|
|
144
|
+
await this.assertRpcChainMatches();
|
|
81
145
|
const request = {
|
|
82
146
|
account: this.account,
|
|
83
147
|
chain: this.chain,
|
|
@@ -93,6 +157,7 @@ export class PrivateKeyRpcClient {
|
|
|
93
157
|
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas
|
|
94
158
|
? BigInt(transaction.maxPriorityFeePerGas)
|
|
95
159
|
: undefined,
|
|
160
|
+
authorizationList: parseAuthorizationList(transaction.authorizationList),
|
|
96
161
|
};
|
|
97
162
|
const hash = await this.walletClient.sendTransaction(request);
|
|
98
163
|
this.sentTransactions.push(hash);
|
|
@@ -108,5 +173,33 @@ export class PrivateKeyRpcClient {
|
|
|
108
173
|
return this.publicClient.request(request);
|
|
109
174
|
}
|
|
110
175
|
}
|
|
176
|
+
/** Read-only viem public client backing this wallet (matchers, receipt waits). */
|
|
177
|
+
get client() {
|
|
178
|
+
return this.publicClient;
|
|
179
|
+
}
|
|
180
|
+
/** Signs an EIP-7702 authorization with this client's local account. */
|
|
181
|
+
async signAuthorization(options) {
|
|
182
|
+
return this.walletClient.signAuthorization({
|
|
183
|
+
account: this.account,
|
|
184
|
+
contractAddress: options.contractAddress,
|
|
185
|
+
chainId: options.chainId,
|
|
186
|
+
nonce: options.nonce,
|
|
187
|
+
executor: options.executor,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// A mismatched rpcUrl/chain pair must fail loudly before anything is
|
|
191
|
+
// broadcast; success is cached, failures retry so a transient RPC error
|
|
192
|
+
// does not poison the client.
|
|
193
|
+
async assertRpcChainMatches() {
|
|
194
|
+
if (this.rpcChainVerified) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const reportedChainId = await this.publicClient.getChainId();
|
|
198
|
+
if (reportedChainId !== this.chain.id) {
|
|
199
|
+
throw new Error(`The RPC endpoint reports chain id ${reportedChainId} but this client is configured for "${this.chain.name}" (id ${this.chain.id}). ` +
|
|
200
|
+
'Refusing to broadcast. Check the rpcUrl and chain options.');
|
|
201
|
+
}
|
|
202
|
+
this.rpcChainVerified = true;
|
|
203
|
+
}
|
|
111
204
|
}
|
|
112
205
|
//# sourceMappingURL=private-key-rpc-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-key-rpc-client.js","sourceRoot":"","sources":["../src/private-key-rpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"private-key-rpc-client.js","sourceRoot":"","sources":["../src/private-key-rpc-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,IAAI,EACJ,SAAS,GAKV,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAWtC,oEAAoE;AACpE,2DAA2D;AAC3D,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAEnD,MAAM,WAAW,GAAG,CAAC,KAAY,EAAW,EAAE,CAC5C,KAAK,CAAC,OAAO,KAAK,IAAI,IAAI,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAE9D,MAAM,mBAAmB,GAAG,CAAC,UAAkB,EAAO,EAAE;IACtD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAClC,OAAO,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,OAAe,CAAC,CAAC,CAAE,KAAK,OAAO,EAAU,CAAC;AAC/E,CAAC,CAAC;AAEF,MAAM,KAAK,GAAG,CAAC,KAAc,EAAmB,EAAE,CAChD,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,KAAa,CAAC,CAAC,CAAC,SAAS,CAAC;AAEnF,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,CAAC,OAAgB,EAAyB,EAAE;IACnE,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AACpD,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,OAAuB,EAAa,EAAE,CACzD,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAE3D,yEAAyE;AACzE,mEAAmE;AACnE,0EAA0E;AAC1E,MAAM,sBAAsB,GAAG,CAC7B,KAAc,EACwF,EAAE;IACxG,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,CAAC,KAAK,IAAI,EAAE,CAA4B,CAAC;QACtD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,eAAe,CAAY,CAAC;QAClE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,MAAM,IAAI,KAAK,CAAC,qBAAqB,KAAK,qBAAqB,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,GAAY,EAAE,IAAY,EAAU,EAAE;YACtD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;gBAC5B,OAAO,GAAG,CAAC;YACb,CAAC;YACD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpD,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC7B,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,qBAAqB,KAAK,KAAK,IAAI,qCAAqC,CAAC,CAAC;QAC5F,CAAC,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,OAAc;YACvB,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC;YAC1C,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC;YACpC,CAAC,EAAE,IAAI,CAAC,CAAQ;YAChB,CAAC,EAAE,IAAI,CAAC,CAAQ;YAChB,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,EAAE,SAAS,CAAC;SAChD,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,OAAO,mBAAmB;IACrB,OAAO,CAAU;IACjB,KAAK,CAAQ;IACb,gBAAgB,GAAU,EAAE,CAAC;IAC7B,uBAAuB,GAK3B,EAAE,CAAC;IAES,YAAY,CAAC;IACb,YAAY,CAAe;IACpC,gBAAgB,GAAG,KAAK,CAAC;IAEjC,YAAY,OAAmC;QAC7C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CACb,sCAAsC,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS,IAAI,CAAC,KAAK,CAAC,EAAE,2CAA2C;gBACpH,uHAAuH;gBACvH,+CAA+C,CAClD,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,mBAAmB,CAAC,mBAAmB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;QAE5E,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,YAAY,GAAG,kBAAkB,CAAC;YACrC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS;SACV,CAAC,CAAC;QACH,IAAI,CAAC,YAAY,GAAG,kBAAkB,CAAC;YACrC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAuB;QACnC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAEpC,QAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,qEAAqE;YACrE,sEAAsE;YACtE,2DAA2D;YAC3D,KAAK,cAAc;gBACjB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAEhC,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,iEAAiE;gBACjE,oEAAoE;gBACpE,qEAAqE;gBACrE,WAAW;gBACX,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;gBAC/B,MAAM,cAAc,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;gBACrE,MAAM,eAAe,GAAG,OAAO,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;gBACxE,MAAM,OAAO,GACX,cAAc,IAAI,CAAC,eAAe,IAAI,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;gBAC9E,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;oBACnC,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;iBACnC,CAAC,CAAC;YACL,CAAC;YAED,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;gBAC3B,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;oBACnC,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC;iBACnC,CAAC,CAAC;YACL,CAAC;YAED,sEAAsE;YACtE,0DAA0D;YAC1D,KAAK,sBAAsB,CAAC;YAC5B,KAAK,sBAAsB,CAAC,CAAC,CAAC;gBAC5B,MAAM,CAAC,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC;gBAC7B,MAAM,MAAM,GACV,OAAO,SAAS,KAAK,QAAQ;oBAC3B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;oBACvB,CAAC,CAAC,SAAS,CAAC;gBAEhB,OAAO,IAAI,CAAC,YAAY,CAAC,aAAa,CAAC;oBACrC,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,KAAK,EAAE,MAAM,CAAC,KAAK;iBACpB,CAAC,CAAC;YACL,CAAC;YAED,KAAK,mBAAmB;gBACtB,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;YAEJ,uEAAuE;YACvE,uBAAuB;YACvB,KAAK,wBAAwB,CAAC,CAAC,CAAC;gBAC9B,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,OAAgB,CAAC,CAAQ,CAAC;gBACxE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC5C,OAAO,IAAI,CAAC;YACd,CAAC;YAED,KAAK,qBAAqB,CAAC,CAAC,CAAC;gBAC3B,MAAM,CAAC,WAAW,CAAC,GAAG,MAA+C,CAAC;gBACtE,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;gBACxE,CAAC;gBAED,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAEnC,MAAM,OAAO,GAAG;oBACd,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,EAAE,EAAE,WAAW,CAAC,EAAqB;oBACrC,IAAI,EAAE,WAAW,CAAC,IAAuB;oBACzC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,KAAe,CAAC,CAAC,CAAC,CAAC,SAAS;oBAC1E,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,GAAa,CAAC,CAAC,CAAC,CAAC,SAAS;oBACpE,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,QAAkB,CAAC,CAAC,CAAC,CAAC,SAAS;oBACnF,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,KAAe,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;oBAClF,YAAY,EAAE,WAAW,CAAC,YAAY;wBACpC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,YAAsB,CAAC;wBAC5C,CAAC,CAAC,SAAS;oBACb,oBAAoB,EAAE,WAAW,CAAC,oBAAoB;wBACpD,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,oBAA8B,CAAC;wBACpD,CAAC,CAAC,SAAS;oBACb,iBAAiB,EAAE,sBAAsB,CAAC,WAAW,CAAC,iBAAiB,CAAC;iBACzE,CAAC;gBAEF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,OAAgB,CAAC,CAAC;gBACvE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACjC,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC;oBAChC,IAAI;oBACJ,EAAE,EAAE,WAAW,CAAC,EAAqB;oBACrC,IAAI,EAAE,WAAW,CAAC,IAAuB;oBACzC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;iBACjE,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACd,CAAC;YAED;gBACE,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,OAAgB,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,wEAAwE;IACxE,KAAK,CAAC,iBAAiB,CAAC,OAKvB;QACC,OAAO,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC;YACzC,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,eAAe,EAAE,OAAO,CAAC,eAAe;YACxC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAClB,CAAC,CAAC;IACd,CAAC;IAED,qEAAqE;IACrE,wEAAwE;IACxE,8BAA8B;IACtB,KAAK,CAAC,qBAAqB;QACjC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;QAC7D,IAAI,eAAe,KAAK,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,qCAAqC,eAAe,uCAAuC,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK;gBACnI,4DAA4D,CAC/D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type RealWalletSession, type RealWalletSetup, type WalletGeneration } from './real-wallet.js';
|
|
2
|
+
export type BuildWalletProfileOptions = {
|
|
3
|
+
/** Unpacked MetaMask extension directory (see prepareMetaMaskExtension). */
|
|
4
|
+
extensionPath: string;
|
|
5
|
+
/** Wallet setup; seedPhrase is required to build a profile. */
|
|
6
|
+
setup: RealWalletSetup;
|
|
7
|
+
/** Directory cached profiles live in. Defaults to ~/.cache/web3-tester/profiles. */
|
|
8
|
+
cacheDir?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Run the one-time onboarding headless. No default — set it here or via
|
|
11
|
+
* WEB3_TESTER_REAL_WALLET_HEADLESS=true|false (see RealWalletLaunchOptions).
|
|
12
|
+
*/
|
|
13
|
+
headless?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* UI generation to drive during onboarding/customization. Defaults to the
|
|
16
|
+
* extension manifest's major version; set it (matching the per-test launch)
|
|
17
|
+
* for custom builds whose manifest version does not reflect their UI.
|
|
18
|
+
*/
|
|
19
|
+
generation?: WalletGeneration;
|
|
20
|
+
/** Rebuild even if a cached profile exists. */
|
|
21
|
+
force?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* One-time profile customization (import keys, add accounts/networks/
|
|
24
|
+
* tokens) baked into the cached profile — `key` joins the cache key, so
|
|
25
|
+
* bump it whenever `run` changes. The builder waits for extension state to
|
|
26
|
+
* flush to disk (13.x persists to IndexedDB with a debounce) before
|
|
27
|
+
* closing, so mutations survive profile close. Note: whatever account/
|
|
28
|
+
* network `run` leaves selected is what every cloned per-test profile
|
|
29
|
+
* boots with — switch back to the primary account before returning if
|
|
30
|
+
* tests expect the defaults.
|
|
31
|
+
*/
|
|
32
|
+
customize?: {
|
|
33
|
+
key: string;
|
|
34
|
+
run(session: RealWalletSession): Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Polls the profile's extension storage (IndexedDB leveldb + blob and Local
|
|
39
|
+
* Extension Settings) from Node until a write newer than `since` lands and
|
|
40
|
+
* the directories stay quiet for `quietMs` (default 1500ms — 500ms of margin
|
|
41
|
+
* above MetaMask 13.x's verified 1000ms OperationSafener debounce). Times
|
|
42
|
+
* out silently; the customize-persistence smoke test is the regression trap
|
|
43
|
+
* for a missed flush.
|
|
44
|
+
*/
|
|
45
|
+
export declare function waitForExtensionStatePersisted(profileDir: string, extensionId: string, options?: {
|
|
46
|
+
since?: number;
|
|
47
|
+
quietMs?: number;
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
export declare const defaultProfileCacheDir: () => string;
|
|
51
|
+
/**
|
|
52
|
+
* Builds (once) and returns a cached, fully onboarded MetaMask profile
|
|
53
|
+
* directory keyed by (seed phrase, password, extension version). The first
|
|
54
|
+
* call walks MetaMask onboarding in a real browser; subsequent calls return
|
|
55
|
+
* instantly. Use cloneWalletProfile to obtain a disposable per-test copy —
|
|
56
|
+
* never launch the cached directory directly.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildWalletProfile(options: BuildWalletProfileOptions): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Copies a cached profile into a disposable directory (per test or per
|
|
61
|
+
* worker) so parallel runs never collide on Chromium's profile singleton
|
|
62
|
+
* lock and tests cannot dirty the cache.
|
|
63
|
+
*/
|
|
64
|
+
export declare function cloneWalletProfile(cachedProfileDir: string, targetDir: string): Promise<string>;
|
|
65
|
+
//# sourceMappingURL=real-wallet-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet-cache.d.ts","sourceRoot":"","sources":["../src/real-wallet-cache.ts"],"names":[],"mappings":"AAOA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAyB1B,MAAM,MAAM,yBAAyB,GAAG;IACtC,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,KAAK,EAAE,eAAe,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;;OASG;IACH,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CAC7E,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,OAAO,CAAC,IAAI,CAAC,CAsDf;AAED,eAAO,MAAM,sBAAsB,QAAO,MACoB,CAAC;AAiB/D;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwH5F;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,gBAAgB,EAAE,MAAM,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAmBjB"}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { mnemonicToAccount } from 'viem/accounts';
|
|
6
|
+
import { extensionManifestVersion } from './metamask-extension.js';
|
|
7
|
+
import { passwordForSetup } from './real-wallet-setup.js';
|
|
8
|
+
import { launchRealWallet, resolveRealWalletProfile, } from './real-wallet.js';
|
|
9
|
+
const READY_MARKER = '.web3-tester-profile-ready';
|
|
10
|
+
// Chromium singleton artifacts must never travel with a cloned profile.
|
|
11
|
+
const SINGLETON_FILES = ['SingletonLock', 'SingletonCookie', 'SingletonSocket'];
|
|
12
|
+
// Regenerable Chromium cache subtrees dominate a cached profile's size;
|
|
13
|
+
// pruning them before the copy makes clones ~3x faster and ~75% smaller.
|
|
14
|
+
// 'Local Extension Settings' (the MetaMask vault) and 'IndexedDB' (13.x
|
|
15
|
+
// debounced extension state) must always travel — never list them here.
|
|
16
|
+
const SKIP_CLONE_DIRS = new Set([
|
|
17
|
+
'Cache',
|
|
18
|
+
'Code Cache',
|
|
19
|
+
'DawnGraphiteCache',
|
|
20
|
+
'DawnWebGPUCache',
|
|
21
|
+
'GPUCache',
|
|
22
|
+
'GraphiteDawnCache',
|
|
23
|
+
'GrShaderCache',
|
|
24
|
+
'Service Worker',
|
|
25
|
+
'ShaderCache',
|
|
26
|
+
'component_crx_cache',
|
|
27
|
+
'extensions_crx_cache',
|
|
28
|
+
]);
|
|
29
|
+
/**
|
|
30
|
+
* Polls the profile's extension storage (IndexedDB leveldb + blob and Local
|
|
31
|
+
* Extension Settings) from Node until a write newer than `since` lands and
|
|
32
|
+
* the directories stay quiet for `quietMs` (default 1500ms — 500ms of margin
|
|
33
|
+
* above MetaMask 13.x's verified 1000ms OperationSafener debounce). Times
|
|
34
|
+
* out silently; the customize-persistence smoke test is the regression trap
|
|
35
|
+
* for a missed flush.
|
|
36
|
+
*/
|
|
37
|
+
export async function waitForExtensionStatePersisted(profileDir, extensionId, options = {}) {
|
|
38
|
+
const since = options.since ?? Date.now();
|
|
39
|
+
const quietMs = options.quietMs ?? 1_500;
|
|
40
|
+
const timeoutMs = options.timeoutMs ?? 15_000;
|
|
41
|
+
// A consumer profileDir can itself be a Chrome "Default"/"Profile N"
|
|
42
|
+
// directory; resolve the actual profile root the same way launch does.
|
|
43
|
+
const profile = resolveRealWalletProfile(profileDir);
|
|
44
|
+
const root = profile.profileDirectory
|
|
45
|
+
? path.join(profile.userDataDir, profile.profileDirectory)
|
|
46
|
+
: path.join(profile.userDataDir, 'Default');
|
|
47
|
+
const watched = [
|
|
48
|
+
path.join(root, 'IndexedDB', `chrome-extension_${extensionId}_0.indexeddb.leveldb`),
|
|
49
|
+
path.join(root, 'IndexedDB', `chrome-extension_${extensionId}_0.indexeddb.blob`),
|
|
50
|
+
path.join(root, 'Local Extension Settings', extensionId),
|
|
51
|
+
];
|
|
52
|
+
const newestWrite = () => {
|
|
53
|
+
let newest = 0;
|
|
54
|
+
for (const dir of watched) {
|
|
55
|
+
let entries;
|
|
56
|
+
try {
|
|
57
|
+
entries = fs.readdirSync(dir);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const entry of entries) {
|
|
63
|
+
try {
|
|
64
|
+
const mtime = fs.statSync(path.join(dir, entry)).mtimeMs;
|
|
65
|
+
if (mtime > newest)
|
|
66
|
+
newest = mtime;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// File rotated away mid-scan.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return newest;
|
|
74
|
+
};
|
|
75
|
+
const deadline = Date.now() + timeoutMs;
|
|
76
|
+
let lastSeen = 0;
|
|
77
|
+
let quietSince = 0;
|
|
78
|
+
while (Date.now() < deadline) {
|
|
79
|
+
const newest = newestWrite();
|
|
80
|
+
if (newest > since) {
|
|
81
|
+
if (newest !== lastSeen) {
|
|
82
|
+
lastSeen = newest;
|
|
83
|
+
quietSince = Date.now();
|
|
84
|
+
}
|
|
85
|
+
else if (Date.now() - quietSince >= quietMs) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
await sleep(250);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export const defaultProfileCacheDir = () => path.join(os.homedir(), '.cache', 'web3-tester', 'profiles');
|
|
93
|
+
const cacheKey = (options) => createHash('sha256')
|
|
94
|
+
.update(JSON.stringify({
|
|
95
|
+
seedPhrase: options.setup.seedPhrase,
|
|
96
|
+
password: passwordForSetup(options.setup),
|
|
97
|
+
extensionVersion: extensionManifestVersion(options.extensionPath),
|
|
98
|
+
customizeKey: options.customize?.key,
|
|
99
|
+
}))
|
|
100
|
+
.digest('hex')
|
|
101
|
+
.slice(0, 16);
|
|
102
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
103
|
+
/**
|
|
104
|
+
* Builds (once) and returns a cached, fully onboarded MetaMask profile
|
|
105
|
+
* directory keyed by (seed phrase, password, extension version). The first
|
|
106
|
+
* call walks MetaMask onboarding in a real browser; subsequent calls return
|
|
107
|
+
* instantly. Use cloneWalletProfile to obtain a disposable per-test copy —
|
|
108
|
+
* never launch the cached directory directly.
|
|
109
|
+
*/
|
|
110
|
+
export async function buildWalletProfile(options) {
|
|
111
|
+
if (!options.setup.seedPhrase) {
|
|
112
|
+
throw new Error('buildWalletProfile requires setup.seedPhrase.');
|
|
113
|
+
}
|
|
114
|
+
const cacheDir = options.cacheDir ?? defaultProfileCacheDir();
|
|
115
|
+
const profileDir = path.join(cacheDir, cacheKey(options));
|
|
116
|
+
const marker = path.join(profileDir, READY_MARKER);
|
|
117
|
+
if (options.force) {
|
|
118
|
+
fs.rmSync(profileDir, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
else if (fs.existsSync(marker)) {
|
|
121
|
+
return profileDir;
|
|
122
|
+
}
|
|
123
|
+
// mkdir is atomic: exactly one concurrent builder wins the lock; the rest
|
|
124
|
+
// wait for the ready marker. Locks left behind by killed builders are
|
|
125
|
+
// considered stale after 5 minutes and stolen.
|
|
126
|
+
const lockDir = `${profileDir}.lock`;
|
|
127
|
+
const lockIsStale = () => {
|
|
128
|
+
try {
|
|
129
|
+
return Date.now() - fs.statSync(lockDir).mtimeMs > 300_000;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
136
|
+
try {
|
|
137
|
+
fs.mkdirSync(lockDir);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
if (lockIsStale()) {
|
|
141
|
+
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
142
|
+
return buildWalletProfile(options);
|
|
143
|
+
}
|
|
144
|
+
// Keep waiting as long as the builder's lock stays fresh — the builder
|
|
145
|
+
// heartbeats the lock every 60s precisely because a customize build can
|
|
146
|
+
// run well past the 5-minute stale threshold. A fixed deadline shorter
|
|
147
|
+
// than that would abandon a healthy builder. The hard ceiling only
|
|
148
|
+
// backstops a hang that somehow keeps the lock fresh without finishing.
|
|
149
|
+
const deadline = Date.now() + 1_800_000;
|
|
150
|
+
while (Date.now() < deadline) {
|
|
151
|
+
if (fs.existsSync(marker)) {
|
|
152
|
+
return profileDir;
|
|
153
|
+
}
|
|
154
|
+
if (!fs.existsSync(lockDir) || lockIsStale()) {
|
|
155
|
+
// The builder died (or stalled past the stale threshold) without
|
|
156
|
+
// producing a profile; take over.
|
|
157
|
+
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
158
|
+
return buildWalletProfile(options);
|
|
159
|
+
}
|
|
160
|
+
await sleep(250);
|
|
161
|
+
}
|
|
162
|
+
throw new Error(`Timed out waiting for another process to finish building the wallet profile at ${profileDir}.`);
|
|
163
|
+
}
|
|
164
|
+
// Builds with a customize hook can exceed the 5-minute stale-lock
|
|
165
|
+
// threshold; refresh the lock mtime so a waiting worker never steals it
|
|
166
|
+
// mid-build.
|
|
167
|
+
const heartbeat = setInterval(() => {
|
|
168
|
+
try {
|
|
169
|
+
const now = new Date();
|
|
170
|
+
fs.utimesSync(lockDir, now, now);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Lock already gone — the finally below is about to run anyway.
|
|
174
|
+
}
|
|
175
|
+
}, 60_000);
|
|
176
|
+
heartbeat.unref?.();
|
|
177
|
+
try {
|
|
178
|
+
fs.rmSync(profileDir, { recursive: true, force: true });
|
|
179
|
+
fs.mkdirSync(profileDir, { recursive: true });
|
|
180
|
+
const onboardStartedAt = Date.now();
|
|
181
|
+
const session = await launchRealWallet({
|
|
182
|
+
extensionPath: options.extensionPath,
|
|
183
|
+
profileDir,
|
|
184
|
+
setup: options.setup,
|
|
185
|
+
headless: options.headless,
|
|
186
|
+
generation: options.generation,
|
|
187
|
+
// Verifying against the seed-derived address guarantees the build
|
|
188
|
+
// actually imported a wallet — a silently skipped onboarding would
|
|
189
|
+
// otherwise produce a cached profile with no vault.
|
|
190
|
+
expectedAddress: mnemonicToAccount(options.setup.seedPhrase).address,
|
|
191
|
+
});
|
|
192
|
+
// 13.x flushes extension state through a debounced write (safe-reload's
|
|
193
|
+
// OperationSafener, a 1000ms trailing-edge debounce around
|
|
194
|
+
// persistenceManager.set()); wait for the post-onboarding (and, when
|
|
195
|
+
// customizing, post-mutation) write to land and stay quiet before
|
|
196
|
+
// closing — a missed flush silently replays onboarding or loses the
|
|
197
|
+
// customization.
|
|
198
|
+
if (options.customize) {
|
|
199
|
+
const mutationsStartedAt = Date.now();
|
|
200
|
+
await options.customize.run(session);
|
|
201
|
+
await waitForExtensionStatePersisted(profileDir, session.extensionId, {
|
|
202
|
+
since: mutationsStartedAt,
|
|
203
|
+
timeoutMs: 20_000,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
await waitForExtensionStatePersisted(profileDir, session.extensionId, {
|
|
208
|
+
since: onboardStartedAt,
|
|
209
|
+
timeoutMs: 15_000,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
await session.close();
|
|
213
|
+
fs.writeFileSync(marker, JSON.stringify({ createdAt: new Date().toISOString() }));
|
|
214
|
+
return profileDir;
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
fs.rmSync(profileDir, { recursive: true, force: true });
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
clearInterval(heartbeat);
|
|
222
|
+
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Copies a cached profile into a disposable directory (per test or per
|
|
227
|
+
* worker) so parallel runs never collide on Chromium's profile singleton
|
|
228
|
+
* lock and tests cannot dirty the cache.
|
|
229
|
+
*/
|
|
230
|
+
export async function cloneWalletProfile(cachedProfileDir, targetDir) {
|
|
231
|
+
if (!fs.existsSync(path.join(cachedProfileDir, READY_MARKER))) {
|
|
232
|
+
throw new Error(`${cachedProfileDir} is not a completed web3-tester profile cache (missing ${READY_MARKER}).`);
|
|
233
|
+
}
|
|
234
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
235
|
+
fs.cpSync(cachedProfileDir, targetDir, {
|
|
236
|
+
recursive: true,
|
|
237
|
+
// Returning false for a directory prunes its whole subtree.
|
|
238
|
+
filter: (source) => !SKIP_CLONE_DIRS.has(path.basename(source)),
|
|
239
|
+
});
|
|
240
|
+
for (const file of [READY_MARKER, ...SINGLETON_FILES]) {
|
|
241
|
+
fs.rmSync(path.join(targetDir, file), { force: true });
|
|
242
|
+
}
|
|
243
|
+
return targetDir;
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=real-wallet-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet-cache.js","sourceRoot":"","sources":["../src/real-wallet-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type BuildWalletProfileOptions } from './real-wallet-cache.js';
|
|
2
|
+
import { type RealWalletSession, type RealWalletSetup, type WalletGeneration } from './real-wallet.js';
|
|
3
|
+
export type RealWalletFixtureOptions = {
|
|
4
|
+
/** Wallet setup. Defaults to WEB3_TESTER_REAL_WALLET_SECRET_RECOVERY_PHRASE / _PASSWORD. */
|
|
5
|
+
setup?: RealWalletSetup;
|
|
6
|
+
/**
|
|
7
|
+
* Unpacked MetaMask directory. Defaults to
|
|
8
|
+
* WEB3_TESTER_REAL_WALLET_EXTENSION_PATH, or downloads the pinned release.
|
|
9
|
+
*/
|
|
10
|
+
extensionPath?: string;
|
|
11
|
+
/** MetaMask version to download when extensionPath is not given. */
|
|
12
|
+
metamaskVersion?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Explicit persistent profile directory. Disables the profile cache; the
|
|
15
|
+
* test reuses (and mutates) this profile directly, so parallel tests must
|
|
16
|
+
* not share it.
|
|
17
|
+
*/
|
|
18
|
+
profileDir?: string;
|
|
19
|
+
baseURL?: string;
|
|
20
|
+
expectedAddress?: string;
|
|
21
|
+
/**
|
|
22
|
+
* MetaMask UI generation to drive ('12x' | '13x'). Defaults to the major
|
|
23
|
+
* version in the extension's manifest; set explicitly for custom builds.
|
|
24
|
+
*/
|
|
25
|
+
generation?: WalletGeneration;
|
|
26
|
+
/**
|
|
27
|
+
* Run the browser headless. No default — choose explicitly here or via
|
|
28
|
+
* WEB3_TESTER_REAL_WALLET_HEADLESS=true|false. Headed is the fully
|
|
29
|
+
* validated mode.
|
|
30
|
+
*/
|
|
31
|
+
headless?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* One-time profile customization baked into the cached profile (import
|
|
34
|
+
* keys, add accounts/networks/tokens) — Synpress defineWalletSetup-style.
|
|
35
|
+
* Ignored when an explicit profileDir bypasses the cache.
|
|
36
|
+
*/
|
|
37
|
+
profileSetup?: BuildWalletProfileOptions['customize'];
|
|
38
|
+
};
|
|
39
|
+
export type RealWalletFixtures = {
|
|
40
|
+
realWalletOptions: RealWalletFixtureOptions;
|
|
41
|
+
realWallet: RealWalletSession;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Playwright fixtures for real-MetaMask tests. Each test gets a disposable
|
|
45
|
+
* clone of a cached, pre-onboarded profile (built once per seed phrase +
|
|
46
|
+
* extension version), so tests are isolated, parallel-safe, and skip
|
|
47
|
+
* onboarding cost after the first run. `context` and `page` are rebound to
|
|
48
|
+
* the persistent extension context.
|
|
49
|
+
*/
|
|
50
|
+
export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & RealWalletFixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
|
|
51
|
+
export { expect } from './matchers.js';
|
|
52
|
+
//# sourceMappingURL=real-wallet-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet-fixtures.d.ts","sourceRoot":"","sources":["../src/real-wallet-fixtures.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,wBAAwB,GAAG;IACrC,4FAA4F;IAC5F,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,yBAAyB,CAAC,WAAW,CAAC,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,iBAAiB,EAAE,wBAAwB,CAAC;IAC5C,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAOF;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,kQAmEf,CAAC;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
|