@marigoldlabs/web3-tester 0.1.0 → 0.1.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/README.md +35 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/injected-provider.d.ts.map +1 -1
- 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 +59 -0
- package/dist/real-wallet.d.ts.map +1 -0
- package/dist/real-wallet.js +759 -0
- package/dist/real-wallet.js.map +1 -0
- package/docs/API.md +49 -1
- package/docs/CONSUMING_FROM_FJORD.md +5 -5
- package/docs/RELEASE_CHECKLIST.md +8 -2
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Web3 Tester
|
|
2
2
|
|
|
3
|
-
`@marigoldlabs/web3-tester` is a Playwright, Anvil, and
|
|
3
|
+
`@marigoldlabs/web3-tester` is a Playwright, Anvil, Viem, and MetaMask harness for Web3 end-to-end tests.
|
|
4
4
|
|
|
5
|
-
The
|
|
5
|
+
The injected fixtures test dApp behavior with a programmable EIP-1193 provider. The real-wallet adapter launches a persistent Chromium profile with MetaMask, imports or unlocks the profile when configured, and exposes wallet-side actions so consumer apps do not carry extension automation code.
|
|
6
6
|
|
|
7
7
|
## What It Provides
|
|
8
8
|
|
|
@@ -12,6 +12,7 @@ The core rule is simple: test the dApp, not MetaMask's popup UI. The harness inj
|
|
|
12
12
|
- EIP-6963 provider announcements for wallet selector testing.
|
|
13
13
|
- Viem-backed chain helpers for impersonation, balance setup, time travel, and block mining.
|
|
14
14
|
- Optional live-chain fixtures for controlled Sepolia QA with a runtime-only private key.
|
|
15
|
+
- Real MetaMask launch, profile resolution, unlock/import, dapp connection, signature confirmation, transaction confirmation, and token approval helpers.
|
|
15
16
|
- Fjord v4 QA specs and reports that document the current state of `https://v4.fjordfoundry.com`.
|
|
16
17
|
|
|
17
18
|
## Install In A Consumer App
|
|
@@ -19,7 +20,7 @@ The core rule is simple: test the dApp, not MetaMask's popup UI. The harness inj
|
|
|
19
20
|
From the Fjord v4 package, install this repo as a dev dependency:
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
|
-
npm install --save-dev
|
|
23
|
+
npm install --save-dev @marigoldlabs/web3-tester
|
|
23
24
|
```
|
|
24
25
|
|
|
25
26
|
Then import the local deterministic fixture:
|
|
@@ -52,6 +53,30 @@ test('signs in through SIWE on Sepolia', async ({ page, wallet }) => {
|
|
|
52
53
|
});
|
|
53
54
|
```
|
|
54
55
|
|
|
56
|
+
For fully in-UI real wallet tests, launch MetaMask through the package:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { launchRealWallet } from '@marigoldlabs/web3-tester/real-wallet';
|
|
60
|
+
|
|
61
|
+
const wallet = await launchRealWallet({
|
|
62
|
+
baseURL: 'https://v4.fjordfoundry.com',
|
|
63
|
+
expectedAddress: process.env.FJORD_REAL_WALLET_ADDRESS,
|
|
64
|
+
extensionPath: process.env.FJORD_REAL_WALLET_EXTENSION_PATH as string,
|
|
65
|
+
profileDir: process.env.FJORD_REAL_WALLET_PROFILE_DIR as string,
|
|
66
|
+
setup: process.env.FJORD_REAL_WALLET_PASSWORD || process.env.FJORD_REAL_WALLET_SECRET_RECOVERY_PHRASE
|
|
67
|
+
? {
|
|
68
|
+
password: process.env.FJORD_REAL_WALLET_PASSWORD,
|
|
69
|
+
seedPhrase: process.env.FJORD_REAL_WALLET_SECRET_RECOVERY_PHRASE,
|
|
70
|
+
}
|
|
71
|
+
: undefined,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
await wallet.connectToDapp();
|
|
75
|
+
await wallet.confirmSignature();
|
|
76
|
+
await wallet.confirmTransaction();
|
|
77
|
+
await wallet.close();
|
|
78
|
+
```
|
|
79
|
+
|
|
55
80
|
## Local Development
|
|
56
81
|
|
|
57
82
|
```bash
|
|
@@ -107,6 +132,11 @@ Copy `.env.example` for local reference. Do not commit real private keys.
|
|
|
107
132
|
| `FJORD_MUTATE_STATE` | unset | Must be `true` to deploy QA tokens or create sale drafts. |
|
|
108
133
|
| `FJORD_PUBLISH_SALES` | unset | Must be `true` to attempt live sale publishing. |
|
|
109
134
|
| `FJORD_ADMIN_MUTATE` | unset | Must be `true` to attempt admin mutation tests. |
|
|
135
|
+
| `FJORD_REAL_WALLET_EXTENSION_PATH` | unset | Path to the unpacked MetaMask extension for real-wallet tests. |
|
|
136
|
+
| `FJORD_REAL_WALLET_PROFILE_DIR` | unset | Persistent Chromium user-data directory, or a Chrome profile directory such as `Profile 1`. |
|
|
137
|
+
| `FJORD_REAL_WALLET_ADDRESS` | unset | Optional expected account address checked after unlock/import. |
|
|
138
|
+
| `FJORD_REAL_WALLET_PASSWORD` | unset | Optional MetaMask password used to unlock the profile. When importing from a seed without a password, web3-tester uses a deterministic test profile password. |
|
|
139
|
+
| `FJORD_REAL_WALLET_SECRET_RECOVERY_PHRASE` | unset | Optional seed phrase used when MetaMask opens on onboarding. |
|
|
110
140
|
|
|
111
141
|
## Package Surface
|
|
112
142
|
|
|
@@ -115,6 +145,7 @@ The installable package exports:
|
|
|
115
145
|
- `@marigoldlabs/web3-tester`
|
|
116
146
|
- `@marigoldlabs/web3-tester/fixtures`
|
|
117
147
|
- `@marigoldlabs/web3-tester/live-fixtures`
|
|
148
|
+
- `@marigoldlabs/web3-tester/real-wallet`
|
|
118
149
|
- `@marigoldlabs/web3-tester/anvil`
|
|
119
150
|
- `@marigoldlabs/web3-tester/mock-wallet-controller`
|
|
120
151
|
- `@marigoldlabs/web3-tester/private-key-rpc-client`
|
|
@@ -169,6 +200,7 @@ test.use({
|
|
|
169
200
|
|
|
170
201
|
- Local tests use deterministic Anvil accounts only.
|
|
171
202
|
- Live tests require explicit environment variables and never store private keys in source.
|
|
203
|
+
- Real-wallet tests use a persistent browser profile and keep extension-side automation inside this package.
|
|
172
204
|
- Mutation tests are skipped unless their opt-in flag is set.
|
|
173
205
|
- Published reports redact secrets and record transaction hashes only when useful for auditability.
|
|
174
206
|
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAE5E,eAAO,MAAM,aAAa,GACxB,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,OAAO,OAAO,KACb,oBAOF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,OAAO,OAAO,KAAG,mBAiBlD,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ export { AnvilInstance, ChainController } from './anvil.js';
|
|
|
2
2
|
export { test, expect } from './fixtures.js';
|
|
3
3
|
export { MockWalletController } from './mock-wallet-controller.js';
|
|
4
4
|
export { PrivateKeyRpcClient } from './private-key-rpc-client.js';
|
|
5
|
+
export { launchRealWallet, resolveRealWalletProfile } from './real-wallet.js';
|
|
5
6
|
export type { AnvilOptions, AnvilSnapshotId, AnvilViemClient, ChainControllerOptions, } from './anvil.js';
|
|
6
7
|
export type { MockWalletControllerOptions, RejectionRule, } from './mock-wallet-controller.js';
|
|
7
8
|
export type { JsonRpcRequest, MockWalletConfig, WalletProviderInfo, } from './types.js';
|
|
8
9
|
export type { PrivateKeyRpcClientOptions } from './private-key-rpc-client.js';
|
|
10
|
+
export type { RealWalletController, RealWalletGasSettings, RealWalletLaunchOptions, RealWalletProfile, RealWalletSession, RealWalletSetup, } from './real-wallet.js';
|
|
9
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","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,YAAY,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,2BAA2B,EAC3B,aAAa,GACd,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","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;AAC9E,YAAY,EACV,YAAY,EACZ,eAAe,EACf,eAAe,EACf,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,2BAA2B,EAC3B,aAAa,GACd,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AAC9E,YAAY,EACV,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { AnvilInstance, ChainController } from './anvil.js';
|
|
|
2
2
|
export { test, expect } from './fixtures.js';
|
|
3
3
|
export { MockWalletController } from './mock-wallet-controller.js';
|
|
4
4
|
export { PrivateKeyRpcClient } from './private-key-rpc-client.js';
|
|
5
|
+
export { launchRealWallet, resolveRealWalletProfile } from './real-wallet.js';
|
|
5
6
|
//# 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"}
|
|
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 +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,
|
|
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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RealWalletSetup } from './real-wallet.js';
|
|
2
|
+
export declare const DEFAULT_WALLET_PASSWORD = "web3-tester-wallet";
|
|
3
|
+
export declare function passwordForSetup(setup: RealWalletSetup | undefined): string | undefined;
|
|
4
|
+
//# sourceMappingURL=real-wallet-setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet-setup.d.ts","sourceRoot":"","sources":["../src/real-wallet-setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAE5D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,sBAElE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet-setup.js","sourceRoot":"","sources":["../src/real-wallet-setup.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D,MAAM,UAAU,gBAAgB,CAAC,KAAkC;IACjE,OAAO,KAAK,EAAE,QAAQ,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACtF,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type BrowserContext } from '@playwright/test';
|
|
2
|
+
export type RealWalletProfile = {
|
|
3
|
+
profileDirectory?: string;
|
|
4
|
+
userDataDir: string;
|
|
5
|
+
};
|
|
6
|
+
export type RealWalletSetup = {
|
|
7
|
+
password?: string;
|
|
8
|
+
seedPhrase?: string;
|
|
9
|
+
};
|
|
10
|
+
export type RealWalletGasSettings = 'site' | 'low' | 'market' | 'aggressive' | {
|
|
11
|
+
gasLimit?: number;
|
|
12
|
+
maxBaseFee: number;
|
|
13
|
+
priorityFee: number;
|
|
14
|
+
};
|
|
15
|
+
export type RealWalletLaunchOptions = {
|
|
16
|
+
baseURL?: string;
|
|
17
|
+
expectedAddress?: string;
|
|
18
|
+
extensionName?: string;
|
|
19
|
+
extensionPath: string;
|
|
20
|
+
headless?: boolean;
|
|
21
|
+
profileDir: string;
|
|
22
|
+
setup?: RealWalletSetup;
|
|
23
|
+
slowMo?: number;
|
|
24
|
+
};
|
|
25
|
+
export type RealWalletController = {
|
|
26
|
+
approveTokenPermission(options?: {
|
|
27
|
+
gasSetting?: RealWalletGasSettings;
|
|
28
|
+
spendLimit?: 'max' | number;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
confirmSignature(): Promise<void>;
|
|
31
|
+
confirmTransaction(options?: {
|
|
32
|
+
gasSetting?: RealWalletGasSettings;
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
connectToDapp(accounts?: string[]): Promise<void>;
|
|
35
|
+
getAccountAddress(): Promise<string>;
|
|
36
|
+
rejectSignature(): Promise<void>;
|
|
37
|
+
rejectTransaction(): Promise<void>;
|
|
38
|
+
};
|
|
39
|
+
export type RealWalletSession = {
|
|
40
|
+
approveTokenPermission(options?: {
|
|
41
|
+
gasSetting?: RealWalletGasSettings;
|
|
42
|
+
spendLimit?: 'max' | number;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
close(): Promise<void>;
|
|
45
|
+
confirmSignature(): Promise<void>;
|
|
46
|
+
confirmTransaction(options?: {
|
|
47
|
+
gasSetting?: RealWalletGasSettings;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
connectToDapp(accounts?: string[]): Promise<void>;
|
|
50
|
+
context: BrowserContext;
|
|
51
|
+
extensionId: string;
|
|
52
|
+
getAccountAddress(): Promise<string>;
|
|
53
|
+
rejectSignature(): Promise<void>;
|
|
54
|
+
rejectTransaction(): Promise<void>;
|
|
55
|
+
wallet: RealWalletController;
|
|
56
|
+
};
|
|
57
|
+
export declare function resolveRealWalletProfile(profileDir: string): RealWalletProfile;
|
|
58
|
+
export declare function launchRealWallet(options: RealWalletLaunchOptions): Promise<RealWalletSession>;
|
|
59
|
+
//# sourceMappingURL=real-wallet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet.d.ts","sourceRoot":"","sources":["../src/real-wallet.ts"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,cAAc,EAA2B,MAAM,kBAAkB,CAAC;AAG1F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B,MAAM,GACN,KAAK,GACL,QAAQ,GACR,YAAY,GACZ;IACE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEN,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,sBAAsB,CAAC,OAAO,CAAC,EAAE;QAC/B,UAAU,CAAC,EAAE,qBAAqB,CAAC;QACnC,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,kBAAkB,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,qBAAqB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,sBAAsB,CAAC,OAAO,CAAC,EAAE;QAC/B,UAAU,CAAC,EAAE,qBAAqB,CAAC;QACnC,UAAU,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,kBAAkB,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,qBAAqB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;CAC9B,CAAC;AAgBF,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAW9E;AAq4BD,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgDnG"}
|
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { chromium } from '@playwright/test';
|
|
4
|
+
import { passwordForSetup } from './real-wallet-setup.js';
|
|
5
|
+
const DEFAULT_EXTENSION_NAME = 'MetaMask';
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
7
|
+
const SHORT_TIMEOUT_MS = 2_000;
|
|
8
|
+
const LOCATOR_PROBE_MS = 250;
|
|
9
|
+
const testId = (id) => `[data-testid="${id}"]`;
|
|
10
|
+
function extensionUrl(extensionId, page = 'home.html') {
|
|
11
|
+
return `chrome-extension://${extensionId}/${page}`;
|
|
12
|
+
}
|
|
13
|
+
function extensionIdFromUrl(url) {
|
|
14
|
+
return /^chrome-extension:\/\/([^/]+)\//.exec(url)?.[1];
|
|
15
|
+
}
|
|
16
|
+
export function resolveRealWalletProfile(profileDir) {
|
|
17
|
+
const resolved = path.resolve(profileDir);
|
|
18
|
+
const profileDirectory = path.basename(resolved);
|
|
19
|
+
const userDataDir = path.dirname(resolved);
|
|
20
|
+
const looksLikeChromeProfile = /^(?:Default|Profile \d+)$/.test(profileDirectory);
|
|
21
|
+
if (looksLikeChromeProfile && fs.existsSync(path.join(userDataDir, 'Local State'))) {
|
|
22
|
+
return { profileDirectory, userDataDir };
|
|
23
|
+
}
|
|
24
|
+
return { userDataDir: resolved };
|
|
25
|
+
}
|
|
26
|
+
async function isVisible(locator, timeout = SHORT_TIMEOUT_MS) {
|
|
27
|
+
await locator.first().waitFor({ state: 'visible', timeout });
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
async function isHidden(locator, timeout = SHORT_TIMEOUT_MS) {
|
|
31
|
+
await locator.first().waitFor({ state: 'hidden', timeout });
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
function wait(ms) {
|
|
35
|
+
return new Promise((resolve) => {
|
|
36
|
+
setTimeout(resolve, ms);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function findVisibleLocator(locators, timeout = SHORT_TIMEOUT_MS, options = {}) {
|
|
40
|
+
const deadline = Date.now() + timeout;
|
|
41
|
+
do {
|
|
42
|
+
const remaining = Math.max(deadline - Date.now(), 1);
|
|
43
|
+
for (const locator of locators) {
|
|
44
|
+
const target = locator.first();
|
|
45
|
+
const probeTimeout = Math.min(LOCATOR_PROBE_MS, remaining);
|
|
46
|
+
if (!(await isVisible(target, probeTimeout).catch(() => false)))
|
|
47
|
+
continue;
|
|
48
|
+
if (options.requireEnabled && !(await target.isEnabled({ timeout: 0 }).catch(() => false)))
|
|
49
|
+
continue;
|
|
50
|
+
return target;
|
|
51
|
+
}
|
|
52
|
+
const delay = Math.min(LOCATOR_PROBE_MS, Math.max(deadline - Date.now(), 0));
|
|
53
|
+
if (delay > 0)
|
|
54
|
+
await wait(delay);
|
|
55
|
+
} while (Date.now() < deadline);
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
async function clickFirstVisible(locators, timeout = SHORT_TIMEOUT_MS, options = { requireEnabled: true }) {
|
|
59
|
+
const target = await findVisibleLocator(locators, timeout, {
|
|
60
|
+
requireEnabled: options.requireEnabled ?? true,
|
|
61
|
+
});
|
|
62
|
+
if (!target)
|
|
63
|
+
return false;
|
|
64
|
+
await target.click({ force: options.force, timeout });
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
async function fillFirstVisible(locators, value, timeout = DEFAULT_TIMEOUT_MS) {
|
|
68
|
+
const target = await findVisibleLocator(locators, timeout);
|
|
69
|
+
if (!target)
|
|
70
|
+
return false;
|
|
71
|
+
await target.fill(value);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
async function startSeedPhraseWordGrid(target, firstWord) {
|
|
75
|
+
await target.fill(firstWord);
|
|
76
|
+
await wait(250);
|
|
77
|
+
await target.press('Space');
|
|
78
|
+
}
|
|
79
|
+
function metaMaskOnboardingLocators(page) {
|
|
80
|
+
return [
|
|
81
|
+
page.locator(testId('onboarding-import-wallet')),
|
|
82
|
+
page.locator(testId('onboarding-import-with-srp-button')),
|
|
83
|
+
page.locator(testId('onboarding-create-wallet')),
|
|
84
|
+
page.locator(testId('onboarding-terms-checkbox')),
|
|
85
|
+
page.locator(testId('srp-input-import__srp-note')),
|
|
86
|
+
page.locator(testId('import-srp-confirm')),
|
|
87
|
+
page.locator(testId('create-password-new-input')),
|
|
88
|
+
page.locator(testId('create-password-confirm-input')),
|
|
89
|
+
page.getByRole('button', { name: 'I have an existing wallet' }),
|
|
90
|
+
page.getByRole('button', { name: 'Create a new wallet' }),
|
|
91
|
+
page.getByRole('button', { name: 'Import using Secret Recovery Phrase' }),
|
|
92
|
+
page.getByRole('heading', { name: 'Import a wallet' }),
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
async function isMetaMaskOnboardingVisible(page) {
|
|
96
|
+
return page.url().includes('/home.html#/onboarding') || Boolean(await findVisibleLocator(metaMaskOnboardingLocators(page), SHORT_TIMEOUT_MS));
|
|
97
|
+
}
|
|
98
|
+
function metaMaskSeedPhraseInputLocators(page) {
|
|
99
|
+
return [
|
|
100
|
+
page.locator(testId('srp-input-import__srp-note')),
|
|
101
|
+
page.locator('#first-word-input-text-area'),
|
|
102
|
+
page.locator('textarea[name="seedPhrase"]'),
|
|
103
|
+
page.locator('textarea').first(),
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
function metaMaskSeedPhraseImportLocators(page) {
|
|
107
|
+
return [
|
|
108
|
+
...metaMaskSeedPhraseInputLocators(page),
|
|
109
|
+
page.getByRole('heading', { name: 'Import a wallet' }),
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
function metaMaskSeedPhraseWordInputs(page) {
|
|
113
|
+
return page.locator('.srp-input-import__words-list input, input[id^="import-srp__srp-word-"], input[data-testid^="import-srp__srp-word-"], input[name^="srp-word-"]');
|
|
114
|
+
}
|
|
115
|
+
async function isSeedPhraseConfirmEnabled(page, timeout = SHORT_TIMEOUT_MS) {
|
|
116
|
+
return Boolean(await findVisibleLocator([page.locator(testId('import-srp-confirm'))], timeout, {
|
|
117
|
+
requireEnabled: true,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
async function fillMetaMaskSeedPhraseWordGrid(page, words, startIndex = 0) {
|
|
121
|
+
const wordInputs = metaMaskSeedPhraseWordInputs(page);
|
|
122
|
+
for (let index = startIndex; index < words.length; index += 1) {
|
|
123
|
+
const input = wordInputs.nth(index);
|
|
124
|
+
await input.waitFor({ state: 'visible', timeout: DEFAULT_TIMEOUT_MS });
|
|
125
|
+
await input.fill(words[index]);
|
|
126
|
+
if (index < words.length - 1) {
|
|
127
|
+
await input.press('Space');
|
|
128
|
+
await wait(100);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async function isMetaMaskSeedPhraseImportVisible(page) {
|
|
133
|
+
return (page.url().includes('/onboarding/import-with-recovery-phrase') ||
|
|
134
|
+
Boolean(await findVisibleLocator(metaMaskSeedPhraseImportLocators(page), SHORT_TIMEOUT_MS)));
|
|
135
|
+
}
|
|
136
|
+
async function isMetaMaskCreatePasswordVisible(page) {
|
|
137
|
+
return (page.url().includes('/onboarding/create-password') ||
|
|
138
|
+
Boolean(await findVisibleLocator([page.locator(testId('create-password-new-input')), page.locator('input[type="password"]').nth(0)], SHORT_TIMEOUT_MS)));
|
|
139
|
+
}
|
|
140
|
+
async function waitForMetaMaskReady(page) {
|
|
141
|
+
await page.waitForLoadState('domcontentloaded', { timeout: DEFAULT_TIMEOUT_MS }).catch(() => undefined);
|
|
142
|
+
await isHidden(page.locator('.spinner, .loading-overlay, [data-testid="loading-overlay"]'), DEFAULT_TIMEOUT_MS).catch(() => undefined);
|
|
143
|
+
}
|
|
144
|
+
function metaMaskUnlockPasswordLocators(page) {
|
|
145
|
+
return [page.locator(testId('unlock-password')), page.locator('input[type="password"]').first()];
|
|
146
|
+
}
|
|
147
|
+
function metaMaskUnlockSubmitLocators(page) {
|
|
148
|
+
return [page.locator(testId('unlock-submit')), page.getByRole('button', { name: 'Unlock' })];
|
|
149
|
+
}
|
|
150
|
+
async function isMetaMaskUnlockVisible(page) {
|
|
151
|
+
return Boolean(await findVisibleLocator(metaMaskUnlockPasswordLocators(page), SHORT_TIMEOUT_MS));
|
|
152
|
+
}
|
|
153
|
+
async function closeMetaMaskOverlay(page) {
|
|
154
|
+
await clickFirstVisible([
|
|
155
|
+
page.locator(testId('popover-close')),
|
|
156
|
+
page.locator('.mm-modal-content .mm-modal-header button').first(),
|
|
157
|
+
], SHORT_TIMEOUT_MS).catch(() => false);
|
|
158
|
+
}
|
|
159
|
+
function metaMaskAccountMenuLocators(page) {
|
|
160
|
+
return [
|
|
161
|
+
page.locator(testId('account-options-menu-button')),
|
|
162
|
+
page.locator(testId('account-menu-icon')),
|
|
163
|
+
page.getByRole('button', { name: /Account options|Account menu/i }),
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
function metaMaskPromptActions(page) {
|
|
167
|
+
return [
|
|
168
|
+
page.locator(testId('onboarding-complete-done')),
|
|
169
|
+
page.locator(testId('metametrics-no-thanks')),
|
|
170
|
+
page.getByRole('button', { name: 'No thanks' }),
|
|
171
|
+
page.getByRole('button', { name: 'Done' }),
|
|
172
|
+
page.getByRole('button', { name: 'Skip' }),
|
|
173
|
+
page.getByRole('button', { name: 'Continue' }),
|
|
174
|
+
page.getByRole('button', { name: 'Open wallet' }),
|
|
175
|
+
page.getByRole('button', { name: 'Maybe later' }),
|
|
176
|
+
page.getByRole('link', { name: 'Maybe later' }),
|
|
177
|
+
];
|
|
178
|
+
}
|
|
179
|
+
function metaMaskTextPromptActions(page) {
|
|
180
|
+
return [
|
|
181
|
+
page.getByText('Maybe later', { exact: true }),
|
|
182
|
+
page.getByText('No thanks', { exact: true }),
|
|
183
|
+
];
|
|
184
|
+
}
|
|
185
|
+
async function clickMetaMaskPromptAction(page, timeout = SHORT_TIMEOUT_MS) {
|
|
186
|
+
return ((await clickFirstVisible(metaMaskPromptActions(page), timeout)) ||
|
|
187
|
+
(await clickFirstVisible(metaMaskTextPromptActions(page), timeout, { requireEnabled: false })));
|
|
188
|
+
}
|
|
189
|
+
async function waitForMetaMaskHome(page, timeout = DEFAULT_TIMEOUT_MS) {
|
|
190
|
+
const deadline = Date.now() + timeout;
|
|
191
|
+
do {
|
|
192
|
+
await waitForMetaMaskReady(page);
|
|
193
|
+
if (await findVisibleLocator(metaMaskAccountMenuLocators(page), SHORT_TIMEOUT_MS))
|
|
194
|
+
return true;
|
|
195
|
+
if (await clickMetaMaskPromptAction(page, SHORT_TIMEOUT_MS))
|
|
196
|
+
continue;
|
|
197
|
+
const delay = Math.min(LOCATOR_PROBE_MS, Math.max(deadline - Date.now(), 0));
|
|
198
|
+
if (delay > 0)
|
|
199
|
+
await wait(delay);
|
|
200
|
+
} while (Date.now() < deadline);
|
|
201
|
+
return Boolean(await findVisibleLocator(metaMaskAccountMenuLocators(page), SHORT_TIMEOUT_MS));
|
|
202
|
+
}
|
|
203
|
+
async function discoverExtensionIdFromRuntime(context) {
|
|
204
|
+
const workerId = context.serviceWorkers().map((worker) => extensionIdFromUrl(worker.url())).find(Boolean);
|
|
205
|
+
if (workerId)
|
|
206
|
+
return workerId;
|
|
207
|
+
const pageId = context.pages().map((page) => extensionIdFromUrl(page.url())).find(Boolean);
|
|
208
|
+
if (pageId)
|
|
209
|
+
return pageId;
|
|
210
|
+
const worker = await context.waitForEvent('serviceworker', { timeout: 10_000 }).catch(() => undefined);
|
|
211
|
+
if (worker) {
|
|
212
|
+
const extensionId = extensionIdFromUrl(worker.url());
|
|
213
|
+
if (extensionId)
|
|
214
|
+
return extensionId;
|
|
215
|
+
}
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
async function discoverExtensionIdFromManagementApi(context, extensionName) {
|
|
219
|
+
const page = await context.newPage();
|
|
220
|
+
try {
|
|
221
|
+
await page.goto('chrome://extensions', { waitUntil: 'domcontentloaded' });
|
|
222
|
+
const extensions = await page.evaluate(() => {
|
|
223
|
+
const chromeApi = globalThis;
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
if (!chromeApi.chrome?.management?.getAll) {
|
|
226
|
+
reject(new Error('chrome.management.getAll is not available on chrome://extensions.'));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
chromeApi.chrome.management.getAll((items) => {
|
|
230
|
+
const error = chromeApi.chrome?.runtime?.lastError;
|
|
231
|
+
if (error)
|
|
232
|
+
reject(new Error(error.message ?? 'Unable to enumerate Chrome extensions.'));
|
|
233
|
+
else
|
|
234
|
+
resolve(items);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
const exact = extensions.find((extension) => extension.name.toLowerCase() === extensionName.toLowerCase());
|
|
239
|
+
if (exact)
|
|
240
|
+
return exact.id;
|
|
241
|
+
const available = extensions.map((extension) => extension.name).sort().join(', ');
|
|
242
|
+
throw new Error(`Unable to find extension "${extensionName}". Installed extensions: ${available || 'none'}.`);
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
await page.close().catch(() => undefined);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function getExtensionId(context, extensionName) {
|
|
249
|
+
return ((await discoverExtensionIdFromRuntime(context)) ??
|
|
250
|
+
(await discoverExtensionIdFromManagementApi(context, extensionName)));
|
|
251
|
+
}
|
|
252
|
+
async function openExtensionHome(context, extensionId) {
|
|
253
|
+
const homeUrl = extensionUrl(extensionId);
|
|
254
|
+
const existing = context.pages().find((page) => page.url().startsWith(homeUrl));
|
|
255
|
+
const page = existing ?? (await context.newPage());
|
|
256
|
+
if (page.url() !== homeUrl)
|
|
257
|
+
await page.goto(homeUrl);
|
|
258
|
+
await waitForMetaMaskReady(page);
|
|
259
|
+
return page;
|
|
260
|
+
}
|
|
261
|
+
function notificationUrlPrefix(extensionId) {
|
|
262
|
+
return extensionUrl(extensionId, 'notification.html');
|
|
263
|
+
}
|
|
264
|
+
function extensionPageUrlPrefix(extensionId) {
|
|
265
|
+
return `chrome-extension://${extensionId}/`;
|
|
266
|
+
}
|
|
267
|
+
function metaMaskActionContentLocators(page) {
|
|
268
|
+
return [
|
|
269
|
+
page.getByRole('heading', {
|
|
270
|
+
name: /Spending cap request|Transaction request|Signature request|Sign-in request|Permission request/i,
|
|
271
|
+
}),
|
|
272
|
+
page.getByText(/Spending cap request|Transaction request|Signature request|Sign-in request|Permission request|This site wants permission/i),
|
|
273
|
+
];
|
|
274
|
+
}
|
|
275
|
+
function metaMaskActionControlLocators(page) {
|
|
276
|
+
return [
|
|
277
|
+
page.locator(testId('confirm-footer-button')),
|
|
278
|
+
page.locator(testId('confirmation-submit-button')),
|
|
279
|
+
page.locator(testId('page-container-footer-next')),
|
|
280
|
+
page.locator(testId('request-signature__sign')),
|
|
281
|
+
page.locator(testId('signature-sign-button')),
|
|
282
|
+
page.locator(testId('custom-spending-cap-input')),
|
|
283
|
+
page.locator('.set-approval-for-all-warning__footer__approve-button'),
|
|
284
|
+
page.getByRole('button', { name: /^(Confirm|Connect|Next|Approve|Sign)$/i }),
|
|
285
|
+
];
|
|
286
|
+
}
|
|
287
|
+
function metaMaskActionLocators(page) {
|
|
288
|
+
return [...metaMaskActionContentLocators(page), ...metaMaskActionControlLocators(page)];
|
|
289
|
+
}
|
|
290
|
+
async function findMetaMaskActionPage(context, extensionId) {
|
|
291
|
+
const extensionPrefix = extensionPageUrlPrefix(extensionId);
|
|
292
|
+
let controlOnlyPage;
|
|
293
|
+
for (const page of [...context.pages()].reverse()) {
|
|
294
|
+
if (page.isClosed() || !page.url().startsWith(extensionPrefix))
|
|
295
|
+
continue;
|
|
296
|
+
await page.waitForLoadState('domcontentloaded', { timeout: LOCATOR_PROBE_MS }).catch(() => undefined);
|
|
297
|
+
if (await findVisibleLocator(metaMaskActionContentLocators(page), LOCATOR_PROBE_MS, { requireEnabled: false })) {
|
|
298
|
+
return page;
|
|
299
|
+
}
|
|
300
|
+
if (!controlOnlyPage &&
|
|
301
|
+
(await findVisibleLocator(metaMaskActionControlLocators(page), LOCATOR_PROBE_MS, { requireEnabled: false }))) {
|
|
302
|
+
controlOnlyPage = page;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return controlOnlyPage;
|
|
306
|
+
}
|
|
307
|
+
async function findMetaMaskLockedPage(context, extensionId) {
|
|
308
|
+
const extensionPrefix = extensionPageUrlPrefix(extensionId);
|
|
309
|
+
for (const page of [...context.pages()].reverse()) {
|
|
310
|
+
if (page.isClosed() || !page.url().startsWith(extensionPrefix))
|
|
311
|
+
continue;
|
|
312
|
+
await page.waitForLoadState('domcontentloaded', { timeout: LOCATOR_PROBE_MS }).catch(() => undefined);
|
|
313
|
+
if (await isMetaMaskUnlockVisible(page))
|
|
314
|
+
return page;
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
317
|
+
}
|
|
318
|
+
function findMetaMaskNotificationPage(context, extensionId) {
|
|
319
|
+
const notificationPrefix = notificationUrlPrefix(extensionId);
|
|
320
|
+
return [...context.pages()]
|
|
321
|
+
.reverse()
|
|
322
|
+
.find((page) => !page.isClosed() && page.url().startsWith(notificationPrefix));
|
|
323
|
+
}
|
|
324
|
+
async function getNotificationPage(context, extensionId, timeout = DEFAULT_TIMEOUT_MS) {
|
|
325
|
+
const prefix = notificationUrlPrefix(extensionId);
|
|
326
|
+
const extensionPrefix = extensionPageUrlPrefix(extensionId);
|
|
327
|
+
const startedAt = Date.now();
|
|
328
|
+
let page = await findMetaMaskActionPage(context, extensionId);
|
|
329
|
+
let notificationPage = findMetaMaskNotificationPage(context, extensionId);
|
|
330
|
+
while (!page && Date.now() - startedAt < timeout) {
|
|
331
|
+
const remaining = Math.max(timeout - (Date.now() - startedAt), 1_000);
|
|
332
|
+
const candidate = await context.waitForEvent('page', { timeout: Math.min(remaining, 1_000) }).catch(() => undefined);
|
|
333
|
+
if (candidate) {
|
|
334
|
+
await candidate
|
|
335
|
+
.waitForURL((url) => url.href.startsWith(prefix) || url.href.startsWith(extensionPrefix), {
|
|
336
|
+
timeout: Math.min(remaining, 5_000),
|
|
337
|
+
})
|
|
338
|
+
.catch(() => undefined);
|
|
339
|
+
}
|
|
340
|
+
page = await findMetaMaskActionPage(context, extensionId);
|
|
341
|
+
notificationPage = findMetaMaskNotificationPage(context, extensionId) ?? notificationPage;
|
|
342
|
+
}
|
|
343
|
+
page ??= notificationPage;
|
|
344
|
+
if (!page)
|
|
345
|
+
throw new Error('Timed out waiting for MetaMask notification window.');
|
|
346
|
+
await waitForMetaMaskReady(page);
|
|
347
|
+
await page.bringToFront().catch(() => undefined);
|
|
348
|
+
return page;
|
|
349
|
+
}
|
|
350
|
+
function shortAddress(address) {
|
|
351
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`.toLowerCase();
|
|
352
|
+
}
|
|
353
|
+
async function pageContainsAddress(page, address) {
|
|
354
|
+
const normalizedAddress = address.toLowerCase();
|
|
355
|
+
const text = ((await page.locator('body').textContent({ timeout: SHORT_TIMEOUT_MS }).catch(() => '')) ?? '')
|
|
356
|
+
.replace(/\s+/g, '')
|
|
357
|
+
.toLowerCase();
|
|
358
|
+
return (text.includes(normalizedAddress) ||
|
|
359
|
+
(text.includes(normalizedAddress.slice(0, 6)) && text.includes(normalizedAddress.slice(-4))) ||
|
|
360
|
+
(text.includes(normalizedAddress.slice(0, 7)) && text.includes(normalizedAddress.slice(-5))));
|
|
361
|
+
}
|
|
362
|
+
async function fillSeedPhrase(page, seedPhrase) {
|
|
363
|
+
const words = seedPhrase.trim().split(/\s+/);
|
|
364
|
+
if (words.length < 12) {
|
|
365
|
+
throw new Error('setup.seedPhrase must contain at least 12 words.');
|
|
366
|
+
}
|
|
367
|
+
const singleInput = await findVisibleLocator(metaMaskSeedPhraseInputLocators(page), DEFAULT_TIMEOUT_MS);
|
|
368
|
+
if (singleInput) {
|
|
369
|
+
await startSeedPhraseWordGrid(singleInput, words[0]);
|
|
370
|
+
await fillMetaMaskSeedPhraseWordGrid(page, words, 1);
|
|
371
|
+
if (await isSeedPhraseConfirmEnabled(page)) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const wordInputs = metaMaskSeedPhraseWordInputs(page);
|
|
376
|
+
if ((await wordInputs.count()) >= words.length) {
|
|
377
|
+
await fillMetaMaskSeedPhraseWordGrid(page, words);
|
|
378
|
+
if (await isSeedPhraseConfirmEnabled(page))
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const textboxes = page.getByRole('textbox');
|
|
382
|
+
if ((await textboxes.count()) >= words.length) {
|
|
383
|
+
for (const [index, word] of words.entries()) {
|
|
384
|
+
await textboxes.nth(index).fill(word);
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
throw new Error('Unable to find MetaMask seed phrase input fields.');
|
|
389
|
+
}
|
|
390
|
+
async function importMetaMaskWallet(page, setup) {
|
|
391
|
+
const password = passwordForSetup(setup);
|
|
392
|
+
if (!password || !setup.seedPhrase) {
|
|
393
|
+
throw new Error('MetaMask is on onboarding. Provide setup.seedPhrase to import a wallet through web3-tester, or use a preconfigured persistent profile.');
|
|
394
|
+
}
|
|
395
|
+
await page.bringToFront().catch(() => undefined);
|
|
396
|
+
const terms = page.locator(testId('onboarding-terms-checkbox'));
|
|
397
|
+
if (await isVisible(terms, SHORT_TIMEOUT_MS).catch(() => false)) {
|
|
398
|
+
await terms.check();
|
|
399
|
+
}
|
|
400
|
+
if (!(await isMetaMaskSeedPhraseImportVisible(page)) && !(await isMetaMaskCreatePasswordVisible(page))) {
|
|
401
|
+
const importStarted = await clickFirstVisible([
|
|
402
|
+
page.locator(testId('onboarding-import-wallet')),
|
|
403
|
+
page.locator(testId('onboarding-import-with-srp-button')),
|
|
404
|
+
page.getByRole('button', { name: 'I have an existing wallet' }),
|
|
405
|
+
page.getByRole('button', { name: 'Import an existing wallet' }),
|
|
406
|
+
page.getByRole('button', { name: 'Import wallet' }),
|
|
407
|
+
], DEFAULT_TIMEOUT_MS);
|
|
408
|
+
if (!importStarted)
|
|
409
|
+
throw new Error('Unable to start MetaMask wallet import flow.');
|
|
410
|
+
await waitForMetaMaskReady(page);
|
|
411
|
+
await clickFirstVisible([
|
|
412
|
+
page.locator(testId('onboarding-import-with-srp-button')),
|
|
413
|
+
page.getByRole('button', { name: 'Import using Secret Recovery Phrase' }),
|
|
414
|
+
], 5_000);
|
|
415
|
+
await clickFirstVisible([
|
|
416
|
+
page.locator(testId('metametrics-no-thanks')),
|
|
417
|
+
page.locator(testId('metametrics-i-agree')),
|
|
418
|
+
page.getByRole('button', { name: 'No thanks' }),
|
|
419
|
+
page.getByRole('button', { name: 'I agree' }),
|
|
420
|
+
], SHORT_TIMEOUT_MS);
|
|
421
|
+
}
|
|
422
|
+
if (!(await isMetaMaskCreatePasswordVisible(page))) {
|
|
423
|
+
await fillSeedPhrase(page, setup.seedPhrase);
|
|
424
|
+
const seedConfirmed = await clickFirstVisible([
|
|
425
|
+
page.locator(testId('import-srp-confirm')),
|
|
426
|
+
page.getByRole('button', { name: 'Confirm Secret Recovery Phrase' }),
|
|
427
|
+
page.getByRole('button', { name: 'Confirm' }),
|
|
428
|
+
], DEFAULT_TIMEOUT_MS);
|
|
429
|
+
if (!seedConfirmed)
|
|
430
|
+
throw new Error('Unable to confirm MetaMask seed phrase import.');
|
|
431
|
+
}
|
|
432
|
+
const passwordFilled = await fillFirstVisible([page.locator(testId('create-password-new-input')), page.locator('input[type="password"]').nth(0)], password);
|
|
433
|
+
if (!passwordFilled)
|
|
434
|
+
throw new Error('Unable to find MetaMask password input.');
|
|
435
|
+
const confirmationPasswordFilled = await fillFirstVisible([page.locator(testId('create-password-confirm-input')), page.locator('input[type="password"]').nth(1)], password);
|
|
436
|
+
if (!confirmationPasswordFilled)
|
|
437
|
+
throw new Error('Unable to find MetaMask password confirmation input.');
|
|
438
|
+
const passwordTerms = page.locator(testId('create-password-terms'));
|
|
439
|
+
if (await isVisible(passwordTerms, SHORT_TIMEOUT_MS).catch(() => false)) {
|
|
440
|
+
await passwordTerms.check();
|
|
441
|
+
}
|
|
442
|
+
const passwordSubmitted = await clickFirstVisible([page.locator(testId('create-password-submit')), page.getByRole('button', { name: 'Import my wallet' })], DEFAULT_TIMEOUT_MS);
|
|
443
|
+
if (!passwordSubmitted)
|
|
444
|
+
throw new Error('Unable to submit MetaMask import password.');
|
|
445
|
+
await finishMetaMaskOnboarding(page);
|
|
446
|
+
}
|
|
447
|
+
async function unlockMetaMask(page, password) {
|
|
448
|
+
const passwordFilled = await fillFirstVisible(metaMaskUnlockPasswordLocators(page), password, DEFAULT_TIMEOUT_MS);
|
|
449
|
+
if (!passwordFilled)
|
|
450
|
+
throw new Error('Unable to find MetaMask unlock password input.');
|
|
451
|
+
const unlocked = await clickFirstVisible(metaMaskUnlockSubmitLocators(page), DEFAULT_TIMEOUT_MS);
|
|
452
|
+
if (!unlocked)
|
|
453
|
+
throw new Error('Unable to submit MetaMask unlock form.');
|
|
454
|
+
await waitForMetaMaskReady(page);
|
|
455
|
+
}
|
|
456
|
+
async function unlockMetaMaskIfNeeded(page, password) {
|
|
457
|
+
if (!(await isMetaMaskUnlockVisible(page)))
|
|
458
|
+
return false;
|
|
459
|
+
if (!password) {
|
|
460
|
+
throw new Error('MetaMask profile is locked. Provide setup.password to unlock through web3-tester, or unlock the persistent profile before running.');
|
|
461
|
+
}
|
|
462
|
+
await page.bringToFront().catch(() => undefined);
|
|
463
|
+
await unlockMetaMask(page, password);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
async function finishMetaMaskOnboarding(page) {
|
|
467
|
+
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
468
|
+
await waitForMetaMaskReady(page);
|
|
469
|
+
const actions = [...metaMaskPromptActions(page), ...metaMaskTextPromptActions(page)];
|
|
470
|
+
const onSetupScreen = page.url().includes('/home.html#/onboarding') ||
|
|
471
|
+
Boolean(await findVisibleLocator(actions, SHORT_TIMEOUT_MS));
|
|
472
|
+
if (!onSetupScreen)
|
|
473
|
+
return;
|
|
474
|
+
const advanced = await clickMetaMaskPromptAction(page, 5_000);
|
|
475
|
+
if (!advanced)
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
if (await waitForMetaMaskHome(page, 10_000))
|
|
479
|
+
return;
|
|
480
|
+
if (page.url().includes('/home.html#/onboarding') ||
|
|
481
|
+
(await findVisibleLocator([...metaMaskPromptActions(page), ...metaMaskTextPromptActions(page)], SHORT_TIMEOUT_MS))) {
|
|
482
|
+
throw new Error('MetaMask wallet import did not complete onboarding.');
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
class MetaMaskRealWallet {
|
|
486
|
+
context;
|
|
487
|
+
homePage;
|
|
488
|
+
extensionId;
|
|
489
|
+
expectedAddress;
|
|
490
|
+
walletPassword;
|
|
491
|
+
constructor(context, homePage, extensionId, expectedAddress, walletPassword) {
|
|
492
|
+
this.context = context;
|
|
493
|
+
this.homePage = homePage;
|
|
494
|
+
this.extensionId = extensionId;
|
|
495
|
+
this.expectedAddress = expectedAddress;
|
|
496
|
+
this.walletPassword = walletPassword;
|
|
497
|
+
}
|
|
498
|
+
async approveTokenPermission(options) {
|
|
499
|
+
const page = await this.notificationPage();
|
|
500
|
+
if (options?.spendLimit === 'max') {
|
|
501
|
+
await clickFirstVisible([page.locator(testId('custom-spending-cap-max-button'))], SHORT_TIMEOUT_MS);
|
|
502
|
+
}
|
|
503
|
+
else if (typeof options?.spendLimit === 'number') {
|
|
504
|
+
await fillFirstVisible([page.locator(testId('custom-spending-cap-input'))], String(options.spendLimit), SHORT_TIMEOUT_MS);
|
|
505
|
+
}
|
|
506
|
+
await this.confirmFooterAction(page);
|
|
507
|
+
if (!page.isClosed()) {
|
|
508
|
+
await waitForMetaMaskReady(page);
|
|
509
|
+
await this.applyGasSetting(page, options?.gasSetting);
|
|
510
|
+
await this.confirmFooterAction(page, SHORT_TIMEOUT_MS).catch(() => undefined);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async confirmSignature() {
|
|
514
|
+
const page = await this.notificationPage();
|
|
515
|
+
const structuredSignButton = page.locator(testId('signature-sign-button')).first();
|
|
516
|
+
const scrollButton = page.locator(testId('signature-request-scroll-button')).first();
|
|
517
|
+
for (let attempts = 0; attempts < 8; attempts += 1) {
|
|
518
|
+
if (!(await isVisible(scrollButton, SHORT_TIMEOUT_MS).catch(() => false)))
|
|
519
|
+
break;
|
|
520
|
+
if (await structuredSignButton.isEnabled().catch(() => false))
|
|
521
|
+
break;
|
|
522
|
+
await scrollButton.click();
|
|
523
|
+
}
|
|
524
|
+
const signed = await clickFirstVisible([
|
|
525
|
+
page.locator(testId('confirm-footer-button')),
|
|
526
|
+
page.locator(testId('confirmation-submit-button')),
|
|
527
|
+
page.locator(testId('request-signature__sign')),
|
|
528
|
+
structuredSignButton,
|
|
529
|
+
page.locator(testId('page-container-footer-next')),
|
|
530
|
+
page.getByRole('button', { name: 'Confirm' }),
|
|
531
|
+
page.getByRole('button', { name: 'Sign' }),
|
|
532
|
+
], DEFAULT_TIMEOUT_MS);
|
|
533
|
+
if (!signed)
|
|
534
|
+
throw new Error('Unable to confirm MetaMask signature request.');
|
|
535
|
+
await clickFirstVisible([page.locator(testId('signature-warning-sign-button'))], SHORT_TIMEOUT_MS);
|
|
536
|
+
}
|
|
537
|
+
async confirmTransaction(options) {
|
|
538
|
+
let page = await this.notificationPage();
|
|
539
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
540
|
+
await this.applyGasSetting(page, options?.gasSetting);
|
|
541
|
+
await clickFirstVisible([page.locator('.set-approval-for-all-warning__footer__approve-button')], SHORT_TIMEOUT_MS);
|
|
542
|
+
await this.confirmFooterAction(page);
|
|
543
|
+
await wait(1_000);
|
|
544
|
+
const nextPage = await findMetaMaskActionPage(this.context, this.extensionId);
|
|
545
|
+
if (!nextPage)
|
|
546
|
+
return;
|
|
547
|
+
page = nextPage;
|
|
548
|
+
await waitForMetaMaskReady(page);
|
|
549
|
+
await page.bringToFront().catch(() => undefined);
|
|
550
|
+
}
|
|
551
|
+
throw new Error('MetaMask transaction confirmation did not settle after multiple confirmation steps.');
|
|
552
|
+
}
|
|
553
|
+
async connectToDapp(accounts) {
|
|
554
|
+
const page = await this.notificationPage();
|
|
555
|
+
await this.selectAccounts(page, accounts);
|
|
556
|
+
await this.confirmFooterAction(page);
|
|
557
|
+
if (!page.isClosed()) {
|
|
558
|
+
await waitForMetaMaskReady(page);
|
|
559
|
+
await this.confirmFooterAction(page, SHORT_TIMEOUT_MS).catch(() => undefined);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
async getAccountAddress() {
|
|
563
|
+
const page = await this.home();
|
|
564
|
+
await waitForMetaMaskHome(page);
|
|
565
|
+
await closeMetaMaskOverlay(page);
|
|
566
|
+
if (this.expectedAddress && await pageContainsAddress(page, this.expectedAddress)) {
|
|
567
|
+
return this.expectedAddress;
|
|
568
|
+
}
|
|
569
|
+
const openedMenu = await clickFirstVisible(metaMaskAccountMenuLocators(page), DEFAULT_TIMEOUT_MS);
|
|
570
|
+
if (!openedMenu)
|
|
571
|
+
throw new Error('Unable to open MetaMask account options menu.');
|
|
572
|
+
const openedDetails = await clickFirstVisible([page.locator(testId('account-list-menu-details'))], DEFAULT_TIMEOUT_MS);
|
|
573
|
+
if (!openedDetails)
|
|
574
|
+
throw new Error('Unable to open MetaMask account details.');
|
|
575
|
+
const addressText = page.locator(testId('address-copy-button-text')).first();
|
|
576
|
+
await addressText.waitFor({ state: 'visible', timeout: DEFAULT_TIMEOUT_MS });
|
|
577
|
+
const address = (await addressText.textContent())?.trim();
|
|
578
|
+
await closeMetaMaskOverlay(page);
|
|
579
|
+
if (!address?.startsWith('0x'))
|
|
580
|
+
throw new Error('Unable to read selected MetaMask account address.');
|
|
581
|
+
return address;
|
|
582
|
+
}
|
|
583
|
+
async rejectSignature() {
|
|
584
|
+
const page = await this.notificationPage();
|
|
585
|
+
await this.rejectFooterAction(page, [
|
|
586
|
+
page.locator(testId('signature-cancel-button')),
|
|
587
|
+
page.locator('.request-signature__footer button.btn-secondary'),
|
|
588
|
+
]);
|
|
589
|
+
}
|
|
590
|
+
async rejectTransaction() {
|
|
591
|
+
const page = await this.notificationPage();
|
|
592
|
+
await this.rejectFooterAction(page);
|
|
593
|
+
}
|
|
594
|
+
async home() {
|
|
595
|
+
const page = await openExtensionHome(this.context, this.extensionId);
|
|
596
|
+
await unlockMetaMaskIfNeeded(page, this.walletPassword);
|
|
597
|
+
return page;
|
|
598
|
+
}
|
|
599
|
+
async notificationPage() {
|
|
600
|
+
await this.unlockVisibleMetaMask();
|
|
601
|
+
let page = await getNotificationPage(this.context, this.extensionId).catch(async (error) => {
|
|
602
|
+
if (await this.unlockVisibleMetaMask()) {
|
|
603
|
+
return getNotificationPage(this.context, this.extensionId);
|
|
604
|
+
}
|
|
605
|
+
throw error;
|
|
606
|
+
});
|
|
607
|
+
if (await unlockMetaMaskIfNeeded(page, this.walletPassword)) {
|
|
608
|
+
page = await getNotificationPage(this.context, this.extensionId);
|
|
609
|
+
}
|
|
610
|
+
return page;
|
|
611
|
+
}
|
|
612
|
+
async unlockVisibleMetaMask() {
|
|
613
|
+
const page = await findMetaMaskLockedPage(this.context, this.extensionId);
|
|
614
|
+
if (!page)
|
|
615
|
+
return false;
|
|
616
|
+
return unlockMetaMaskIfNeeded(page, this.walletPassword);
|
|
617
|
+
}
|
|
618
|
+
async confirmFooterAction(page, timeout = DEFAULT_TIMEOUT_MS) {
|
|
619
|
+
const confirmed = await clickFirstVisible([
|
|
620
|
+
page.locator(testId('confirm-footer-button')),
|
|
621
|
+
page.locator(testId('confirmation-submit-button')),
|
|
622
|
+
page.locator(testId('page-container-footer-next')),
|
|
623
|
+
page.getByRole('button', { name: 'Confirm' }),
|
|
624
|
+
page.getByRole('button', { name: 'Connect' }),
|
|
625
|
+
page.getByRole('button', { name: 'Next' }),
|
|
626
|
+
page.getByRole('button', { name: 'Approve' }),
|
|
627
|
+
], timeout);
|
|
628
|
+
if (!confirmed)
|
|
629
|
+
throw new Error('Unable to confirm MetaMask notification.');
|
|
630
|
+
}
|
|
631
|
+
async rejectFooterAction(page, extraLocators = []) {
|
|
632
|
+
const rejected = await clickFirstVisible([
|
|
633
|
+
...extraLocators,
|
|
634
|
+
page.locator(testId('confirm-footer-cancel-button')),
|
|
635
|
+
page.locator(testId('page-container-footer-cancel')),
|
|
636
|
+
page.getByRole('button', { name: 'Reject' }),
|
|
637
|
+
page.getByRole('button', { name: 'Cancel' }),
|
|
638
|
+
], DEFAULT_TIMEOUT_MS);
|
|
639
|
+
if (!rejected)
|
|
640
|
+
throw new Error('Unable to reject MetaMask notification.');
|
|
641
|
+
}
|
|
642
|
+
async selectAccounts(page, accounts) {
|
|
643
|
+
if (!accounts?.length)
|
|
644
|
+
return;
|
|
645
|
+
const rows = page.locator('.choose-account-list .choose-account-list__account');
|
|
646
|
+
const rowCount = await rows.count();
|
|
647
|
+
if (!rowCount)
|
|
648
|
+
return;
|
|
649
|
+
const expected = accounts.map((account) => account.toLowerCase());
|
|
650
|
+
const expectedShort = accounts.map(shortAddress);
|
|
651
|
+
for (let index = 0; index < rowCount; index += 1) {
|
|
652
|
+
const row = rows.nth(index);
|
|
653
|
+
const text = ((await row.textContent()) ?? '').toLowerCase();
|
|
654
|
+
const matches = expected.some((account) => text.includes(account)) || expectedShort.some((account) => text.includes(account));
|
|
655
|
+
if (!matches)
|
|
656
|
+
continue;
|
|
657
|
+
const checkbox = row.locator('input[type="checkbox"]').first();
|
|
658
|
+
if (await isVisible(checkbox, SHORT_TIMEOUT_MS).catch(() => false))
|
|
659
|
+
await checkbox.check();
|
|
660
|
+
else
|
|
661
|
+
await row.click();
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (rowCount > 1) {
|
|
665
|
+
throw new Error(`Unable to find requested MetaMask account in connect prompt: ${accounts.join(', ')}.`);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
async applyGasSetting(page, gasSetting) {
|
|
669
|
+
if (!gasSetting || gasSetting === 'site')
|
|
670
|
+
return;
|
|
671
|
+
const editOpened = await clickFirstVisible([page.locator(testId('edit-gas-fee-icon')), page.getByRole('button', { name: 'Edit gas fee' })], SHORT_TIMEOUT_MS);
|
|
672
|
+
if (!editOpened)
|
|
673
|
+
throw new Error('Unable to open MetaMask gas fee editor.');
|
|
674
|
+
if (typeof gasSetting === 'string') {
|
|
675
|
+
const testIdBySetting = {
|
|
676
|
+
aggressive: 'edit-gas-fee-item-high',
|
|
677
|
+
low: 'edit-gas-fee-item-low',
|
|
678
|
+
market: 'edit-gas-fee-item-medium',
|
|
679
|
+
};
|
|
680
|
+
const selected = await clickFirstVisible([page.locator(testId(testIdBySetting[gasSetting]))], DEFAULT_TIMEOUT_MS);
|
|
681
|
+
if (!selected)
|
|
682
|
+
throw new Error(`Unable to select MetaMask ${gasSetting} gas setting.`);
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
const customOpened = await clickFirstVisible([page.locator(testId('edit-gas-fee-item-custom'))], DEFAULT_TIMEOUT_MS);
|
|
686
|
+
if (!customOpened)
|
|
687
|
+
throw new Error('Unable to open MetaMask custom gas editor.');
|
|
688
|
+
const baseFeeFilled = await fillFirstVisible([page.locator(testId('base-fee-input'))], String(gasSetting.maxBaseFee));
|
|
689
|
+
if (!baseFeeFilled)
|
|
690
|
+
throw new Error('Unable to fill MetaMask custom base fee.');
|
|
691
|
+
const priorityFeeFilled = await fillFirstVisible([page.locator(testId('priority-fee-input'))], String(gasSetting.priorityFee));
|
|
692
|
+
if (!priorityFeeFilled)
|
|
693
|
+
throw new Error('Unable to fill MetaMask custom priority fee.');
|
|
694
|
+
if (gasSetting.gasLimit !== undefined) {
|
|
695
|
+
const gasLimitFilled = await fillFirstVisible([page.locator(testId('gas-limit-input'))], String(gasSetting.gasLimit));
|
|
696
|
+
if (!gasLimitFilled)
|
|
697
|
+
throw new Error('Unable to fill MetaMask custom gas limit.');
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const saved = await clickFirstVisible([page.locator('.popover-footer button.btn-primary'), page.getByRole('button', { name: 'Save' })], DEFAULT_TIMEOUT_MS);
|
|
701
|
+
if (!saved)
|
|
702
|
+
throw new Error('Unable to save MetaMask gas setting.');
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
async function prepareMetaMask({ expectedAddress, page, setup, wallet, }) {
|
|
706
|
+
if (await isMetaMaskOnboardingVisible(page)) {
|
|
707
|
+
if (!setup?.seedPhrase) {
|
|
708
|
+
throw new Error('MetaMask is on onboarding. Provide setup.seedPhrase to import a wallet through web3-tester, or use a preconfigured persistent profile.');
|
|
709
|
+
}
|
|
710
|
+
await importMetaMaskWallet(page, setup);
|
|
711
|
+
}
|
|
712
|
+
await unlockMetaMaskIfNeeded(page, passwordForSetup(setup));
|
|
713
|
+
if (!expectedAddress)
|
|
714
|
+
return;
|
|
715
|
+
const address = await wallet.getAccountAddress();
|
|
716
|
+
if (address.toLowerCase() !== expectedAddress.toLowerCase()) {
|
|
717
|
+
throw new Error(`MetaMask account ${address} does not match expected address ${expectedAddress}.`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
export async function launchRealWallet(options) {
|
|
721
|
+
const extensionName = options.extensionName ?? DEFAULT_EXTENSION_NAME;
|
|
722
|
+
if (!fs.existsSync(options.extensionPath)) {
|
|
723
|
+
throw new Error(`MetaMask extension path does not exist: ${options.extensionPath}`);
|
|
724
|
+
}
|
|
725
|
+
const profile = resolveRealWalletProfile(options.profileDir);
|
|
726
|
+
const context = await chromium.launchPersistentContext(profile.userDataDir, {
|
|
727
|
+
args: [
|
|
728
|
+
...(profile.profileDirectory ? [`--profile-directory=${profile.profileDirectory}`] : []),
|
|
729
|
+
`--disable-extensions-except=${options.extensionPath}`,
|
|
730
|
+
`--load-extension=${options.extensionPath}`,
|
|
731
|
+
],
|
|
732
|
+
baseURL: options.baseURL,
|
|
733
|
+
headless: options.headless ?? false,
|
|
734
|
+
slowMo: options.slowMo,
|
|
735
|
+
});
|
|
736
|
+
const extensionId = await getExtensionId(context, extensionName);
|
|
737
|
+
const page = await openExtensionHome(context, extensionId);
|
|
738
|
+
const wallet = new MetaMaskRealWallet(context, page, extensionId, options.expectedAddress, passwordForSetup(options.setup));
|
|
739
|
+
await prepareMetaMask({
|
|
740
|
+
expectedAddress: options.expectedAddress,
|
|
741
|
+
page,
|
|
742
|
+
setup: options.setup,
|
|
743
|
+
wallet,
|
|
744
|
+
});
|
|
745
|
+
return {
|
|
746
|
+
approveTokenPermission: (approvalOptions) => wallet.approveTokenPermission(approvalOptions),
|
|
747
|
+
close: () => context.close(),
|
|
748
|
+
confirmSignature: () => wallet.confirmSignature(),
|
|
749
|
+
confirmTransaction: (confirmationOptions) => wallet.confirmTransaction(confirmationOptions),
|
|
750
|
+
connectToDapp: (accounts) => wallet.connectToDapp(accounts),
|
|
751
|
+
context,
|
|
752
|
+
extensionId,
|
|
753
|
+
getAccountAddress: () => wallet.getAccountAddress(),
|
|
754
|
+
rejectSignature: () => wallet.rejectSignature(),
|
|
755
|
+
rejectTransaction: () => wallet.rejectTransaction(),
|
|
756
|
+
wallet,
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
//# sourceMappingURL=real-wallet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-wallet.js","sourceRoot":"","sources":["../src/real-wallet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAgD,MAAM,kBAAkB,CAAC;AAC1F,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAgE1D,MAAM,sBAAsB,GAAG,UAAU,CAAC;AAC1C,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAC7B,MAAM,MAAM,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC;AAEvD,SAAS,YAAY,CAAC,WAAmB,EAAE,IAAI,GAAG,WAAW;IAC3D,OAAO,sBAAsB,WAAW,IAAI,IAAI,EAAE,CAAC;AACrD,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,OAAO,iCAAiC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,UAAkB;IACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC1C,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,sBAAsB,GAAG,2BAA2B,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAElF,IAAI,sBAAsB,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAC3C,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,OAAgB,EAAE,OAAO,GAAG,gBAAgB;IACnE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,OAAgB,EAAE,OAAO,GAAG,gBAAgB;IAClE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,IAAI,CAAC,EAAU;IACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,QAA4B,EAC5B,OAAO,GAAG,gBAAgB,EAC1B,UAAwC,EAAE;IAE1C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IAEtC,GAAG,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QACrD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;YAC/B,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;YAC3D,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;gBAAE,SAAS;YAC1E,IAAI,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;gBAAE,SAAS;YACrG,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7E,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE;IAEhC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,iBAAiB,CAC9B,QAA4B,EAC5B,OAAO,GAAG,gBAAgB,EAC1B,UAAyD,EAAE,cAAc,EAAE,IAAI,EAAE;IAEjF,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE;QACzD,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,IAAI;KAC/C,CAAC,CAAC;IACH,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE1B,MAAM,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,gBAAgB,CAC7B,QAA4B,EAC5B,KAAa,EACb,OAAO,GAAG,kBAAkB;IAE5B,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE1B,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,MAAe,EAAE,SAAiB;IACvE,MAAM,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,0BAA0B,CAAC,IAAU;IAC5C,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC;QACzD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QACjD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QACjD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC;QACrD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC;QAC/D,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC;QACzD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,qCAAqC,EAAE,CAAC;QACzE,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;KACvD,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,2BAA2B,CAAC,IAAU;IACnD,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC,IAAI,OAAO,CAAC,MAAM,kBAAkB,CAAC,0BAA0B,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAChJ,CAAC;AAED,SAAS,+BAA+B,CAAC,IAAU;IACjD,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO,CAAC,6BAA6B,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,6BAA6B,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE;KACjC,CAAC;AACJ,CAAC;AAED,SAAS,gCAAgC,CAAC,IAAU;IAClD,OAAO;QACL,GAAG,+BAA+B,CAAC,IAAI,CAAC;QACxC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CAAC,IAAU;IAC9C,OAAO,IAAI,CAAC,OAAO,CACjB,gJAAgJ,CACjJ,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,0BAA0B,CAAC,IAAU,EAAE,OAAO,GAAG,gBAAgB;IAC9E,OAAO,OAAO,CACZ,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE;QAC9E,cAAc,EAAE,IAAI;KACrB,CAAC,CACH,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,8BAA8B,CAAC,IAAU,EAAE,KAAwB,EAAE,UAAU,GAAG,CAAC;IAChG,MAAM,UAAU,GAAG,4BAA4B,CAAC,IAAI,CAAC,CAAC;IAEtD,KAAK,IAAI,KAAK,GAAG,UAAU,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACvE,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/B,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,iCAAiC,CAAC,IAAU;IACzD,OAAO,CACL,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;QAC9D,OAAO,CAAC,MAAM,kBAAkB,CAAC,gCAAgC,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAC5F,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,+BAA+B,CAAC,IAAU;IACvD,OAAO,CACL,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;QAClD,OAAO,CACL,MAAM,kBAAkB,CACtB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAClG,gBAAgB,CACjB,CACF,CACF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,IAAU;IAC5C,MAAM,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACxG,MAAM,QAAQ,CACZ,IAAI,CAAC,OAAO,CAAC,6DAA6D,CAAC,EAC3E,kBAAkB,CACnB,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC;AAED,SAAS,8BAA8B,CAAC,IAAU;IAChD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,4BAA4B,CAAC,IAAU;IAC9C,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,IAAU;IAC/C,OAAO,OAAO,CAAC,MAAM,kBAAkB,CAAC,8BAA8B,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;AACnG,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,IAAU;IAC5C,MAAM,iBAAiB,CACrB;QACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,KAAK,EAAE;KAClE,EACD,gBAAgB,CACjB,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,2BAA2B,CAAC,IAAU;IAC7C,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,6BAA6B,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;KACpE,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAU;IACvC,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;QAC/C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC1C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC1C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC9C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;QACjD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,IAAU;IAC3C,OAAO;QACL,IAAI,CAAC,SAAS,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC9C,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;KAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,yBAAyB,CAAC,IAAU,EAAE,OAAO,GAAG,gBAAgB;IAC7E,OAAO,CACL,CAAC,MAAM,iBAAiB,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/D,CAAC,MAAM,iBAAiB,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,CAC/F,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAU,EAAE,OAAO,GAAG,kBAAkB;IACzE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IAEtC,GAAG,CAAC;QACF,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,MAAM,kBAAkB,CAAC,2BAA2B,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/F,IAAI,MAAM,yBAAyB,CAAC,IAAI,EAAE,gBAAgB,CAAC;YAAE,SAAS;QAEtE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7E,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE;IAEhC,OAAO,OAAO,CAAC,MAAM,kBAAkB,CAAC,2BAA2B,CAAC,IAAI,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAChG,CAAC;AAED,KAAK,UAAU,8BAA8B,CAAC,OAAuB;IACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1G,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3F,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACvG,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;QACrD,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;IACtC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,oCAAoC,CAAC,OAAuB,EAAE,aAAqB;IAChG,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,SAAS,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC1E,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE;YAE1C,MAAM,SAAS,GAAG,UAOjB,CAAC;YAEF,OAAO,IAAI,OAAO,CAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACtD,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;oBAC1C,MAAM,CAAC,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC,CAAC;oBACvF,OAAO;gBACT,CAAC;gBAED,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,CAAC;oBACnD,IAAI,KAAK;wBAAE,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,wCAAwC,CAAC,CAAC,CAAC;;wBACnF,OAAO,CAAC,KAAK,CAAC,CAAC;gBACtB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3G,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,EAAE,CAAC;QAE3B,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClF,MAAM,IAAI,KAAK,CAAC,6BAA6B,aAAa,4BAA4B,SAAS,IAAI,MAAM,GAAG,CAAC,CAAC;IAChH,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,OAAuB,EAAE,aAAqB;IAC1E,OAAO,CACL,CAAC,MAAM,8BAA8B,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC,MAAM,oCAAoC,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CACrE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,OAAuB,EAAE,WAAmB;IAC3E,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;IAChF,MAAM,IAAI,GAAG,QAAQ,IAAI,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,GAAG,EAAE,KAAK,OAAO;QAAE,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrD,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,qBAAqB,CAAC,WAAmB;IAChD,OAAO,YAAY,CAAC,WAAW,EAAE,mBAAmB,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,sBAAsB,CAAC,WAAmB;IACjD,OAAO,sBAAsB,WAAW,GAAG,CAAC;AAC9C,CAAC;AAED,SAAS,6BAA6B,CAAC,IAAU;IAC/C,OAAO;QACL,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE;YACxB,IAAI,EAAE,gGAAgG;SACvG,CAAC;QACF,IAAI,CAAC,SAAS,CACZ,2HAA2H,CAC5H;KACF,CAAC;AACJ,CAAC;AAED,SAAS,6BAA6B,CAAC,IAAU;IAC/C,OAAO;QACL,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;QAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;QACjD,IAAI,CAAC,OAAO,CAAC,uDAAuD,CAAC;QACrE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,wCAAwC,EAAE,CAAC;KAC7E,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAU;IACxC,OAAO,CAAC,GAAG,6BAA6B,CAAC,IAAI,CAAC,EAAE,GAAG,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,OAAuB,EAAE,WAAmB;IAChF,MAAM,eAAe,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAC5D,IAAI,eAAiC,CAAC;IAEtC,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,SAAS;QACzE,MAAM,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAEtG,IAAI,MAAM,kBAAkB,CAAC,6BAA6B,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YAC/G,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IACE,CAAC,eAAe;YAChB,CAAC,MAAM,kBAAkB,CAAC,6BAA6B,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,EAC5G,CAAC;YACD,eAAe,GAAG,IAAI,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,OAAuB,EAAE,WAAmB;IAChF,MAAM,eAAe,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAE5D,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAClD,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC;YAAE,SAAS;QACzE,MAAM,IAAI,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACtG,IAAI,MAAM,uBAAuB,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACvD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,4BAA4B,CAAC,OAAuB,EAAE,WAAmB;IAChF,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;SACxB,OAAO,EAAE;SACT,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,OAAuB,EAAE,WAAmB,EAAE,OAAO,GAAG,kBAAkB;IAC3G,MAAM,MAAM,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAClD,MAAM,eAAe,GAAG,sBAAsB,CAAC,WAAW,CAAC,CAAC;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,IAAI,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,gBAAgB,GAAG,4BAA4B,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAE1E,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;QACjD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,EAAE,KAAK,CAAC,CAAC;QACtE,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAErH,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,SAAS;iBACZ,UAAU,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE;gBACxF,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC;aACpC,CAAC;iBACD,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5B,CAAC;QAED,IAAI,GAAG,MAAM,sBAAsB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC1D,gBAAgB,GAAG,4BAA4B,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,gBAAgB,CAAC;IAC5F,CAAC;IAED,IAAI,KAAK,gBAAgB,CAAC;IAC1B,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IAClF,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACjD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,IAAU,EAAE,OAAe;IAC5D,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAChD,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;SACzG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;SACnB,WAAW,EAAE,CAAC;IAEjB,OAAO,CACL,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAChC,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5F,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC7F,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAU,EAAE,UAAkB;IAC1D,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,+BAA+B,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC,CAAC;IACxG,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,uBAAuB,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,8BAA8B,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,MAAM,0BAA0B,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,OAAO;QACT,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,4BAA4B,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC/C,MAAM,8BAA8B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAClD,IAAI,MAAM,0BAA0B,CAAC,IAAI,CAAC;YAAE,OAAO;IACrD,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAC9C,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAC5C,MAAM,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QACD,OAAO;IACT,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;AACvE,CAAC;AAED,KAAK,UAAU,oBAAoB,CAAC,IAAU,EAAE,KAAsB;IACpE,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,wIAAwI,CACzI,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAEjD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC;IAChE,IAAI,MAAM,SAAS,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,iCAAiC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,+BAA+B,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACvG,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAC3C;YACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC;YAChD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC;YACzD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC;YAC/D,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,2BAA2B,EAAE,CAAC;YAC/D,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC;SACpD,EACD,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAEpF,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,iBAAiB,CACrB;YACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC;YACzD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,qCAAqC,EAAE,CAAC;SAC1E,EACD,KAAK,CACN,CAAC;QAEF,MAAM,iBAAiB,CACrB;YACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;YAC3C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;YAC/C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;SAC9C,EACD,gBAAgB,CACjB,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,CAAC,MAAM,+BAA+B,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACnD,MAAM,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAE7C,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAC3C;YACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;YAC1C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,gCAAgC,EAAE,CAAC;YACpE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;SAC9C,EACD,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,gBAAgB,CAC3C,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAClG,QAAQ,CACT,CAAC;IACF,IAAI,CAAC,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAEhF,MAAM,0BAA0B,GAAG,MAAM,gBAAgB,CACvD,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EACtG,QAAQ,CACT,CAAC;IACF,IAAI,CAAC,0BAA0B;QAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAEzG,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACpE,IAAI,MAAM,SAAS,CAAC,aAAa,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED,MAAM,iBAAiB,GAAG,MAAM,iBAAiB,CAC/C,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC,EACxG,kBAAkB,CACnB,CAAC;IACF,IAAI,CAAC,iBAAiB;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAEtF,MAAM,wBAAwB,CAAC,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAU,EAAE,QAAgB;IACxD,MAAM,cAAc,GAAG,MAAM,gBAAgB,CAAC,8BAA8B,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,kBAAkB,CAAC,CAAC;IAClH,IAAI,CAAC,cAAc;QAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IAEvF,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,4BAA4B,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC,CAAC;IACjG,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IACzE,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,KAAK,UAAU,sBAAsB,CAAC,IAAU,EAAE,QAA4B;IAC5E,IAAI,CAAC,CAAC,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,oIAAoI,CACrI,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,wBAAwB,CAAC,IAAU;IAChD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAChD,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,EAAE,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC;QACrF,MAAM,aAAa,GACjB,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;YAC7C,OAAO,CAAC,MAAM,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,QAAQ;YAAE,MAAM;IACvB,CAAC;IAED,IAAI,MAAM,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC;QAAE,OAAO;IACpD,IACE,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QAC7C,CAAC,MAAM,kBAAkB,CAAC,CAAC,GAAG,qBAAqB,CAAC,IAAI,CAAC,EAAE,GAAG,yBAAyB,CAAC,IAAI,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,EAClH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,MAAM,kBAAkB;IAEH;IACA;IACA;IACA;IACA;IALnB,YACmB,OAAuB,EACvB,QAAc,EACd,WAAmB,EACnB,eAAwB,EACxB,cAAuB;QAJvB,YAAO,GAAP,OAAO,CAAgB;QACvB,aAAQ,GAAR,QAAQ,CAAM;QACd,gBAAW,GAAX,WAAW,CAAQ;QACnB,oBAAe,GAAf,eAAe,CAAS;QACxB,mBAAc,GAAd,cAAc,CAAS;IACvC,CAAC;IAEJ,KAAK,CAAC,sBAAsB,CAAC,OAG5B;QACC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE3C,IAAI,OAAO,EAAE,UAAU,KAAK,KAAK,EAAE,CAAC;YAClC,MAAM,iBAAiB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,gCAAgC,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACtG,CAAC;aAAM,IAAI,OAAO,OAAO,EAAE,UAAU,KAAK,QAAQ,EAAE,CAAC;YACnD,MAAM,gBAAgB,CACpB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC,EACnD,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAC1B,gBAAgB,CACjB,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YACrB,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YACtD,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3C,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QACnF,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,iCAAiC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAErF,KAAK,IAAI,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,IAAI,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;gBAAE,MAAM;YACjF,IAAI,MAAM,oBAAoB,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;gBAAE,MAAM;YACrE,MAAM,YAAY,CAAC,KAAK,EAAE,CAAC;QAC7B,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CACpC;YACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;YAC/C,oBAAoB;YACpB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;YAClD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAC3C,EACD,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAE9E,MAAM,iBAAiB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACrG,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,OAAgD;QACvE,IAAI,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAEzC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;YACtD,MAAM,iBAAiB,CACrB,CAAC,IAAI,CAAC,OAAO,CAAC,uDAAuD,CAAC,CAAC,EACvE,gBAAgB,CACjB,CAAC;YACF,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAErC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAC9E,IAAI,CAAC,QAAQ;gBAAE,OAAO;YAEtB,IAAI,GAAG,QAAQ,CAAC;YAChB,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACnD,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,qFAAqF,CAAC,CAAC;IACzG,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAmB;QACrC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAErC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YACrB,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;YACjC,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,eAAe,IAAI,MAAM,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YAClF,OAAO,IAAI,CAAC,eAAe,CAAC;QAC9B,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,iBAAiB,CACxC,2BAA2B,CAAC,IAAI,CAAC,EACjC,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAElF,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAC3C,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC,EACnD,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAEhF,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;QAC7E,MAAM,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC7E,MAAM,OAAO,GAAG,CAAC,MAAM,WAAW,CAAC,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;QAC1D,MAAM,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAEjC,IAAI,CAAC,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACrG,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE;YAClC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,yBAAyB,CAAC,CAAC;YAC/C,IAAI,CAAC,OAAO,CAAC,iDAAiD,CAAC;SAChE,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3C,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,IAAI;QAChB,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACrE,MAAM,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAEnC,IAAI,IAAI,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,KAAc,EAAE,EAAE;YAClG,IAAI,MAAM,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;gBACvC,OAAO,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YAC7D,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,IAAI,MAAM,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YAC5D,IAAI,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,qBAAqB;QACjC,MAAM,IAAI,GAAG,MAAM,sBAAsB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1E,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QACxB,OAAO,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAC3D,CAAC;IAEO,KAAK,CAAC,mBAAmB,CAAC,IAAU,EAAE,OAAO,GAAG,kBAAkB;QACxE,MAAM,SAAS,GAAG,MAAM,iBAAiB,CACvC;YACE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,4BAA4B,CAAC,CAAC;YAClD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC7C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC1C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;SAC9C,EACD,OAAO,CACR,CAAC;QACF,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9E,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,IAAU,EAAE,gBAAoC,EAAE;QACjF,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CACtC;YACE,GAAG,aAAa;YAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC;YACpD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC;YACpD,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;YAC5C,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;SAC7C,EACD,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC5E,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,IAAU,EAAE,QAAmB;QAC1D,IAAI,CAAC,QAAQ,EAAE,MAAM;YAAE,OAAO;QAE9B,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,oDAAoD,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACpC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAEtB,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG,QAAQ,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAEjD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;YAC7D,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;YAC9H,IAAI,CAAC,OAAO;gBAAE,SAAS;YAEvB,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,KAAK,EAAE,CAAC;YAC/D,IAAI,MAAM,SAAS,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC;gBAAE,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC;;gBACtF,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,gEAAgE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1G,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,eAAe,CAAC,IAAU,EAAE,UAAkC;QAC1E,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,MAAM;YAAE,OAAO;QAEjD,MAAM,UAAU,GAAG,MAAM,iBAAiB,CACxC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC,EAC/F,gBAAgB,CACjB,CAAC;QACF,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAE5E,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;YACnC,MAAM,eAAe,GAAoD;gBACvE,UAAU,EAAE,wBAAwB;gBACpC,GAAG,EAAE,uBAAuB;gBAC5B,MAAM,EAAE,0BAA0B;aACnC,CAAC;YAEF,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CACtC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EACnD,kBAAkB,CACnB,CAAC;YACF,IAAI,CAAC,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,UAAU,eAAe,CAAC,CAAC;QACzF,CAAC;aAAM,CAAC;YACN,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAC1C,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAClD,kBAAkB,CACnB,CAAC;YACF,IAAI,CAAC,YAAY;gBAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;YAEjF,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;YACtH,IAAI,CAAC,aAAa;gBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAEhF,MAAM,iBAAiB,GAAG,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;YAC/H,IAAI,CAAC,iBAAiB;gBAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YAExF,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACtC,MAAM,cAAc,GAAG,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACtH,IAAI,CAAC,cAAc;oBAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,iBAAiB,CACnC,CAAC,IAAI,CAAC,OAAO,CAAC,oCAAoC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,EAChG,kBAAkB,CACnB,CAAC;QACF,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IACtE,CAAC;CACF;AAED,KAAK,UAAU,eAAe,CAAC,EAC7B,eAAe,EACf,IAAI,EACJ,KAAK,EACL,MAAM,GAMP;IACC,IAAI,MAAM,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,wIAAwI,CACzI,CAAC;QACJ,CAAC;QAED,MAAM,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;IAE5D,IAAI,CAAC,eAAe;QAAE,OAAO;IAE7B,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,iBAAiB,EAAE,CAAC;IACjD,IAAI,OAAO,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,WAAW,EAAE,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,oCAAoC,eAAe,GAAG,CAAC,CAAC;IACrG,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAgC;IACrE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,sBAAsB,CAAC;IACtE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,2CAA2C,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,OAAO,GAAG,wBAAwB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,uBAAuB,CAAC,OAAO,CAAC,WAAW,EAAE;QAC1E,IAAI,EAAE;YACJ,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,uBAAuB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACxF,+BAA+B,OAAO,CAAC,aAAa,EAAE;YACtD,oBAAoB,OAAO,CAAC,aAAa,EAAE;SAC5C;QACD,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,KAAK;QACnC,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,IAAI,kBAAkB,CACnC,OAAO,EACP,IAAI,EACJ,WAAW,EACX,OAAO,CAAC,eAAe,EACvB,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAChC,CAAC;IAEF,MAAM,eAAe,CAAC;QACpB,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,IAAI;QACJ,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,MAAM;KACP,CAAC,CAAC;IAEH,OAAO;QACL,sBAAsB,EAAE,CAAC,eAAe,EAAE,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,eAAe,CAAC;QAC3F,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE;QAC5B,gBAAgB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,EAAE;QACjD,kBAAkB,EAAE,CAAC,mBAAmB,EAAE,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,mBAAmB,CAAC;QAC3F,aAAa,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC;QAC3D,OAAO;QACP,WAAW;QACX,iBAAiB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE;QACnD,eAAe,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,EAAE;QAC/C,iBAAiB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,iBAAiB,EAAE;QACnD,MAAM;KACP,CAAC;AACJ,CAAC"}
|
package/docs/API.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
-
This package exposes two Playwright fixture families:
|
|
3
|
+
This package exposes two Playwright fixture families and one real-wallet adapter:
|
|
4
4
|
|
|
5
5
|
- `fixtures`: local deterministic Anvil tests.
|
|
6
6
|
- `live-fixtures`: live Sepolia tests that sign with a runtime-only private key.
|
|
7
|
+
- `real-wallet`: persistent Chromium and MetaMask automation for fully in-UI Web3 tests.
|
|
7
8
|
|
|
8
9
|
## Local Fixtures
|
|
9
10
|
|
|
@@ -48,6 +49,53 @@ Optional:
|
|
|
48
49
|
SEPOLIA_RPC_URL=https://...
|
|
49
50
|
```
|
|
50
51
|
|
|
52
|
+
## Real Wallet
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { launchRealWallet } from '@marigoldlabs/web3-tester/real-wallet';
|
|
56
|
+
|
|
57
|
+
const session = await launchRealWallet({
|
|
58
|
+
extensionPath: process.env.FJORD_REAL_WALLET_EXTENSION_PATH as string,
|
|
59
|
+
profileDir: process.env.FJORD_REAL_WALLET_PROFILE_DIR as string,
|
|
60
|
+
expectedAddress: process.env.FJORD_REAL_WALLET_ADDRESS,
|
|
61
|
+
setup: process.env.FJORD_REAL_WALLET_PASSWORD || process.env.FJORD_REAL_WALLET_SECRET_RECOVERY_PHRASE
|
|
62
|
+
? {
|
|
63
|
+
password: process.env.FJORD_REAL_WALLET_PASSWORD,
|
|
64
|
+
seedPhrase: process.env.FJORD_REAL_WALLET_SECRET_RECOVERY_PHRASE,
|
|
65
|
+
}
|
|
66
|
+
: undefined,
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`launchRealWallet` starts a persistent Chromium context with the configured unpacked MetaMask extension. `profileDir` can be either a dedicated Playwright user data directory or a Chrome profile directory such as `Default` or `Profile 1`; Chrome profile paths are mapped back to their user data root and launched with `--profile-directory`.
|
|
71
|
+
|
|
72
|
+
Options:
|
|
73
|
+
|
|
74
|
+
| Option | Description |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `extensionPath` | Required path to the unpacked MetaMask extension. |
|
|
77
|
+
| `profileDir` | Required persistent Chromium user data directory, or a Chrome profile directory. |
|
|
78
|
+
| `baseURL` | Optional Playwright base URL for pages opened from the returned context. |
|
|
79
|
+
| `expectedAddress` | Optional account address assertion after unlock/import. |
|
|
80
|
+
| `extensionName` | Extension name used to resolve the extension ID. Defaults to `MetaMask`. |
|
|
81
|
+
| `headless` | Chromium headless setting. Defaults to `false` because extensions require headed Chromium in normal use. |
|
|
82
|
+
| `setup.password` | Optional password used to unlock MetaMask, or to import a seed phrase when onboarding is visible. |
|
|
83
|
+
| `setup.seedPhrase` | Seed phrase used if MetaMask opens on onboarding. When no password is supplied, web3-tester imports with a deterministic test profile password. |
|
|
84
|
+
| `slowMo` | Optional Playwright slow-motion delay. |
|
|
85
|
+
|
|
86
|
+
Returned session methods:
|
|
87
|
+
|
|
88
|
+
| Method | Description |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `connectToDapp(accounts?)` | Approves a dapp connection request in MetaMask. |
|
|
91
|
+
| `confirmSignature()` | Confirms a pending signature request. |
|
|
92
|
+
| `confirmTransaction(options?)` | Confirms a pending transaction request. |
|
|
93
|
+
| `approveTokenPermission(options?)` | Approves a pending ERC-20 spending permission request. |
|
|
94
|
+
| `rejectSignature()` | Rejects a pending signature request. |
|
|
95
|
+
| `rejectTransaction()` | Rejects a pending transaction request. |
|
|
96
|
+
| `getAccountAddress()` | Returns the selected MetaMask account. |
|
|
97
|
+
| `close()` | Closes the persistent browser context. |
|
|
98
|
+
|
|
51
99
|
## MockWalletController
|
|
52
100
|
|
|
53
101
|
```ts
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# Consuming Web3 Tester From Fjord V4
|
|
2
2
|
|
|
3
|
-
This guide covers adding
|
|
3
|
+
This guide covers adding the published package to Fjord v4 as a dependency.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install --save-dev
|
|
8
|
+
npm install --save-dev @marigoldlabs/web3-tester
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
For a pinned dependency, use
|
|
11
|
+
For a pinned dependency, use an exact package version:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npm install --save-dev
|
|
14
|
+
npm install --save-dev @marigoldlabs/web3-tester@<version>
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Playwright Config
|
|
@@ -120,4 +120,4 @@ Keep destructive or expensive tests behind separate flags so normal CI remains r
|
|
|
120
120
|
- Run live Sepolia read-only tests on a scheduled job or protected branch.
|
|
121
121
|
- Run live mutation tests manually with approval.
|
|
122
122
|
- Store `FJORD_PRIVATE_KEY` and `SEPOLIA_RPC_URL` only as CI secrets.
|
|
123
|
-
- Pin
|
|
123
|
+
- Pin the npm dependency to an exact version once Fjord depends on it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Release Checklist
|
|
2
2
|
|
|
3
|
-
Use this before
|
|
3
|
+
Use this before publishing a new version or pinning it inside Fjord v4.
|
|
4
4
|
|
|
5
5
|
## Required
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ Use this before pushing a new version or pinning it inside Fjord v4.
|
|
|
16
16
|
From a clean consumer repo:
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install --save-dev
|
|
19
|
+
npm install --save-dev @marigoldlabs/web3-tester@<version>
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Then import:
|
|
@@ -47,3 +47,9 @@ git add .
|
|
|
47
47
|
git commit -m "Document and package Web3 tester"
|
|
48
48
|
git push -u origin main
|
|
49
49
|
```
|
|
50
|
+
|
|
51
|
+
## Publish
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm publish --access public
|
|
55
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marigoldlabs/web3-tester",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A Playwright, Anvil, and Viem based Web3 E2E harness with an injected programmable wallet provider.",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"./private-key-rpc-client": {
|
|
40
40
|
"types": "./dist/private-key-rpc-client.d.ts",
|
|
41
41
|
"import": "./dist/private-key-rpc-client.js"
|
|
42
|
+
},
|
|
43
|
+
"./real-wallet": {
|
|
44
|
+
"types": "./dist/real-wallet.d.ts",
|
|
45
|
+
"import": "./dist/real-wallet.js"
|
|
42
46
|
}
|
|
43
47
|
},
|
|
44
48
|
"files": [
|
|
@@ -56,15 +60,14 @@
|
|
|
56
60
|
"typecheck": "tsc --noEmit",
|
|
57
61
|
"verify": "npm run typecheck && npm run build && npm test"
|
|
58
62
|
},
|
|
59
|
-
"dependencies": {
|
|
60
|
-
"viem": "^2.39.3"
|
|
61
|
-
},
|
|
62
63
|
"peerDependencies": {
|
|
63
|
-
"@playwright/test": ">=1.56.0"
|
|
64
|
+
"@playwright/test": ">=1.56.0",
|
|
65
|
+
"viem": ">=2.39.3 <3"
|
|
64
66
|
},
|
|
65
67
|
"devDependencies": {
|
|
66
68
|
"@playwright/test": "^1.56.1",
|
|
67
69
|
"@types/node": "^24.10.1",
|
|
68
|
-
"typescript": "^5.9.3"
|
|
70
|
+
"typescript": "^5.9.3",
|
|
71
|
+
"viem": "^2.52.0"
|
|
69
72
|
}
|
|
70
73
|
}
|