如何用Puppeteer或其他工具自动化MetaMask?dAppeteer报错求方案
问题说明
我已用Puppeteer完成网站页面自动化流程,但无法实现MetaMask的授权操作。尝试使用dAppeteer库时出现报错,想了解是否必须依赖该库,以及是否有更优的解决方案。
复现代码
import dotenv from 'dotenv' dotenv.config() import dappeteer from '@chainsafe/dappeteer'; console.log('dappeteer: ', dappeteer) function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function main() { let browser; let metamask; browser = await dappeteer.launch({ headless: false, metaMaskVersion: "v11.0.0", defaultViewport: null, automation: "puppeteer", }) metamask = await dappeteer.setupMetaMask(browser, { seed: process.env.SEED, password: process.env.PASSWORD }) } main();
报错日志
/home/lucrypto/Documents/code/scroll-automate/node_modules/@chainsafe/dappeteer/dist/puppeteer/page.js:73 return new elements_1.DPuppeteerElementHandle((await this.page.waitForXPath(xpath, opts))); ^ TypeError: this.page.waitForXPath is not a function at DPupeteerPage.waitForXPath (/home/lucrypto/Documents/code/scroll-automate/node_modules/@chainsafe/dappeteer/dist/puppeteer/page.js:73:72) at getElementByContent (/home/lucrypto/Documents/code/scroll-automate/node_modules/@chainsafe/dappeteer/dist/helpers/selectors.js:5:71) at clickOnElement (/home/lucrypto/Documents/code/scroll-automate/node_modules/@chainsafe/dappeteer/dist/helpers/actions.js:51:45) at importAccount (/home/lucrypto/Documents/code/scroll-automate/node_modules/@chainsafe/dappeteer/dist/setup/setupActions.js:41:40) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async Object.setupMetaMask (/home/lucrypto/Documents/code/scroll-automate/node_modules/@chainsafe/dappeteer/dist/setup/setupMetaMask.js:33:9) at async file:///home/lucrypto/Documents/code/scroll-automate/src/index.js:25:18
系统环境
- OS:Ubuntu 20.04.6 LTS
- Node.js版本:v20.10.0
- dAppeteer版本:5.2.1
- Puppeteer版本:22.0.0
需求
优先修复dAppeteer的问题,也接受其他可行的MetaMask自动化方案。
解决方案
一、修复dAppeteer报错
报错核心原因是版本不兼容:Puppeteer从v19开始重构页面API,page.waitForXPath被迁移到Frame对象下,而dAppeteer 5.2.1的代码仍在调用旧版API。
解决步骤:
降级Puppeteer到兼容版本
安装v18.x版本的Puppeteer,该版本保留了page.waitForXPath方法,与dAppeteer 5.2.1兼容:npm install puppeteer@18.2.1匹配MetaMask版本
dAppeteer对MetaMask版本有严格依赖,v11.0.0可能与当前dAppeteer版本不匹配。可查看dAppeteer官方文档的版本对应表,替换为推荐的MetaMask版本(比如v10.25.0),修改代码中的metaMaskVersion参数。
二、替代方案:手动用Puppeteer操作MetaMask
如果不想依赖dAppeteer,可直接用Puppeteer模拟手动操作MetaMask,步骤如下:
准备MetaMask扩展
下载MetaMask的CRX扩展包,或获取浏览器中已安装的MetaMask扩展ID。启动浏览器并加载扩展
在Puppeteer启动参数中配置扩展加载规则。模拟MetaMask初始化流程
用waitForSelector、click、type等API,模拟导入助记词、设置密码等操作。处理dApp授权请求
当dApp发起连接/交易请求时,切换到MetaMask弹窗页面,点击授权按钮。
示例代码:
import puppeteer from 'puppeteer'; import dotenv from 'dotenv'; dotenv.config(); async function main() { // 替换为你的MetaMask扩展路径或ID const metamaskExtensionPath = './path/to/metamask/crx'; const browser = await puppeteer.launch({ headless: false, args: [ `--disable-extensions-except=${metamaskExtensionPath}`, `--load-extension=${metamaskExtensionPath}`, '--no-sandbox', ], defaultViewport: null, }); // 获取MetaMask页面 let metamaskPage; const pages = await browser.pages(); for (const page of pages) { if (page.url().startsWith('chrome-extension://')) { metamaskPage = page; break; } } if (!metamaskPage) { metamaskPage = await browser.newPage(); // 替换为你的MetaMask扩展ID对应的主页地址 await metamaskPage.goto('chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/home.html'); } // 模拟导入账户流程 await metamaskPage.waitForSelector('[data-testid="onboarding-terms-checkbox"]'); await metamaskPage.click('[data-testid="onboarding-terms-checkbox"]'); await metamaskPage.click('[data-testid="onboarding-accept-terms"]'); await metamaskPage.click('[data-testid="import-wallet"]'); await metamaskPage.click('[data-testid="metametrics-i-agree"]'); // 输入助记词 const seedWords = process.env.SEED.split(' '); for (let i = 0; i < 12; i++) { await metamaskPage.type(`[data-testid="import-srp__srp-word-${i}"]`, seedWords[i]); } // 设置密码 await metamaskPage.type('[data-testid="create-password"]', process.env.PASSWORD); await metamaskPage.type('[data-testid="confirm-password"]', process.env.PASSWORD); await metamaskPage.click('[data-testid="create-password-terms"]'); await metamaskPage.click('[data-testid="import-srp-confirm"]'); // 完成初始化 await metamaskPage.waitForSelector('[data-testid="onboarding-complete-done"]'); await metamaskPage.click('[data-testid="onboarding-complete-done"]'); await metamaskPage.click('[data-testid="pin-extension-next"]'); await metamaskPage.click('[data-testid="pin-extension-done"]'); // 操作dApp并处理授权 const dappPage = await browser.newPage(); await dappPage.goto('https://your-dapp-url.com'); // 点击连接钱包按钮 await dappPage.click('#connect-wallet-button'); // 切换到MetaMask授权弹窗 const popupPages = await browser.pages(); const authPopup = popupPages.find(page => page.url().includes('request') && page.url().startsWith('chrome-extension://')); await authPopup.waitForSelector('[data-testid="connect-approve-button"]'); await authPopup.click('[data-testid="connect-approve-button"]'); } main();
注意:MetaMask的页面选择器会随版本更新变化,需根据你使用的MetaMask版本调整对应的选择器。
内容的提问来源于stack exchange,提问作者MrCodeur

