Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/connect-multichain/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Fix `PrivateKey is not a constructor` when connecting over the MWP transport from a bundled browser app. `eciesjs` publishes CommonJS only — no `module` field, and no `import` condition in its `exports` map — so bundlers shape `await import('eciesjs')` as `{ default: <module.exports> }` rather than a namespace carrying the named exports, leaving `decrypt`, `encrypt`, `PrivateKey` and `PublicKey` `undefined`. `createKeyManager` now unwraps `default` when it is present. Node and dev servers that synthesize the named exports were unaffected. ([#343](https://github.com/MetaMask/connect-monorepo/pull/343))

## [1.2.0]

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable id-length -- vitest alias */
import type { IKeyManager } from '@metamask/mobile-wallet-protocol-core';
import * as t from 'vitest';
import { vi } from 'vitest';

import { createKeyManager } from './KeyManager';

const PLAINTEXT = 'hello from the dapp';

/**
* Exercises every method of the key manager against real `eciesjs`.
*
* @param keymanager - The key manager under test.
* @returns The decrypted plaintext, which should match what was encrypted.
*/
async function roundTrip(keymanager: IKeyManager): Promise<string> {
const { privateKey, publicKey } = keymanager.generateKeyPair();
keymanager.validatePeerKey(publicKey);
const ciphertext = await keymanager.encrypt(PLAINTEXT, publicKey);
return keymanager.decrypt(ciphertext, privateKey);
}

t.describe('createKeyManager', () => {
t.it('round-trips with the namespace shape Node resolves', async () => {
const keymanager = await createKeyManager();

t.expect(await roundTrip(keymanager)).toBe(PLAINTEXT);
});

t.it('round-trips when the import resolves to `{ default }`', async () => {
// The shape bundlers produce for `eciesjs`, which publishes CommonJS only.
// Destructuring it directly yields `undefined` for every named export.
const actual = await vi.importActual('eciesjs');
vi.resetModules();
vi.doMock('eciesjs', () => ({ default: actual }));

try {
const { createKeyManager: create } = await import('./KeyManager');
const keymanager = await create();

t.expect(await roundTrip(keymanager)).toBe(PLAINTEXT);
} finally {
vi.doUnmock('eciesjs');
vi.resetModules();
}
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import type {
* @returns A ready-to-use key manager instance.
*/
export async function createKeyManager(): Promise<IKeyManager> {
const { decrypt, encrypt, PrivateKey, PublicKey } = await import('eciesjs');
// `eciesjs` is CommonJS-only, so bundlers shape this import as
// `{ default: <module.exports> }` rather than a namespace of named exports.
const ecies = await import('eciesjs');
const { decrypt, encrypt, PrivateKey, PublicKey } = ecies.default ?? ecies;

return {
generateKeyPair(): KeyPair {
Expand Down