UNPKG

@solarity/hardhat-migrate

Version:
125 lines (121 loc) 4.92 kB
import { MigrateError } from "../../utils/index.js"; let trezorInitialized = false; let cachedAddress = null; let lastMnemonicIndexAsked = null; export async function initTrezor() { if (trezorInitialized) return; const TrezorConnect = await getTrezorConnect(); try { await TrezorConnect.init({ manifest: { email: "developer@example.com", appUrl: "https://hardhat.org", }, debug: false, lazyLoad: true, transports: ["BridgeTransport", "NodeUsbTransport"], }); // eslint-disable-next-line no-console console.log(` Trezor Settings Configuration Notice We're applying two important settings to your Trezor device: 1. Device Passphrase Entry We've set your Trezor to always request passphrase entry directly on the device itself. This ensures better security as it's currently not possible to enter passphrases through the console interface. 2. Temporary Safety Check Override We're temporarily modifying safety checks with the "PromptTemporarily" setting. This is necessary because some derivation paths used by our application are incorrectly flagged as "unknown" by the device firmware (reference: Trezor Suite issue #17203 - https://github.com/trezor/trezor-suite/issues/17203). The device will prompt you to confirm these changes. `); const result = await TrezorConnect.applySettings({ passphrase_always_on_device: true, safety_checks: "PromptTemporarily", }); if (!result.success) { throw new MigrateError(`Failed to apply Trezor settings: ${result.payload.error}`); } trezorInitialized = true; } catch (error) { throw new MigrateError(`Failed to initialize Trezor connection: ${error.message}`); } } export async function signWithTrezor(tx, mnemonicIndex = 0) { if (!trezorInitialized) { await initTrezor(); } const trezorTx = { to: tx.to, value: tx.value?.toString() || "0", chainId: Number(tx.chainId), gasLimit: tx.gasLimit?.toString() || "0", gasPrice: tx.gasPrice?.toString(), nonce: tx.nonce?.toString() || "0", data: tx.data || "0x", }; const TrezorConnect = await getTrezorConnect(); const result = await TrezorConnect.ethereumSignTransaction({ transaction: trezorTx, path: getKeyPath(mnemonicIndex), useEmptyPassphrase: true, }); if (!result.success) { let errorMessage = `Failed to sign transaction with Trezor: ${result.payload.error}\n`; if (result.payload.error.includes("Forbidden key path")) { errorMessage += "\n See following GitHub issue on how to resolve the problem: https://github.com/trezor/trezor-suite/issues/17203 \n\n"; } throw new MigrateError(errorMessage); } return result.payload.serializedTx; } export async function getTrezorAddress(mnemonicIndex = 0) { if (!trezorInitialized) { await initTrezor(); } if (lastMnemonicIndexAsked === mnemonicIndex && cachedAddress) { return cachedAddress; } const TrezorConnect = await getTrezorConnect(); try { const result = await TrezorConnect.ethereumGetAddress({ path: getKeyPath(mnemonicIndex), useEmptyPassphrase: true, }); if (!result.success) { throw new MigrateError(`Failed to get Trezor address: ${result.payload.error}`); } lastMnemonicIndexAsked = mnemonicIndex; cachedAddress = result.payload.address; return result.payload.address; } catch (error) { throw new MigrateError(`Error getting address from Trezor: ${error.message}`); } } function getKeyPath(mnemonicIndex) { return `m/44'/60'/0'/0/${mnemonicIndex}`; } async function getTrezorConnect() { try { const trezorModule = await import("@trezor/connect"); // `@trezor/connect` is published as CommonJS with a synthetic `default` // export, so when it is pulled in through Node's ESM bridge we end up with // a namespace shaped like `{ default: { default: TrezorConnect } }`. // The helper below unwraps both layers (and gracefully falls back in case // this packaging detail changes in the future). const trezorConnect = trezorModule?.default?.default ?? trezorModule?.default ?? trezorModule; if (!trezorConnect) { throw new MigrateError("Unable to load Trezor Connect: unexpected module shape."); } return trezorConnect; } catch (error) { throw new MigrateError(`An error occurred while importing @trezor/connect (if the module cannot be found, please install it): ${String(error.message || error)}.`); } } //# sourceMappingURL=trezor-integration.js.map