diff --git a/packages/wallets/ledger/src/polyfills/index.ts b/packages/wallets/ledger/src/polyfills/index.ts index 260f6ed0e..e9db0daf3 100644 --- a/packages/wallets/ledger/src/polyfills/index.ts +++ b/packages/wallets/ledger/src/polyfills/index.ts @@ -1,3 +1 @@ -'use strict'; - import './Buffer.js'; diff --git a/packages/wallets/trezor/src/adapter.ts b/packages/wallets/trezor/src/adapter.ts index 4a91c54c6..785ddd509 100644 --- a/packages/wallets/trezor/src/adapter.ts +++ b/packages/wallets/trezor/src/adapter.ts @@ -1,10 +1,11 @@ import type { WalletName } from '@solana/wallet-adapter-base'; -import type { TrezorConnect } from '@trezor/connect-web'; import { BaseSignerWalletAdapter, + WalletAccountError, WalletConfigError, WalletDisconnectedError, WalletDisconnectionError, + WalletLoadError, WalletNotConnectedError, WalletNotReadyError, WalletPublicKeyError, @@ -14,7 +15,15 @@ import { } from '@solana/wallet-adapter-base'; import type { Transaction, TransactionVersion, VersionedTransaction } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js'; -import { DEVICE, DEVICE_EVENT } from './constants.js'; +import type { + DeviceEventMessage, + SolanaPublicKey, + SolanaSignedTransaction, + Success, + TrezorConnect, + Unsuccessful, +} from '@trezor/connect-web'; +import './polyfills/index.js'; export interface TrezorWalletAdapterConfig { derivationPath?: string; @@ -27,12 +36,12 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { name = TrezorWalletName; url = 'https://trezor.io'; icon = - 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEwOHB4IiBoZWlnaHQ9IjEwOHB4IiB2aWV3Qm94PSIwIDAgMTA4IDEwOCIgdmVyc2lvbj0iMS4xIj4NCjxnIGlkPSJzdXJmYWNlMSI+DQo8cGF0aCBzdHlsZT0iIHN0cm9rZTpub25lO2ZpbGwtcnVsZTpub256ZXJvO2ZpbGw6cmdiKDEwMCUsMTAwJSwxMDAlKTtmaWxsLW9wYWNpdHk6MTsiIGQ9Ik0gNTQgMCBDIDgzLjgyNDIxOSAwIDEwOCAyNC4xNzU3ODEgMTA4IDU0IEMgMTA4IDgzLjgyNDIxOSA4My44MjQyMTkgMTA4IDU0IDEwOCBDIDI0LjE3NTc4MSAxMDggMCA4My44MjQyMTkgMCA1NCBDIDAgMjQuMTc1NzgxIDI0LjE3NTc4MSAwIDU0IDAgWiBNIDU0IDAgIi8+DQo8cGF0aCBzdHlsZT0iIHN0cm9rZTpub25lO2ZpbGwtcnVsZTpub256ZXJvO2ZpbGw6cmdiKDAlLDAlLDAlKTtmaWxsLW9wYWNpdHk6MTsiIGQ9Ik0gNTQuMDQ2ODc1IDEwLjEyNSBDIDQxLjk0MTQwNiAxMC4xMjUgMzIuMTQwNjI1IDIwLjI0MjE4OCAzMi4xNDA2MjUgMzIuNzQyMTg4IEwgMzIuMTQwNjI1IDQxLjIxODc1IEMgMjcuODkwNjI1IDQyLjAxNTYyNSAyMy42MjUgNDMuMDc0MjE5IDIzLjYyNSA0NC40NDkyMTkgTCAyMy42MjUgODguNjg3NSBDIDIzLjYyNSA4OC42ODc1IDIzLjYyNSA4OS45MTAxNTYgMjQuOTU3MDMxIDkwLjQ4ODI4MSBDIDI5Ljc4MTI1IDkyLjUwNzgxMiA0OC43Njk1MzEgOTkuNDQ1MzEyIDUzLjEzMjgxMiAxMDEuMDM1MTU2IEMgNTMuNjk1MzEyIDEwMS4yNSA1My44NTE1NjIgMTAxLjI1IDU0IDEwMS4yNSBDIDU0LjIwNzAzMSAxMDEuMjUgNTQuMzA0Njg4IDEwMS4yNSA1NC44NjcxODggMTAxLjAzNTE1NiBDIDU5LjIzMDQ2OSA5OS40NDUzMTIgNzguMjY1NjI1IDkyLjUwNzgxMiA4My4wOTM3NSA5MC40ODgyODEgQyA4NC4zMjgxMjUgODkuOTYwOTM4IDg0LjM3NSA4OC43MzgyODEgODQuMzc1IDg4LjczODI4MSBMIDg0LjM3NSA0NC40NDkyMTkgQyA4NC4zNzUgNDMuMDc0MjE5IDgwLjE3MTg3NSA0MS45NjQ4NDQgNzUuOTA2MjUgNDEuMjE4NzUgTCA3NS45MDYyNSAzMi43NDIxODggQyA3NS45Njg3NSAyMC4yNDIxODggNjYuMTA5Mzc1IDEwLjEyNSA1NC4wNDY4NzUgMTAuMTI1IFogTSA1NC4wNDY4NzUgMjAuOTMzNTk0IEMgNjEuMTgzNTk0IDIwLjkzMzU5NCA2NS40OTYwOTQgMjUuMzg2NzE5IDY1LjQ5NjA5NCAzMi43NTM5MDYgTCA2NS40OTYwOTQgNDAuMTIxMDk0IEMgNTcuNDk2MDk0IDM5LjU0Mjk2OSA1MC42NjQwNjIgMzkuNTQyOTY5IDQyLjYxMzI4MSA0MC4xMjEwOTQgTCA0Mi42MTMyODEgMzIuNzUzOTA2IEMgNDIuNjEzMjgxIDI1LjM3NSA0Ni45MjU3ODEgMjAuOTMzNTk0IDU0LjA0Njg3NSAyMC45MzM1OTQgWiBNIDU0IDUwLjk2ODc1IEMgNjMuOTU3MDMxIDUwLjk2ODc1IDcyLjMxNjQwNiA1MS43NjU2MjUgNzIuMzE2NDA2IDUzLjE5MTQwNiBMIDcyLjMxNjQwNiA4MC43ODkwNjIgQyA3Mi4zMTY0MDYgODEuMjE4NzUgNzIuMjY1NjI1IDgxLjI2OTUzMSA3MS44OTg0MzggODEuNDIxODc1IEMgNzEuNTQ2ODc1IDgxLjU4NTkzOCA1NC45MTc5NjkgODcuNzc3MzQ0IDU0LjkxNzk2OSA4Ny43NzczNDQgQyA1NC45MTc5NjkgODcuNzc3MzQ0IDU0LjI0NjA5NCA4Ny45OTIxODggNTQuMDQ2ODc1IDg3Ljk5MjE4OCBDIDUzLjgzOTg0NCA4Ny45OTIxODggNTMuMTc5Njg4IDg3LjcyNjU2MiA1My4xNzk2ODggODcuNzI2NTYyIEMgNTMuMTc5Njg4IDg3LjcyNjU2MiAzNi41NTA3ODEgODEuNTM1MTU2IDM2LjE5OTIxOSA4MS4zNzEwOTQgQyAzNS44NDM3NSA4MS4yMDcwMzEgMzUuNzgxMjUgODEuMTU2MjUgMzUuNzgxMjUgODAuNzM4MjgxIEwgMzUuNzgxMjUgNTMuMTQwNjI1IEMgMzUuNjgzNTk0IDUxLjcxNDg0NCA0NC4wNDI5NjkgNTAuOTY4NzUgNTQgNTAuOTY4NzUgWiBNIDU0IDUwLjk2ODc1ICIvPg0KPC9nPg0KPC9zdmc+DQo='; + 'data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjEwOCIgd2lkdGg9IjEwOCIgdmlld0JveD0iMCAwIDEwOCAxMDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTU0IDBjMjkuODI0MjE5IDAgNTQgMjQuMTc1NzgxIDU0IDU0cy0yNC4xNzU3ODEgNTQtNTQgNTQtNTQtMjQuMTc1NzgxLTU0LTU0IDI0LjE3NTc4MS01NCA1NC01NHptMCAwIiBmaWxsPSIjZmZmIi8+PHBhdGggZD0ibTU0LjA0Njg3NSAxMC4xMjVjLTEyLjEwNTQ2OSAwLTIxLjkwNjI1IDEwLjExNzE4OC0yMS45MDYyNSAyMi42MTcxODh2OC40NzY1NjJjLTQuMjUuNzk2ODc1LTguNTE1NjI1IDEuODU1NDY5LTguNTE1NjI1IDMuMjMwNDY5djQ0LjIzODI4MXMwIDEuMjIyNjU2IDEuMzMyMDMxIDEuODAwNzgxYzQuODI0MjE5IDIuMDE5NTMxIDIzLjgxMjUgOC45NTcwMzEgMjguMTc1NzgxIDEwLjU0Njg3NS41NjI1LjIxNDg0NC43MTg3NS4yMTQ4NDQuODY3MTg4LjIxNDg0NC4yMDcwMzEgMCAuMzA0Njg4IDAgLjg2NzE4OC0uMjE0ODQ0IDQuMzYzMjgxLTEuNTg5ODQ0IDIzLjM5ODQzNy04LjUyNzM0NCAyOC4yMjY1NjItMTAuNTQ2ODc1IDEuMjM0Mzc1LS41MjczNDMgMS4yODEyNS0xLjc1IDEuMjgxMjUtMS43NXYtNDQuMjg5MDYyYzAtMS4zNzUtNC4yMDMxMjUtMi40ODQzNzUtOC40Njg3NS0zLjIzMDQ2OXYtOC40NzY1NjJjLjA2MjUtMTIuNS05Ljc5Njg3NS0yMi42MTcxODgtMjEuODU5Mzc1LTIyLjYxNzE4OHptMCAxMC44MDg1OTRjNy4xMzY3MTkgMCAxMS40NDkyMTkgNC40NTMxMjUgMTEuNDQ5MjE5IDExLjgyMDMxMnY3LjM2NzE4OGMtOC0uNTc4MTI1LTE0LjgzMjAzMi0uNTc4MTI1LTIyLjg4MjgxMyAwdi03LjM2NzE4OGMwLTcuMzc4OTA2IDQuMzEyNS0xMS44MjAzMTIgMTEuNDMzNTk0LTExLjgyMDMxMnptLS4wNDY4NzUgMzAuMDM1MTU2YzkuOTU3MDMxIDAgMTguMzE2NDA2Ljc5Njg3NSAxOC4zMTY0MDYgMi4yMjI2NTZ2MjcuNTk3NjU2YzAgLjQyOTY4OC0uMDUwNzgxLjQ4MDQ2OS0uNDE3OTY4LjYzMjgxMy0uMzUxNTYzLjE2NDA2My0xNi45ODA0NjkgNi4zNTU0NjktMTYuOTgwNDY5IDYuMzU1NDY5cy0uNjcxODc1LjIxNDg0NC0uODcxMDk0LjIxNDg0NGMtLjIwNzAzMSAwLS44NjcxODctLjI2NTYyNi0uODY3MTg3LS4yNjU2MjZzLTE2LjYyODkwNy02LjE5MTQwNi0xNi45ODA0NjktNi4zNTU0NjhjLS4zNTU0NjktLjE2NDA2My0uNDE3OTY5LS4yMTQ4NDQtLjQxNzk2OS0uNjMyODEzdi0yNy41OTc2NTZjLS4wOTc2NTYtMS40MjU3ODEgOC4yNjE3MTktMi4xNzE4NzUgMTguMjE4NzUtMi4xNzE4NzV6bTAgMCIvPjwvc3ZnPg=='; supportedTransactionVersions: ReadonlySet = new Set(['legacy', 0]); private _derivationPath: string; private _wallet: TrezorConnect | null; - private _connectUrl?: string; + private _connectUrl: string | undefined; private _connecting: boolean; private _publicKey: PublicKey | null; private _readyState: WalletReadyState = @@ -47,7 +56,7 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { super(); this._derivationPath = config.derivationPath || `m/44'/501'/0'/0'`; this._wallet = null; - this._connectUrl = config.connectUrl && config.connectUrl + (config.connectUrl.endsWith('/') ? '' : '/'); + this._connectUrl = config.connectUrl?.replace(/\/*$/, '/'); this._connecting = false; this._publicKey = null; } @@ -71,52 +80,56 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { this._connecting = true; + let wallet: TrezorConnect; try { const { default: TrezorConnect } = await import('@trezor/connect-web'); - // @ts-ignore - this._wallet = TrezorConnect.default as TrezorConnect; + // @ts-expect-error // HACK: TrezorConnect.default is undefined. + wallet = TrezorConnect.default as TrezorConnect; } catch (error: any) { - throw new WalletConfigError(error?.message, error); + throw new WalletLoadError(error?.message, error); } - await this._wallet.init({ - manifest: { - email: 'gabriel.kerekes@vacuumlabs.com', - appUrl: 'https://github.com/solana-labs/wallet-adapter', - }, - lazyLoad: true, - ...(this._connectUrl - ? { - connectSrc: this._connectUrl, - iframeSrc: this._connectUrl, - } - : {}), - }); - - let result; try { - result = await this._wallet.solanaGetPublicKey({ - path: this._derivationPath, + await wallet.init({ + manifest: { + email: 'gabriel.kerekes@vacuumlabs.com', + appUrl: window.location.href, + }, + lazyLoad: true, + ...(this._connectUrl + ? { + connectSrc: this._connectUrl, + iframeSrc: this._connectUrl, + } + : {}), }); } catch (error: any) { - throw new WalletPublicKeyError(error?.message, error); + throw new WalletConfigError(error?.message, error); } + let result: Unsuccessful | Success; + try { + result = await wallet.solanaGetPublicKey({ path: this._derivationPath }); + } catch (error: any) { + throw new WalletAccountError(error?.message, error); + } if (!result.success) { - throw new WalletPublicKeyError(result.payload?.error, result.payload); + throw new WalletAccountError(result.payload?.error, result.payload); } - const publicKey = result.payload.publicKey; + let publicKey: PublicKey; + try { + publicKey = new PublicKey(Buffer.from(result.payload.publicKey, 'hex')); + } catch (error: any) { + throw new WalletPublicKeyError(error?.message, error); + } - this._wallet.on(DEVICE_EVENT, (event: any) => { - if (event.type === DEVICE.DISCONNECT) { - this._disconnected(); - } - }); + wallet.on('DEVICE_EVENT', this._onDeviceEvent); - this._publicKey = new PublicKey(Buffer.from(publicKey, 'hex')); + this._wallet = wallet; + this._publicKey = publicKey; - this.emit('connect', this._publicKey); + this.emit('connect', publicKey); } catch (error: any) { this.emit('error', error); throw error; @@ -126,15 +139,20 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { } async disconnect(): Promise { - this._publicKey = null; + const wallet = this._wallet; + if (wallet) { + this._wallet = null; + this._publicKey = null; - try { - await this._wallet?.dispose(); - } catch (error: any) { - this.emit('error', new WalletDisconnectionError(error?.message, error)); - } + try { + wallet.off('DEVICE_EVENT', this._onDeviceEvent); + await wallet.dispose(); + } catch (error: any) { + this.emit('error', new WalletDisconnectionError(error?.message, error)); + } - this.emit('disconnect'); + this.emit('disconnect'); + } } async signTransaction(transaction: T): Promise { @@ -147,7 +165,7 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { ? transaction.message.serialize() : transaction.serializeMessage(); - let result; + let result: Unsuccessful | Success; try { result = await wallet.solanaSignTransaction({ path: this._derivationPath, @@ -161,7 +179,12 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { throw new WalletSignTransactionError(result.payload?.error, result.payload); } - transaction.addSignature(publicKey, Buffer.from(result.payload.signature, 'hex')); + try { + const signature = Buffer.from(result.payload.signature, 'hex'); + transaction.addSignature(publicKey, signature); + } catch (error: any) { + throw new WalletSignTransactionError(error?.message, error); + } return transaction; } catch (error: any) { @@ -170,11 +193,27 @@ export class TrezorWalletAdapter extends BaseSignerWalletAdapter { } } - private _disconnected = () => { - this._wallet?.dispose(); - this._publicKey = null; + private _onDeviceEvent = (event: DeviceEventMessage) => { + if (event.type === 'device-disconnect') { + this._disconnected(); + } + }; + + private _disconnected = async () => { + const wallet = this._wallet; + if (wallet) { + this._wallet = null; + this._publicKey = null; + + try { + wallet.off('DEVICE_EVENT', this._onDeviceEvent); + wallet.dispose(); + } catch (error: any) { + this.emit('error', new WalletDisconnectionError(error?.message, error)); + } - this.emit('error', new WalletDisconnectedError()); - this.emit('disconnect'); + this.emit('error', new WalletDisconnectedError()); + this.emit('disconnect'); + } }; } diff --git a/packages/wallets/trezor/src/constants.ts b/packages/wallets/trezor/src/constants.ts deleted file mode 100644 index 982afc369..000000000 --- a/packages/wallets/trezor/src/constants.ts +++ /dev/null @@ -1,19 +0,0 @@ -// redeclared from TrezorConnect to allow async import -export const DEVICE_EVENT = 'DEVICE_EVENT'; -export const DEVICE = { - CONNECT: 'device-connect', - CONNECT_UNACQUIRED: 'device-connect_unacquired', - DISCONNECT: 'device-disconnect', - CHANGED: 'device-changed', - ACQUIRE: 'device-acquire', - RELEASE: 'device-release', - ACQUIRED: 'device-acquired', - RELEASED: 'device-released', - USED_ELSEWHERE: 'device-used_elsewhere', - LOADING: 'device-loading', - BUTTON: 'button', - PIN: 'pin', - PASSPHRASE: 'passphrase', - PASSPHRASE_ON_DEVICE: 'passphrase_on_device', - WORD: 'word', -}; diff --git a/packages/wallets/trezor/src/polyfills/Buffer.ts b/packages/wallets/trezor/src/polyfills/Buffer.ts new file mode 100644 index 000000000..3a82fd890 --- /dev/null +++ b/packages/wallets/trezor/src/polyfills/Buffer.ts @@ -0,0 +1,7 @@ +import { Buffer } from 'buffer'; + +if (typeof window !== 'undefined' && window.Buffer === undefined) { + (window as any).Buffer = Buffer; +} + +export {}; diff --git a/packages/wallets/trezor/src/polyfills/index.ts b/packages/wallets/trezor/src/polyfills/index.ts new file mode 100644 index 000000000..e9db0daf3 --- /dev/null +++ b/packages/wallets/trezor/src/polyfills/index.ts @@ -0,0 +1 @@ +import './Buffer.js';