diff --git a/.changeset/hot-hairs-sit.md b/.changeset/hot-hairs-sit.md new file mode 100644 index 00000000000..3c4e127d1dd --- /dev/null +++ b/.changeset/hot-hairs-sit.md @@ -0,0 +1,7 @@ +--- +"@fuel-ts/crypto": minor +"@fuel-ts/wallet": minor +--- + +- Now exporting `randomUUID` from `@fuel-ts/crypto` +- Bumping `@fuel-ts/wallet` because it's consuming `randomUUID` diff --git a/packages/crypto/src/browser/crypto.ts b/packages/crypto/src/browser/crypto.ts index b0dcb8f078a..0f803b32bc5 100644 --- a/packages/crypto/src/browser/crypto.ts +++ b/packages/crypto/src/browser/crypto.ts @@ -9,6 +9,27 @@ if (!crypto) { ); } +if (!crypto.randomUUID) { + throw new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto.randomUUID' in current browser environment.` + ); +} + +if (!crypto.subtle) { + throw new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto.subtle' in current browser environment.` + ); +} + +if (!crypto.getRandomValues) { + throw new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto.getRandomValues' in current browser environment.` + ); +} + if (!btoa) { throw new FuelError( ErrorCode.ENV_DEPENDENCY_MISSING, diff --git a/packages/crypto/src/browser/index.ts b/packages/crypto/src/browser/index.ts index b60afdfbe2a..fc884602c5f 100644 --- a/packages/crypto/src/browser/index.ts +++ b/packages/crypto/src/browser/index.ts @@ -18,6 +18,7 @@ const api: CryptoApi = { keccak256, decryptJsonWalletData, encryptJsonWalletData, + randomUUID: crypto.randomUUID, }; export default api; diff --git a/packages/crypto/src/index.browser.ts b/packages/crypto/src/index.browser.ts index d461ad391ff..fc599597aab 100644 --- a/packages/crypto/src/index.browser.ts +++ b/packages/crypto/src/index.browser.ts @@ -13,4 +13,5 @@ export const { keccak256, decryptJsonWalletData, encryptJsonWalletData, + randomUUID, } = cryptoApi; diff --git a/packages/crypto/src/index.ts b/packages/crypto/src/index.ts index b8ce4fc2412..10443ebf31a 100644 --- a/packages/crypto/src/index.ts +++ b/packages/crypto/src/index.ts @@ -13,4 +13,5 @@ export const { keccak256, decryptJsonWalletData, encryptJsonWalletData, + randomUUID, } = cryptoApi; diff --git a/packages/crypto/src/node/index.ts b/packages/crypto/src/node/index.ts index b60afdfbe2a..ecef378e44e 100644 --- a/packages/crypto/src/node/index.ts +++ b/packages/crypto/src/node/index.ts @@ -1,3 +1,5 @@ +import crypto from 'crypto'; + import { scrypt, keccak256 } from '../shared'; import type { CryptoApi } from '../types'; @@ -18,6 +20,7 @@ const api: CryptoApi = { keccak256, decryptJsonWalletData, encryptJsonWalletData, + randomUUID: crypto.randomUUID, }; export default api; diff --git a/packages/crypto/src/types.ts b/packages/crypto/src/types.ts index 83f0493cb62..a273e84fb0c 100644 --- a/packages/crypto/src/types.ts +++ b/packages/crypto/src/types.ts @@ -26,4 +26,5 @@ export interface CryptoApi { keccak256(data: Uint8Array): Uint8Array; encryptJsonWalletData(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise; decryptJsonWalletData(data: Uint8Array, key: Uint8Array, iv: Uint8Array): Promise; + randomUUID(): string; } diff --git a/packages/crypto/test/crypto-browser/btoa-undefined.test.ts b/packages/crypto/test/crypto-browser/btoa-undefined.test.ts new file mode 100644 index 00000000000..de955cb16e5 --- /dev/null +++ b/packages/crypto/test/crypto-browser/btoa-undefined.test.ts @@ -0,0 +1,26 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { CryptoMock } from './crypto-mock'; + +/** + * @group node + */ +describe('throws when btoa is unavailable', () => { + test('btoa is undefined', async () => { + if (!globalThis.crypto) { + // this is for node v18 where globalThis.crypto is undefined + // these are tests for the browser environment anyways so doing this is okay + vi.stubGlobal('crypto', new CryptoMock()); + } + vi.stubGlobal('btoa', undefined); + + await expectToThrowFuelError( + () => import('../../src/browser/crypto'), + new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'btoa' in current browser environment.` + ) + ); + }); +}); diff --git a/packages/crypto/test/crypto-browser/crypto-getRandomValues-undefined.test.ts b/packages/crypto/test/crypto-browser/crypto-getRandomValues-undefined.test.ts new file mode 100644 index 00000000000..d16344b92c9 --- /dev/null +++ b/packages/crypto/test/crypto-browser/crypto-getRandomValues-undefined.test.ts @@ -0,0 +1,21 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { CryptoMock } from './crypto-mock'; + +/** + * @group node + */ +describe('throws when crypto.getRandomValues is unavailable', () => { + test('crypto.getRandomValues is undefined', async () => { + vi.stubGlobal('crypto', new CryptoMock('getRandomValues')); + + await expectToThrowFuelError( + () => import('../../src/browser/crypto'), + new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto.getRandomValues' in current browser environment.` + ) + ); + }); +}); diff --git a/packages/crypto/test/crypto-browser/crypto-mock.ts b/packages/crypto/test/crypto-browser/crypto-mock.ts new file mode 100644 index 00000000000..6f7d9ddc65d --- /dev/null +++ b/packages/crypto/test/crypto-browser/crypto-mock.ts @@ -0,0 +1,17 @@ +import * as cr from 'crypto'; + +export class CryptoMock { + constructor(private toUndefined?: 'subtle' | 'randomUUID' | 'getRandomValues') {} + + get subtle() { + return this.toUndefined === 'subtle' ? undefined : cr.subtle; + } + + get randomUUID() { + return this.toUndefined === 'randomUUID' ? undefined : cr.randomUUID; + } + + get getRandomValues() { + return this.toUndefined === 'getRandomValues' ? undefined : cr.getRandomValues; + } +} diff --git a/packages/crypto/test/crypto-browser/crypto-randomUUID-undefined.test.ts b/packages/crypto/test/crypto-browser/crypto-randomUUID-undefined.test.ts new file mode 100644 index 00000000000..fa71c97f1ab --- /dev/null +++ b/packages/crypto/test/crypto-browser/crypto-randomUUID-undefined.test.ts @@ -0,0 +1,21 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { CryptoMock } from './crypto-mock'; + +/** + * @group node + */ +describe('throws when crypto.randomUUID is unavailable', () => { + test('crypto.randomUUID is undefined', async () => { + vi.stubGlobal('crypto', new CryptoMock('randomUUID')); + + await expectToThrowFuelError( + () => import('../../src/browser/crypto'), + new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto.randomUUID' in current browser environment.` + ) + ); + }); +}); diff --git a/packages/crypto/test/crypto-browser/crypto-subtle-undefined.test.ts b/packages/crypto/test/crypto-browser/crypto-subtle-undefined.test.ts new file mode 100644 index 00000000000..a8f7b491f27 --- /dev/null +++ b/packages/crypto/test/crypto-browser/crypto-subtle-undefined.test.ts @@ -0,0 +1,21 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +import { CryptoMock } from './crypto-mock'; + +/** + * @group node + */ +describe('throws when crypto.subtle is unavailable', () => { + test('crypto.subtle is undefined', async () => { + vi.stubGlobal('crypto', new CryptoMock('subtle')); + + await expectToThrowFuelError( + () => import('../../src/browser/crypto'), + new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto.subtle' in current browser environment.` + ) + ); + }); +}); diff --git a/packages/crypto/test/crypto-browser/crypto-undefined.test.ts b/packages/crypto/test/crypto-browser/crypto-undefined.test.ts new file mode 100644 index 00000000000..1f46406d623 --- /dev/null +++ b/packages/crypto/test/crypto-browser/crypto-undefined.test.ts @@ -0,0 +1,19 @@ +import { ErrorCode, FuelError } from '@fuel-ts/errors'; +import { expectToThrowFuelError } from '@fuel-ts/errors/test-utils'; + +/** + * @group node + */ +describe('throws when crypto is unavailable', () => { + test('crypto is undefined', async () => { + vi.stubGlobal('crypto', undefined); + + await expectToThrowFuelError( + () => import('../../src/browser/crypto'), + new FuelError( + ErrorCode.ENV_DEPENDENCY_MISSING, + `Could not find 'crypto' in current browser environment.` + ) + ); + }); +}); diff --git a/packages/wallet/package.json b/packages/wallet/package.json index c567f60ef7c..41d8bde69c0 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -62,10 +62,6 @@ "@fuel-ts/errors": "workspace:*", "ethers": "^6.7.1", "portfinder": "^1.0.32", - "tree-kill": "^1.2.2", - "uuid": "^9.0.0" - }, - "devDependencies": { - "@types/uuid": "^9.0.1" + "tree-kill": "^1.2.2" } } diff --git a/packages/wallet/src/keystore-wallet.ts b/packages/wallet/src/keystore-wallet.ts index 145c43c9de1..19a060076ef 100644 --- a/packages/wallet/src/keystore-wallet.ts +++ b/packages/wallet/src/keystore-wallet.ts @@ -7,11 +7,11 @@ import { stringFromBuffer, decryptJsonWalletData, encryptJsonWalletData, + randomUUID, } from '@fuel-ts/crypto'; import { ErrorCode, FuelError } from '@fuel-ts/errors'; import type { AbstractAddress } from '@fuel-ts/interfaces'; import { hexlify } from 'ethers'; -import { v4 as uuidv4 } from 'uuid'; export type KeystoreWallet = { id: string; @@ -90,7 +90,7 @@ export async function encryptKeystoreWallet( // Construct keystore. const keystore: KeystoreWallet = { - id: uuidv4(), + id: randomUUID(), version: 3, address: removeHexPrefix(ownerAddress.toHexString()), crypto: { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 502b2e7327a..0357fce6e5d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1220,13 +1220,6 @@ importers: tree-kill: specifier: ^1.2.2 version: 1.2.2 - uuid: - specifier: ^9.0.0 - version: 9.0.0 - devDependencies: - '@types/uuid': - specifier: ^9.0.1 - version: 9.0.1 packages/wallet-manager: dependencies: @@ -9173,10 +9166,6 @@ packages: resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==} dev: true - /@types/uuid@9.0.1: - resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} - dev: true - /@types/web-bluetooth@0.0.16: resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} dev: true @@ -22294,11 +22283,6 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - /uuid@9.0.0: - resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} - hasBin: true - dev: false - /v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true