@marigoldlabs/web3-tester 0.1.2 → 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.
Files changed (75) hide show
  1. package/.env.example +26 -17
  2. package/LICENSE +21 -0
  3. package/README.md +167 -41
  4. package/dist/anvil.d.ts +90 -2
  5. package/dist/anvil.d.ts.map +1 -1
  6. package/dist/anvil.js +215 -13
  7. package/dist/anvil.js.map +1 -1
  8. package/dist/contracts/test-erc20.d.ts +227 -0
  9. package/dist/contracts/test-erc20.d.ts.map +1 -0
  10. package/dist/contracts/test-erc20.js +8 -0
  11. package/dist/contracts/test-erc20.js.map +1 -0
  12. package/dist/erc20.d.ts +38 -0
  13. package/dist/erc20.d.ts.map +1 -0
  14. package/dist/erc20.js +229 -0
  15. package/dist/erc20.js.map +1 -0
  16. package/dist/fixtures.d.ts +44 -2
  17. package/dist/fixtures.d.ts.map +1 -1
  18. package/dist/fixtures.js +162 -17
  19. package/dist/fixtures.js.map +1 -1
  20. package/dist/index.d.ts +17 -5
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +7 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/injected-provider.d.ts.map +1 -1
  25. package/dist/injected-provider.js +142 -79
  26. package/dist/injected-provider.js.map +1 -1
  27. package/dist/live-fixtures.d.ts +32 -3
  28. package/dist/live-fixtures.d.ts.map +1 -1
  29. package/dist/live-fixtures.js +64 -27
  30. package/dist/live-fixtures.js.map +1 -1
  31. package/dist/matchers.d.ts +90 -0
  32. package/dist/matchers.d.ts.map +1 -0
  33. package/dist/matchers.js +268 -0
  34. package/dist/matchers.js.map +1 -0
  35. package/dist/metamask-extension.d.ts +34 -0
  36. package/dist/metamask-extension.d.ts.map +1 -0
  37. package/dist/metamask-extension.js +97 -0
  38. package/dist/metamask-extension.js.map +1 -0
  39. package/dist/mock-wallet-controller.d.ts +205 -3
  40. package/dist/mock-wallet-controller.d.ts.map +1 -1
  41. package/dist/mock-wallet-controller.js +843 -46
  42. package/dist/mock-wallet-controller.js.map +1 -1
  43. package/dist/private-key-rpc-client.d.ts +1730 -0
  44. package/dist/private-key-rpc-client.d.ts.map +1 -1
  45. package/dist/private-key-rpc-client.js +105 -12
  46. package/dist/private-key-rpc-client.js.map +1 -1
  47. package/dist/real-wallet-cache.d.ts +65 -0
  48. package/dist/real-wallet-cache.d.ts.map +1 -0
  49. package/dist/real-wallet-cache.js +245 -0
  50. package/dist/real-wallet-cache.js.map +1 -0
  51. package/dist/real-wallet-fixtures.d.ts +52 -0
  52. package/dist/real-wallet-fixtures.d.ts.map +1 -0
  53. package/dist/real-wallet-fixtures.js +73 -0
  54. package/dist/real-wallet-fixtures.js.map +1 -0
  55. package/dist/real-wallet.d.ts +123 -14
  56. package/dist/real-wallet.d.ts.map +1 -1
  57. package/dist/real-wallet.js +1336 -57
  58. package/dist/real-wallet.js.map +1 -1
  59. package/dist/transactions.d.ts +118 -0
  60. package/dist/transactions.d.ts.map +1 -0
  61. package/dist/transactions.js +207 -0
  62. package/dist/transactions.js.map +1 -0
  63. package/dist/types.d.ts +1 -0
  64. package/dist/types.d.ts.map +1 -1
  65. package/dist/walletconnect.d.ts +206 -0
  66. package/dist/walletconnect.d.ts.map +1 -0
  67. package/dist/walletconnect.js +359 -0
  68. package/dist/walletconnect.js.map +1 -0
  69. package/examples/live-sepolia.spec.ts +20 -1
  70. package/package.json +62 -6
  71. package/docs/API.md +0 -223
  72. package/docs/ARCHITECTURE.md +0 -81
  73. package/docs/CONSUMING_FROM_FJORD.md +0 -123
  74. package/docs/FJORD_LIVE_QA.md +0 -87
  75. package/docs/RELEASE_CHECKLIST.md +0 -55
package/dist/index.js CHANGED
@@ -1,6 +1,12 @@
1
1
  export { AnvilInstance, ChainController } from './anvil.js';
2
+ export { Erc20DealError, dealErc20, discoverErc20BalanceSlot, getErc20Balance, TEST_ERC20_ABI, TEST_ERC20_BYTECODE, } from './erc20.js';
3
+ export { anyValue, web3Matchers } from './matchers.js';
4
+ export { extractRevertInfo, waitForDecodedTransaction, web3Equals, web3Stringify, } from './transactions.js';
2
5
  export { test, expect } from './fixtures.js';
3
- export { MockWalletController } from './mock-wallet-controller.js';
6
+ export { MockWalletController, httpRpcClient } from './mock-wallet-controller.js';
4
7
  export { PrivateKeyRpcClient } from './private-key-rpc-client.js';
5
8
  export { launchRealWallet, resolveRealWalletProfile } from './real-wallet.js';
9
+ export { DEFAULT_METAMASK_VERSION, prepareMetaMaskExtension, extensionManifestVersion, } from './metamask-extension.js';
10
+ export { buildWalletProfile, cloneWalletProfile, waitForExtensionStatePersisted, } from './real-wallet-cache.js';
11
+ export { DEFAULT_WALLET_PASSWORD } from './real-wallet-setup.js';
6
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EACL,cAAc,EACd,SAAS,EACT,wBAAwB,EACxB,eAAe,EACf,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,UAAU,EACV,aAAa,GACd,MAAM,mBAAmB,CAAC;AAU3B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EACL,wBAAwB,EACxB,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"injected-provider.d.ts","sourceRoot":"","sources":["../src/injected-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,eAAO,MAAM,aAAa,+BAAc,CAAC;AACzC,eAAO,MAAM,WAAW,0BAAe,CAAC;AAExC,eAAO,MAAM,2BAA2B,GAAI,QAAQ,gBAAgB,KAAG,MAuItE,CAAC"}
1
+ {"version":3,"file":"injected-provider.d.ts","sourceRoot":"","sources":["../src/injected-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKnD,eAAO,MAAM,aAAa,+BAAc,CAAC;AACzC,eAAO,MAAM,WAAW,0BAAe,CAAC;AAExC,eAAO,MAAM,2BAA2B,GAAI,QAAQ,gBAAgB,KAAG,MAsMtE,CAAC"}
@@ -5,34 +5,22 @@ 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
- const getListeners = (event) => {
11
- if (!listeners.has(event)) {
12
- listeners.set(event, new Set());
13
- }
14
- return listeners.get(event);
15
- };
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
+ }
16
17
 
17
- const emit = (event, payload) => {
18
- if (event === 'accountsChanged') {
19
- provider.selectedAddress = Array.isArray(payload) && payload.length > 0 ? payload[0] : null;
20
- }
18
+ const toNetworkVersion = (chainId) => String(Number(BigInt(chainId)));
21
19
 
22
- if (event === 'chainChanged') {
23
- provider.chainId = payload;
24
- provider.networkVersion = String(Number(BigInt(payload)));
25
- }
26
-
27
- const callbacks = listeners.get(event);
28
- if (!callbacks) {
29
- return;
30
- }
31
-
32
- for (const callback of [...callbacks]) {
33
- callback(payload);
34
- }
35
- };
20
+ // EIP-1193 connectivity means "can the provider reach the chain", which is
21
+ // independent of account authorization. The controller's disconnect()
22
+ // simulates losing the chain via the 'disconnect' event.
23
+ let chainDisconnected = false;
36
24
 
37
25
  const request = async (args) => {
38
26
  if (!args || typeof args.method !== 'string') {
@@ -56,74 +44,123 @@ export const buildInjectedProviderScript = (config) => `
56
44
  return response.result;
57
45
  };
58
46
 
59
- const provider = {
60
- isMetaMask: true,
61
- isMock: true,
62
- selectedAddress: config.connected && config.accounts.length > 0 ? config.accounts[0] : null,
63
- chainId: config.chainId,
64
- networkVersion: String(Number(BigInt(config.chainId))),
65
- request,
66
- enable: () => request({ method: 'eth_requestAccounts' }),
67
- send: (methodOrPayload, paramsOrCallback) => {
68
- if (typeof methodOrPayload === 'string') {
69
- return request({ method: methodOrPayload, params: paramsOrCallback });
47
+ // One distinct provider object per announced wallet so selector tests can
48
+ // detect a dapp talking to the wrong provider. They share request handling
49
+ // and state, but each has its own identity and listener registry.
50
+ const createProviderEntry = (info) => {
51
+ const listeners = new Map();
52
+
53
+ const getListeners = (event) => {
54
+ if (!listeners.has(event)) {
55
+ listeners.set(event, new Set());
70
56
  }
57
+ return listeners.get(event);
58
+ };
59
+
60
+ const provider = {
61
+ isMetaMask: true,
62
+ isMock: true,
63
+ selectedAddress: config.connected && config.accounts.length > 0 ? config.accounts[0] : null,
64
+ chainId: config.chainId,
65
+ networkVersion: toNetworkVersion(config.chainId),
66
+ request,
67
+ isConnected: () => !chainDisconnected,
68
+ enable: () => request({ method: 'eth_requestAccounts' }),
69
+ send: (methodOrPayload, paramsOrCallback) => {
70
+ if (typeof methodOrPayload === 'string') {
71
+ return request({ method: methodOrPayload, params: paramsOrCallback });
72
+ }
73
+
74
+ const payload = methodOrPayload;
75
+ const callback = paramsOrCallback;
76
+ request(payload)
77
+ .then((result) => callback?.(null, { id: payload.id, jsonrpc: '2.0', result }))
78
+ .catch((error) => callback?.(error, null));
79
+ },
80
+ sendAsync: (payload, callback) => {
81
+ request(payload)
82
+ .then((result) => callback(null, { id: payload.id, jsonrpc: '2.0', result }))
83
+ .catch((error) => callback(error, null));
84
+ },
85
+ on: (event, handler) => {
86
+ getListeners(event).add(handler);
87
+ return provider;
88
+ },
89
+ once: (event, handler) => {
90
+ const wrapped = (payload) => {
91
+ provider.removeListener(event, wrapped);
92
+ handler(payload);
93
+ };
94
+ provider.on(event, wrapped);
95
+ return provider;
96
+ },
97
+ removeListener: (event, handler) => {
98
+ listeners.get(event)?.delete(handler);
99
+ return provider;
100
+ },
101
+ removeAllListeners: (event) => {
102
+ if (event) {
103
+ listeners.delete(event);
104
+ } else {
105
+ listeners.clear();
106
+ }
107
+ return provider;
108
+ },
109
+ _metamask: {
110
+ isUnlocked: async () => true,
111
+ },
112
+ };
71
113
 
72
- const payload = methodOrPayload;
73
- const callback = paramsOrCallback;
74
- request(payload)
75
- .then((result) => callback?.(null, { id: payload.id, jsonrpc: '2.0', result }))
76
- .catch((error) => callback?.(error, null));
77
- },
78
- sendAsync: (payload, callback) => {
79
- request(payload)
80
- .then((result) => callback(null, { id: payload.id, jsonrpc: '2.0', result }))
81
- .catch((error) => callback(error, null));
82
- },
83
- on: (event, handler) => {
84
- getListeners(event).add(handler);
85
- return provider;
86
- },
87
- once: (event, handler) => {
88
- const wrapped = (payload) => {
89
- provider.removeListener(event, wrapped);
90
- handler(payload);
91
- };
92
- provider.on(event, wrapped);
93
- return provider;
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();
114
+ return { info, provider, listeners };
115
+ };
116
+
117
+ const entries = config.providers.map(createProviderEntry);
118
+
119
+ const emit = (event, payload) => {
120
+ if (event === 'connect') {
121
+ chainDisconnected = false;
122
+ }
123
+ if (event === 'disconnect') {
124
+ chainDisconnected = true;
125
+ }
126
+
127
+ for (const entry of entries) {
128
+ if (event === 'accountsChanged') {
129
+ entry.provider.selectedAddress =
130
+ Array.isArray(payload) && payload.length > 0 ? payload[0] : null;
131
+ }
132
+
133
+ if (event === 'chainChanged') {
134
+ entry.provider.chainId = payload;
135
+ entry.provider.networkVersion = toNetworkVersion(payload);
136
+ }
137
+
138
+ const callbacks = entry.listeners.get(event);
139
+ if (!callbacks) {
140
+ continue;
141
+ }
142
+
143
+ for (const callback of [...callbacks]) {
144
+ callback(payload);
104
145
  }
105
- return provider;
106
- },
107
- _metamask: {
108
- isUnlocked: async () => true,
109
- },
146
+ }
110
147
  };
111
148
 
112
149
  const announceProviders = () => {
113
- for (const info of config.providers) {
150
+ for (const entry of entries) {
114
151
  window.dispatchEvent(
115
152
  new CustomEvent('eip6963:announceProvider', {
116
- detail: {
117
- info,
118
- provider,
119
- },
153
+ detail: Object.freeze({
154
+ info: Object.freeze({ ...entry.info }),
155
+ provider: entry.provider,
156
+ }),
120
157
  }),
121
158
  );
122
159
  }
123
160
  };
124
161
 
125
162
  Object.defineProperty(window, 'ethereum', {
126
- value: provider,
163
+ value: entries[0].provider,
127
164
  configurable: true,
128
165
  enumerable: true,
129
166
  writable: true,
@@ -136,6 +173,32 @@ export const buildInjectedProviderScript = (config) => `
136
173
 
137
174
  window.addEventListener('eip6963:requestProvider', announceProviders);
138
175
  queueMicrotask(announceProviders);
176
+
177
+ // The serialized config snapshot goes stale once the controller mutates
178
+ // accounts or chain and the page navigates; refresh the synchronous mirror
179
+ // properties from the wallet's current state as soon as the bridge is up.
180
+ const refresh = async () => {
181
+ try {
182
+ const response = await window.${BRIDGE_NAME}({
183
+ method: 'metamask_getProviderState',
184
+ params: [],
185
+ });
186
+ if (!response.ok) {
187
+ return;
188
+ }
189
+
190
+ const state = response.result;
191
+ for (const entry of entries) {
192
+ entry.provider.selectedAddress = state.accounts.length > 0 ? state.accounts[0] : null;
193
+ entry.provider.chainId = state.chainId;
194
+ entry.provider.networkVersion = toNetworkVersion(state.chainId);
195
+ }
196
+ } catch {
197
+ // Bridge not available yet (e.g. detached frame) — sync values fall
198
+ // back to the injected config snapshot.
199
+ }
200
+ };
201
+ refresh();
139
202
  })();
140
203
  `;
141
204
  //# sourceMappingURL=injected-provider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"injected-provider.js","sourceRoot":"","sources":["../src/injected-provider.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,4BAA4B,CAAC;AACjD,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAE7C,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AACzC,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,MAAwB,EAAU,EAAE,CAAC;;mBAE9D,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAqCL,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAwFZ,YAAY;;;;;;;;CAQ9C,CAAC"}
1
+ {"version":3,"file":"injected-provider.js","sourceRoot":"","sources":["../src/injected-provider.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAAG,4BAA4B,CAAC;AACjD,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAE7C,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AACzC,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAExC,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,MAAwB,EAAU,EAAE,CAAC;;mBAE9D,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;oCAyBL,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAyIZ,YAAY;;;;;;;;;;;;;sCAaT,WAAW;;;;;;;;;;;;;;;;;;;;;CAqBhD,CAAC"}
@@ -1,9 +1,38 @@
1
- import { MockWalletController } from './mock-wallet-controller.js';
1
+ import type { Chain } from 'viem';
2
+ import { MockWalletController, type MockWalletControllerOptions } from './mock-wallet-controller.js';
2
3
  import { PrivateKeyRpcClient } from './private-key-rpc-client.js';
4
+ export type LiveFixtureOptions = {
5
+ /** Target chain. Defaults to Sepolia. */
6
+ chain?: Chain;
7
+ /**
8
+ * Passed through to PrivateKeyRpcClient for chains that are neither
9
+ * `testnet: true` nor local dev chains. Without it such chains throw at
10
+ * fixture setup.
11
+ */
12
+ allowMainnet?: boolean;
13
+ /** Env var holding the signing key. Defaults to WEB3_TESTER_PRIVATE_KEY. */
14
+ privateKeyEnv?: string;
15
+ /** Env var holding the RPC URL. Defaults to WEB3_TESTER_RPC_URL. */
16
+ rpcUrlEnv?: string;
17
+ /**
18
+ * Per-test overrides for the injected wallet (provider identity,
19
+ * autoApprove, allowedOrigins, …). Live wallets default to
20
+ * autoApprove: false and origin-scope the provider to baseURL; override
21
+ * here only for keys you are comfortable auto-signing with.
22
+ */
23
+ walletOptions?: Omit<Partial<MockWalletControllerOptions>, 'accounts' | 'chainId'>;
24
+ };
3
25
  export type LiveWeb3Fixtures = {
4
- wallet: MockWalletController;
26
+ liveOptions: LiveFixtureOptions;
5
27
  liveClient: PrivateKeyRpcClient;
28
+ wallet: MockWalletController;
6
29
  };
30
+ /**
31
+ * Builds a live-chain fixture family. The defaults read the signing key from
32
+ * WEB3_TESTER_PRIVATE_KEY (with the legacy FJORD_PRIVATE_KEY still honored)
33
+ * and target Sepolia; pass options to bind other chains or env var names.
34
+ */
35
+ export declare function createLiveFixtures(defaults?: LiveFixtureOptions): import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & LiveWeb3Fixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
7
36
  export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & LiveWeb3Fixtures, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
8
- export { expect } from '@playwright/test';
37
+ export { expect } from './matchers.js';
9
38
  //# sourceMappingURL=live-fixtures.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"live-fixtures.d.ts","sourceRoot":"","sources":["../src/live-fixtures.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,UAAU,EAAE,mBAAmB,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,IAAI,gQA+Bf,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"live-fixtures.d.ts","sourceRoot":"","sources":["../src/live-fixtures.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,EACjC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,yCAAyC;IACzC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,2BAA2B,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,CAAC;CACpF,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,kBAAkB,CAAC;IAChC,UAAU,EAAE,mBAAmB,CAAC;IAChC,MAAM,EAAE,oBAAoB,CAAC;CAC9B,CAAC;AAWF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,GAAE,kBAAuB,kQAuDnE;AAED,eAAO,MAAM,IAAI,gQAAuB,CAAC;AAGzC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
@@ -1,33 +1,70 @@
1
1
  import { test as base } from '@playwright/test';
2
2
  import { sepolia } from 'viem/chains';
3
- import { MockWalletController } from './mock-wallet-controller.js';
3
+ import { MockWalletController, } from './mock-wallet-controller.js';
4
4
  import { PrivateKeyRpcClient } from './private-key-rpc-client.js';
5
- export const test = base.extend({
6
- liveClient: async ({}, use) => {
7
- const privateKey = process.env.FJORD_PRIVATE_KEY;
8
- if (!privateKey) {
9
- throw new Error('FJORD_PRIVATE_KEY is required for live Sepolia Fjord tests.');
5
+ const resolveEnv = (names) => {
6
+ for (const name of names) {
7
+ if (name && process.env[name]) {
8
+ return process.env[name];
10
9
  }
11
- await use(new PrivateKeyRpcClient({
12
- privateKey: privateKey,
13
- chain: sepolia,
14
- rpcUrl: process.env.SEPOLIA_RPC_URL,
15
- }));
16
- },
17
- wallet: async ({ page, liveClient }, use) => {
18
- const wallet = new MockWalletController(page, liveClient, {
19
- accounts: [liveClient.account.address],
20
- chainId: sepolia.id,
21
- autoApprove: true,
22
- connected: true,
23
- providerInfo: {
24
- name: 'MetaMask',
25
- rdns: 'io.metamask',
10
+ }
11
+ return undefined;
12
+ };
13
+ /**
14
+ * Builds a live-chain fixture family. The defaults read the signing key from
15
+ * WEB3_TESTER_PRIVATE_KEY (with the legacy FJORD_PRIVATE_KEY still honored)
16
+ * and target Sepolia; pass options to bind other chains or env var names.
17
+ */
18
+ export function createLiveFixtures(defaults = {}) {
19
+ return base.extend({
20
+ liveOptions: [
21
+ async ({}, use) => {
22
+ await use(defaults);
26
23
  },
27
- });
28
- await wallet.injectMockProvider();
29
- await use(wallet);
30
- },
31
- });
32
- export { expect } from '@playwright/test';
24
+ { option: true },
25
+ ],
26
+ liveClient: async ({ liveOptions }, use) => {
27
+ const options = { ...defaults, ...liveOptions };
28
+ const privateKeyEnv = options.privateKeyEnv ?? 'WEB3_TESTER_PRIVATE_KEY';
29
+ const privateKey = resolveEnv([privateKeyEnv, 'FJORD_PRIVATE_KEY']);
30
+ if (!privateKey) {
31
+ throw new Error(`${privateKeyEnv} is required for live-chain tests.`);
32
+ }
33
+ const rpcUrlEnv = options.rpcUrlEnv ?? 'WEB3_TESTER_RPC_URL';
34
+ await use(new PrivateKeyRpcClient({
35
+ privateKey: privateKey,
36
+ chain: options.chain ?? sepolia,
37
+ rpcUrl: resolveEnv([rpcUrlEnv, 'SEPOLIA_RPC_URL']),
38
+ allowMainnet: options.allowMainnet,
39
+ }));
40
+ },
41
+ wallet: async ({ page, liveClient, liveOptions, baseURL }, use) => {
42
+ const options = { ...defaults, ...liveOptions };
43
+ const wallet = new MockWalletController(page, liveClient, {
44
+ accounts: [liveClient.account.address],
45
+ chainId: liveClient.chain.id,
46
+ // A real key sits behind this provider, so nothing signs or connects
47
+ // until the test arms it (wallet.approveNext(...) for one request,
48
+ // wallet.autoApprove(true) or walletOptions for a whole test), and
49
+ // only frames on the dapp's own origin can reach the wallet at all.
50
+ autoApprove: false,
51
+ connected: true,
52
+ // EIP-5792 stays off over a real key: capability-probing dapps keep
53
+ // the eth_sendTransaction fallback with per-transaction arming,
54
+ // capping a bare approveNext() at one real transaction.
55
+ eip5792: false,
56
+ ...(baseURL ? { allowedOrigins: [baseURL] } : {}),
57
+ // Masquerade as MetaMask by default so production wallet selectors
58
+ // (wagmi / EIP-6963) detect the injected provider unmodified.
59
+ providerInfo: { name: 'MetaMask', rdns: 'io.metamask' },
60
+ ...options.walletOptions,
61
+ });
62
+ await wallet.injectMockProvider();
63
+ await use(wallet);
64
+ },
65
+ });
66
+ }
67
+ export const test = createLiveFixtures();
68
+ // The web3-extended expect: every matcher from ./matchers.js, zero migration.
69
+ export { expect } from './matchers.js';
33
70
  //# sourceMappingURL=live-fixtures.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"live-fixtures.js","sourceRoot":"","sources":["../src/live-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAOlE,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAmB;IAChD,UAAU,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;QAC5B,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACjF,CAAC;QAED,MAAM,GAAG,CACP,IAAI,mBAAmB,CAAC;YACtB,UAAU,EAAE,UAA2B;YACvC,KAAK,EAAE,OAAO;YACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;SACpC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,EAAE;QAC1C,MAAM,MAAM,GAAG,IAAI,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE;YACxD,QAAQ,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC;YACtC,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,WAAW,EAAE,IAAI;YACjB,SAAS,EAAE,IAAI;YACf,YAAY,EAAE;gBACZ,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,aAAa;aACpB;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAClC,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC;CACF,CAAC,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"live-fixtures.js","sourceRoot":"","sources":["../src/live-fixtures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ import { type ExpectMatcherState } from '@playwright/test';
2
+ import { type Abi, type Address } from 'viem';
3
+ import { type ChainLike, type RevertTarget, type TransactionTarget } from './transactions.js';
4
+ type MatcherResult = {
5
+ pass: boolean;
6
+ message: () => string;
7
+ expected?: unknown;
8
+ actual?: unknown;
9
+ };
10
+ /** Named or positional; positional `undefined` = wildcard; values may be predicates. */
11
+ export type EventArgsExpectation = Record<string, unknown> | readonly unknown[];
12
+ export type EventMatchOptions = {
13
+ args?: EventArgsExpectation;
14
+ /** Restrict to events emitted by this contract. */
15
+ address?: Address;
16
+ /** Exact number of matching events; default passes on >= 1 (and `.not` asserts zero). */
17
+ count?: number;
18
+ timeout?: number;
19
+ };
20
+ export type BalanceChange = {
21
+ address: Address;
22
+ delta: bigint | number | string;
23
+ };
24
+ /** hardhat-chai-matchers `anyValue` parity for arg wildcards. */
25
+ export declare const anyValue: (_value: unknown) => boolean;
26
+ export declare const web3Matchers: {
27
+ toEmitEvent(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, abi: Abi, eventName: string, options?: EventMatchOptions): Promise<MatcherResult>;
28
+ toChangeBalance(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, address: Address, delta: bigint | number | string, options?: {
29
+ includeFee?: boolean;
30
+ timeout?: number;
31
+ }): Promise<MatcherResult>;
32
+ toChangeBalances(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, changes: readonly BalanceChange[], options?: {
33
+ includeFee?: boolean;
34
+ timeout?: number;
35
+ }): Promise<MatcherResult>;
36
+ toChangeTokenBalance(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, token: Address, address: Address, delta: bigint | number | string, options?: {
37
+ timeout?: number;
38
+ }): Promise<MatcherResult>;
39
+ toChangeTokenBalances(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, token: Address, changes: readonly BalanceChange[], options?: {
40
+ timeout?: number;
41
+ }): Promise<MatcherResult>;
42
+ toBeReverted(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, options?: {
43
+ timeout?: number;
44
+ }): Promise<MatcherResult>;
45
+ toBeRevertedWith(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, reason: string | RegExp, options?: {
46
+ timeout?: number;
47
+ }): Promise<MatcherResult>;
48
+ toBeRevertedWithCustomError(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, abi: Abi, errorName: string, options?: {
49
+ args?: readonly unknown[];
50
+ timeout?: number;
51
+ }): Promise<MatcherResult>;
52
+ toBeRevertedWithPanic(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, code?: bigint | number, options?: {
53
+ timeout?: number;
54
+ }): Promise<MatcherResult>;
55
+ toHaveTokenBalance(this: ExpectMatcherState, holder: Address, chain: ChainLike, token: Address, expected: bigint | number | string | ((balance: bigint) => boolean)): Promise<MatcherResult>;
56
+ };
57
+ /** Pre-extended expect carrying every web3 matcher, fully typed. */
58
+ export declare const expect: import("@playwright/test").Expect<{
59
+ toEmitEvent(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, abi: Abi, eventName: string, options?: EventMatchOptions): Promise<MatcherResult>;
60
+ toChangeBalance(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, address: Address, delta: bigint | number | string, options?: {
61
+ includeFee?: boolean;
62
+ timeout?: number;
63
+ }): Promise<MatcherResult>;
64
+ toChangeBalances(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, changes: readonly BalanceChange[], options?: {
65
+ includeFee?: boolean;
66
+ timeout?: number;
67
+ }): Promise<MatcherResult>;
68
+ toChangeTokenBalance(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, token: Address, address: Address, delta: bigint | number | string, options?: {
69
+ timeout?: number;
70
+ }): Promise<MatcherResult>;
71
+ toChangeTokenBalances(this: ExpectMatcherState, target: TransactionTarget, chain: ChainLike, token: Address, changes: readonly BalanceChange[], options?: {
72
+ timeout?: number;
73
+ }): Promise<MatcherResult>;
74
+ toBeReverted(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, options?: {
75
+ timeout?: number;
76
+ }): Promise<MatcherResult>;
77
+ toBeRevertedWith(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, reason: string | RegExp, options?: {
78
+ timeout?: number;
79
+ }): Promise<MatcherResult>;
80
+ toBeRevertedWithCustomError(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, abi: Abi, errorName: string, options?: {
81
+ args?: readonly unknown[];
82
+ timeout?: number;
83
+ }): Promise<MatcherResult>;
84
+ toBeRevertedWithPanic(this: ExpectMatcherState, target: RevertTarget, chain: ChainLike, code?: bigint | number, options?: {
85
+ timeout?: number;
86
+ }): Promise<MatcherResult>;
87
+ toHaveTokenBalance(this: ExpectMatcherState, holder: Address, chain: ChainLike, token: Address, expected: bigint | number | string | ((balance: bigint) => boolean)): Promise<MatcherResult>;
88
+ }>;
89
+ export {};
90
+ //# sourceMappingURL=matchers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../src/matchers.ts"],"names":[],"mappings":"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"}