diff --git a/package.json b/package.json index 79451216..977cae62 100644 --- a/package.json +++ b/package.json @@ -57,22 +57,16 @@ "browser": "dist/browser/index.js", "types": "dist/index.d.ts", "files": [ - "dist/index.cjs", - "dist/index.cjs.map", - "dist/index.d.ts", - "dist/index.js", - "dist/index.js.map", - "dist/browser/index.js", - "dist/browser/index.js.map", - "dist/bundler/index.js", - "dist/bundler/index.js.map" + "dist", + "src", + "tsconfig.json" ], "scripts": { "autolint": "prettier --write . && eslint --fix .", "bench": "yarn build:bench && node dist/bench/index.cjs", "build": "yarn clean:dist && rollup -c", "build:bench": "rollup -c rollup.config.bench.js", - "build:docs": "yarn clean:docs && mkdir -p tmp && cp README.md tmp/ && sed -i.bak '/badge.svg/d' tmp/README.md && typedoc --excludePrivate --readme tmp/README.md src/index.ts", + "build:docs": "yarn clean:docs && mkdir -p tmp && cp README.md tmp/ && sed -i.bak '/badge.svg/d' tmp/README.md && typedoc", "clean": "yarn clean:artifacts && yarn clean:dist && yarn clean:docs && yarn clean:deps", "clean:artifacts": "rm -rf docs tmp package.tgz", "clean:deps": "rm -rf node_modules", @@ -108,9 +102,8 @@ "@xmtp/user-preferences-bindings-wasm": "^0.3.6", "async-mutex": "^0.4.1", "elliptic": "^6.5.4", - "ethers": "^5.7.2", "long": "^5.2.3", - "viem": "^1.21.4" + "viem": "^2.7.8" }, "devDependencies": { "@commitlint/cli": "17.8.1", @@ -126,6 +119,7 @@ "@types/node": "^18.19.18", "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", + "@vitest/coverage-v8": "^1.3.1", "@xmtp/rollup-plugin-resolve-extensions": "1.0.1", "benny": "^3.7.1", "dd-trace": "^5.5.0", @@ -138,6 +132,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-promise": "^6.1.1", + "ethers": "^5.7.2", "happy-dom": "^13.6.2", "husky": "^7.0.4", "prettier": "^3.2.5", diff --git a/rollup.config.bench.js b/rollup.config.bench.js index 3b465dea..9d906038 100644 --- a/rollup.config.bench.js +++ b/rollup.config.bench.js @@ -11,8 +11,8 @@ const external = [ 'benny', 'crypto', 'elliptic', - 'ethers', 'long', + 'viem', ] const plugins = [ diff --git a/rollup.config.js b/rollup.config.js index da943f30..7bbd085b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -15,7 +15,6 @@ const external = [ 'async-mutex', 'crypto', 'elliptic', - 'ethers', 'long', 'viem', ] diff --git a/src/Client.ts b/src/Client.ts index cf83e247..4b915728 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -10,7 +10,6 @@ import { EnvelopeMapperWithMessage, EnvelopeWithMessage, } from './utils' -import { utils } from 'ethers' import { Signer } from './types/Signer' import { Conversations } from './conversations' import { ContentTypeText, TextCodec } from './codecs/Text' @@ -44,7 +43,7 @@ import { import { hasMetamaskWithSnaps } from './keystore/snapHelpers' import { packageName, version } from './snapInfo.json' import { ExtractDecodedType } from './types/client' -import type { WalletClient } from 'viem' +import { getAddress, type WalletClient } from 'viem' import { Contacts } from './Contacts' import { KeystoreInterfaces } from './keystore/rpcDefinitions' const { Compression } = proto @@ -430,7 +429,7 @@ export default class Client { async getUserContact( peerAddress: string ): Promise { - peerAddress = utils.getAddress(peerAddress) // EIP55 normalize the address case. + peerAddress = getAddress(peerAddress) // EIP55 normalize the address case. const existingBundle = this.knownPublicKeyBundles.get(peerAddress) if (existingBundle) { return existingBundle @@ -456,7 +455,7 @@ export default class Client { ): Promise<(PublicKeyBundle | SignedPublicKeyBundle | undefined)[]> { // EIP55 normalize all peer addresses const normalizedAddresses = peerAddresses.map((address) => - utils.getAddress(address) + getAddress(address) ) // The logic here is tricky because we need to do a batch query for any uncached bundles, // then interleave back into an ordered array. So we create a map @@ -501,7 +500,7 @@ export default class Client { * Used to force getUserContact fetch contact from the network. */ forgetContact(peerAddress: string) { - peerAddress = utils.getAddress(peerAddress) // EIP55 normalize the address case. + peerAddress = getAddress(peerAddress) // EIP55 normalize the address case. this.knownPublicKeyBundles.delete(peerAddress) } @@ -552,7 +551,7 @@ export default class Client { const rawPeerAddresses: string[] = peerAddress // Try to normalize each of the peer addresses const normalizedPeerAddresses = rawPeerAddresses.map((address) => - utils.getAddress(address) + getAddress(address) ) // The getUserContactsFromNetwork will return false instead of throwing // on invalid envelopes @@ -563,7 +562,7 @@ export default class Client { return contacts.map((contact) => !!contact) } try { - peerAddress = utils.getAddress(peerAddress) // EIP55 normalize the address case. + peerAddress = getAddress(peerAddress) // EIP55 normalize the address case. } catch (e) { return false } diff --git a/src/authn/LocalAuthenticator.ts b/src/authn/LocalAuthenticator.ts index 425b7538..bf8edcd6 100644 --- a/src/authn/LocalAuthenticator.ts +++ b/src/authn/LocalAuthenticator.ts @@ -1,9 +1,8 @@ import { authn, signature, publicKey } from '@xmtp/proto' import AuthData from './AuthData' import { PrivateKey } from '../crypto' -import { hexToBytes } from '../crypto/utils' import Token from './Token' -import { keccak256 } from 'viem' +import { hexToBytes, keccak256 } from 'viem' export default class LocalAuthenticator { private identityKey: PrivateKey diff --git a/src/conversations/Conversation.ts b/src/conversations/Conversation.ts index a12b4a47..05b7dc10 100644 --- a/src/conversations/Conversation.ts +++ b/src/conversations/Conversation.ts @@ -6,7 +6,6 @@ import { concat, toNanoString, } from '../utils' -import { utils } from 'ethers' import Stream from '../Stream' import Client, { ListMessagesOptions, @@ -33,6 +32,7 @@ import { sha256 } from '../crypto/encryption' import { buildDecryptV1Request, getResultOrThrow } from '../utils/keystore' import { ContentTypeText } from '../codecs/Text' import { ConsentState } from '../Contacts' +import { getAddress } from 'viem' /** * Conversation represents either a V1 or V2 conversation with a common set of methods. @@ -178,7 +178,7 @@ export class ConversationV1 private client: Client constructor(client: Client, address: string, createdAt: Date) { - this.peerAddress = utils.getAddress(address) + this.peerAddress = getAddress(address) this.client = client this.createdAt = createdAt } diff --git a/src/crypto/PublicKey.ts b/src/crypto/PublicKey.ts index eeb8da39..2d284e99 100644 --- a/src/crypto/PublicKey.ts +++ b/src/crypto/PublicKey.ts @@ -2,10 +2,10 @@ import { publicKey } from '@xmtp/proto' import * as secp from '@noble/secp256k1' import Long from 'long' import Signature, { WalletSigner } from './Signature' -import { equalBytes, hexToBytes } from './utils' -import { utils } from 'ethers' +import { computeAddress, equalBytes, splitSignature } from './utils' import { Signer } from '../types/Signer' import { sha256 } from './encryption' +import { hashMessage, Hex, hexToBytes } from 'viem' // SECP256k1 public key in uncompressed format with prefix type secp256k1Uncompressed = { @@ -90,7 +90,7 @@ export class UnsignedPublicKey implements publicKey.UnsignedPublicKey { // Derive Ethereum address from this public key. getEthereumAddress(): string { - return utils.computeAddress(this.secp256k1Uncompressed.bytes) + return computeAddress(this.secp256k1Uncompressed.bytes) } // Encode public key into bytes. @@ -256,16 +256,11 @@ export class PublicKey const sigString = await wallet.signMessage( WalletSigner.identitySigRequestText(this.bytesToSign()) ) - const eSig = utils.splitSignature(sigString) - const r = hexToBytes(eSig.r) - const s = hexToBytes(eSig.s) - const sigBytes = new Uint8Array(64) - sigBytes.set(r) - sigBytes.set(s, r.length) + const { bytes, recovery } = splitSignature(sigString as Hex) this.signature = new Signature({ ecdsaCompact: { - bytes: sigBytes, - recovery: eSig.recoveryParam, + bytes, + recovery, }, }) } @@ -278,7 +273,7 @@ export class PublicKey throw new Error('key is not signed') } const digest = hexToBytes( - utils.hashMessage(WalletSigner.identitySigRequestText(this.bytesToSign())) + hashMessage(WalletSigner.identitySigRequestText(this.bytesToSign())) ) const pk = this.signature.getPublicKey(digest) if (!pk) { diff --git a/src/crypto/Signature.ts b/src/crypto/Signature.ts index 906be632..55e0cfea 100644 --- a/src/crypto/Signature.ts +++ b/src/crypto/Signature.ts @@ -3,9 +3,9 @@ import Long from 'long' import * as secp from '@noble/secp256k1' import { PublicKey, UnsignedPublicKey, SignedPublicKey } from './PublicKey' import { SignedPrivateKey } from './PrivateKey' -import { utils } from 'ethers' import { Signer } from '../types/Signer' -import { bytesToHex, equalBytes, hexToBytes } from './utils' +import { bytesToHex, equalBytes, splitSignature } from './utils' +import { Hex, hashMessage, hexToBytes } from 'viem' // ECDSA signature with recovery bit. export type ECDSACompactWithRecovery = { @@ -164,7 +164,7 @@ export class WalletSigner implements KeySigner { signature: ECDSACompactWithRecovery ): UnsignedPublicKey | undefined { const digest = hexToBytes( - utils.hashMessage(this.identitySigRequestText(key.bytesToSign())) + hashMessage(this.identitySigRequestText(key.bytesToSign())) ) return ecdsaSignerKey(digest, signature) } @@ -174,16 +174,11 @@ export class WalletSigner implements KeySigner { const sigString = await this.wallet.signMessage( WalletSigner.identitySigRequestText(keyBytes) ) - const eSig = utils.splitSignature(sigString) - const r = hexToBytes(eSig.r) - const s = hexToBytes(eSig.s) - const sigBytes = new Uint8Array(64) - sigBytes.set(r) - sigBytes.set(s, r.length) + const { bytes, recovery } = splitSignature(sigString as Hex) const signature = new Signature({ walletEcdsaCompact: { - bytes: sigBytes, - recovery: eSig.recoveryParam, + bytes, + recovery, }, }) return new SignedPublicKey({ keyBytes, signature }) diff --git a/src/crypto/utils.ts b/src/crypto/utils.ts index 3f2a269c..4fe5381a 100644 --- a/src/crypto/utils.ts +++ b/src/crypto/utils.ts @@ -1,19 +1,15 @@ import * as secp from '@noble/secp256k1' +import { + Hex, + getAddress, + hexToSignature, + keccak256, + hexToBytes, + bytesToHex as viemBytesToHex, +} from 'viem' export const bytesToHex = secp.utils.bytesToHex -export function hexToBytes(s: string): Uint8Array { - if (s.startsWith('0x')) { - s = s.slice(2) - } - const bytes = new Uint8Array(s.length / 2) - for (let i = 0; i < bytes.length; i++) { - const j = i * 2 - bytes[i] = Number.parseInt(s.slice(j, j + 2), 16) - } - return bytes -} - export function bytesToBase64(bytes: Uint8Array): string { return Buffer.from(bytes).toString('base64') } @@ -29,3 +25,31 @@ export function equalBytes(b1: Uint8Array, b2: Uint8Array): boolean { } return true } + +/** + * Compute the Ethereum address from uncompressed PublicKey bytes + */ +export function computeAddress(bytes: Uint8Array) { + const publicKey = viemBytesToHex(bytes.slice(1)) as Hex + const hash = keccak256(publicKey) + const address = hash.substring(hash.length - 40) + return getAddress(`0x${address}`) +} + +/** + * Split an Ethereum signature hex string into bytes and a recovery bit + */ +export function splitSignature(signature: Hex) { + const eSig = hexToSignature(signature) + const r = hexToBytes(eSig.r) + const s = hexToBytes(eSig.s) + let v = Number(eSig.v) + if (v === 0 || v === 1) { + v += 27 + } + const recovery = 1 - (v % 2) + const bytes = new Uint8Array(64) + bytes.set(r) + bytes.set(s, r.length) + return { bytes, recovery } +} diff --git a/src/keystore/providers/NetworkKeyManager.ts b/src/keystore/providers/NetworkKeyManager.ts index 351d74ca..065946bd 100644 --- a/src/keystore/providers/NetworkKeyManager.ts +++ b/src/keystore/providers/NetworkKeyManager.ts @@ -1,4 +1,3 @@ -import { utils } from 'ethers' import { Signer } from '../../types/Signer' import crypto from '../../crypto/crypto' import { @@ -10,10 +9,11 @@ import { } from '../../crypto' import type { PreEventCallback } from '../../Client' import { LocalAuthenticator } from '../../authn' -import { bytesToHex, hexToBytes } from '../../crypto/utils' +import { bytesToHex } from '../../crypto/utils' import Ciphertext from '../../crypto/Ciphertext' import { privateKey as proto } from '@xmtp/proto' import TopicPersistence from '../persistence/TopicPersistence' +import { Hex, getAddress, hexToBytes, verifyMessage } from 'viem' const KEY_BUNDLE_NAME = 'key_bundle' /** @@ -39,7 +39,7 @@ export default class NetworkKeyManager { // I think we want to namespace the storage address by wallet // This will allow us to support switching between multiple wallets in the same browser let walletAddress = await this.signer.getAddress() - walletAddress = utils.getAddress(walletAddress) + walletAddress = getAddress(walletAddress) return `${walletAddress}/${name}` } @@ -91,24 +91,23 @@ export default class NetworkKeyManager { if (this.preEnableIdentityCallback) { await this.preEnableIdentityCallback() } - let sig = await wallet.signMessage(input) + const sig = await wallet.signMessage(input) // Check that the signature is correct, was created using the expected // input, and retry if not. This mitigates a bug in interacting with // LedgerLive for iOS, where the previous signature response is // returned in some cases. - let address = utils.verifyMessage(input, sig) - if (address !== walletAddr) { - sig = await wallet.signMessage(input) - console.log('invalid signature, retrying') - - address = utils.verifyMessage(input, sig) - if (address !== walletAddr) { - throw new Error('invalid signature') - } + const valid = verifyMessage({ + address: walletAddr as `0x${string}`, + message: input, + signature: sig as Hex, + }) + + if (!valid) { + throw new Error('invalid signature') } - const secret = hexToBytes(sig) + const secret = hexToBytes(sig as Hex) const ciphertext = await encrypt(bytes, secret) return proto.EncryptedPrivateKeyBundle.encode({ v1: { @@ -136,7 +135,9 @@ export default class NetworkKeyManager { await this.preEnableIdentityCallback() } const secret = hexToBytes( - await wallet.signMessage(storageSigRequestText(eBundle.walletPreKey)) + (await wallet.signMessage( + storageSigRequestText(eBundle.walletPreKey) + )) as Hex ) // Ledger uses the last byte = v=[0,1,...] but Metamask and other wallets generate with diff --git a/src/utils/topic.ts b/src/utils/topic.ts index 98d28006..f706d321 100644 --- a/src/utils/topic.ts +++ b/src/utils/topic.ts @@ -1,4 +1,4 @@ -import { utils } from 'ethers' +import { getAddress } from 'viem' export const buildContentTopic = (name: string): string => `/xmtp/0/${name}/proto` @@ -8,7 +8,7 @@ export const buildDirectMessageTopic = ( recipient: string ): string => { // EIP55 normalize the address case. - const members = [utils.getAddress(sender), utils.getAddress(recipient)] + const members = [getAddress(sender), getAddress(recipient)] members.sort() return buildContentTopic(`dm-${members.join('-')}`) } @@ -19,17 +19,17 @@ export const buildDirectMessageTopicV2 = (randomString: string): string => { export const buildUserContactTopic = (walletAddr: string): string => { // EIP55 normalize the address case. - return buildContentTopic(`contact-${utils.getAddress(walletAddr)}`) + return buildContentTopic(`contact-${getAddress(walletAddr)}`) } export const buildUserIntroTopic = (walletAddr: string): string => { // EIP55 normalize the address case. - return buildContentTopic(`intro-${utils.getAddress(walletAddr)}`) + return buildContentTopic(`intro-${getAddress(walletAddr)}`) } export const buildUserInviteTopic = (walletAddr: string): string => { // EIP55 normalize the address case. - return buildContentTopic(`invite-${utils.getAddress(walletAddr)}`) + return buildContentTopic(`invite-${getAddress(walletAddr)}`) } export const buildUserPrivateStoreTopic = (addrPrefixedKey: string): string => { diff --git a/test/authn/Authn.test.ts b/test/authn/Authn.test.ts index 0e0170fd..c720744e 100644 --- a/test/authn/Authn.test.ts +++ b/test/authn/Authn.test.ts @@ -2,11 +2,10 @@ import Long from 'long' import { PrivateKey, PrivateKeyBundleV1, Signature } from '../../src/crypto' import Authenticator from '../../src/authn/LocalAuthenticator' import Token from '../../src/authn/Token' -import { hexToBytes } from '../../src/crypto/utils' import { newWallet, sleep } from '../helpers' import { Wallet } from 'ethers' import AuthCache from '../../src/authn/AuthCache' -import { keccak256 } from 'viem' +import { hexToBytes, keccak256 } from 'viem' describe('authn', () => { let authenticator: Authenticator diff --git a/test/conversations/Conversation.test.ts b/test/conversations/Conversation.test.ts index cbbfc660..5407df30 100644 --- a/test/conversations/Conversation.test.ts +++ b/test/conversations/Conversation.test.ts @@ -394,9 +394,7 @@ describe('conversation', () => { }) it('throws when opening a conversation with an unknown address', () => { - expect(alice.conversations.newConversation('0xfoo')).rejects.toThrow( - 'invalid address' - ) + expect(alice.conversations.newConversation('0xfoo')).rejects.toThrow() const validButUnknown = '0x1111111111222222222233333333334444444444' expect( alice.conversations.newConversation(validButUnknown) @@ -405,19 +403,13 @@ describe('conversation', () => { ) }) - it('normalizes upper and lowercase addresses', async () => { + it('normalizes lowercase addresses', async () => { const bobLower = bob.address.toLowerCase() - const bobUpper = '0x' + bob.address.substring(2).toUpperCase() await expect( alice.conversations.newConversation(bobLower) ).resolves.toMatchObject({ peerAddress: bob.address, }) - await expect( - alice.conversations.newConversation(bobUpper) - ).resolves.toMatchObject({ - peerAddress: bob.address, - }) }) it('filters out spoofed messages', async () => { diff --git a/test/crypto/PrivateKeyBundle.test.ts b/test/crypto/PrivateKeyBundle.test.ts index c53b7888..dcf4cab6 100644 --- a/test/crypto/PrivateKeyBundle.test.ts +++ b/test/crypto/PrivateKeyBundle.test.ts @@ -5,9 +5,9 @@ import { PrivateKeyBundleV2, SignedPublicKeyBundle, } from '../../src/crypto' -import { hexToBytes } from '../../src/crypto/utils' import { newWallet } from '../helpers' import { storageSigRequestText } from '../../src/keystore/providers/NetworkKeyManager' +import { hexToBytes } from 'viem' describe('Crypto', function () { describe('PrivateKeyBundle', function () { @@ -30,14 +30,14 @@ describe('Crypto', function () { it('human-friendly storage signature request text', async function () { const pri = PrivateKey.fromBytes( hexToBytes( - '08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a' + '0x08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a' ) ) expect(pri.secp256k1).toBeTruthy() const wallet = newWallet() const bundle = await PrivateKeyBundleV1.generate(wallet) const preKey = hexToBytes( - 'f51bd1da9ec2239723ae2cf6a9f8d0ac37546b27e634002c653d23bacfcc67ad' + '0xf51bd1da9ec2239723ae2cf6a9f8d0ac37546b27e634002c653d23bacfcc67ad' ) const actual = storageSigRequestText(preKey) const expected = diff --git a/test/crypto/PublicKey.test.ts b/test/crypto/PublicKey.test.ts index 521d2ca0..c91d28c0 100644 --- a/test/crypto/PublicKey.test.ts +++ b/test/crypto/PublicKey.test.ts @@ -5,12 +5,12 @@ import { SignedPublicKey, SignedPrivateKey, WalletSigner, - utils, Signature, } from '../../src/crypto' import { Wallet } from 'ethers' -import { hexToBytes, equalBytes } from '../../src/crypto/utils' +import { equalBytes } from '../../src/crypto/utils' import { newWallet } from '../helpers' +import { hexToBytes } from 'viem' describe('Crypto', function () { describe('Signed Keys', function () { @@ -67,8 +67,8 @@ describe('Crypto', function () { describe('PublicKey', function () { it('derives address from public key', function () { // using the sample from https://kobl.one/blog/create-full-ethereum-keypair-and-address/ - const bytes = utils.hexToBytes( - '04836b35a026743e823a90a0ee3b91bf615c6a757e2b60b9e1dc1826fd0dd16106f7bc1e8179f665015f43c6c81f39062fc2086ed849625c06e04697698b21855e' + const bytes = hexToBytes( + '0x04836b35a026743e823a90a0ee3b91bf615c6a757e2b60b9e1dc1826fd0dd16106f7bc1e8179f665015f43c6c81f39062fc2086ed849625c06e04697698b21855e' ) const pub = new PublicKey({ secp256k1Uncompressed: { bytes }, @@ -81,7 +81,7 @@ describe('Crypto', function () { it('human-friendly identity key signature request', async function () { const alice = PrivateKey.fromBytes( hexToBytes( - '08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a' + '0x08aaa9dad3ed2f12220a206fd789a6ee2376bb6595b4ebace57c7a79e6e4f1f12c8416d611399eda6c74cb1a4c08aaa9dad3ed2f1a430a4104e208133ea0973a9968fe5362e5ac0a8bbbe2aa16d796add31f3d027a1b894389873d7f282163bceb1fc3ca60d589d1e667956c40fed4cdaa7edc1392d2100b8a' ) ) const actual = WalletSigner.identitySigRequestText( diff --git a/test/keystore/InMemoryKeystore.test.ts b/test/keystore/InMemoryKeystore.test.ts index c2d514a5..bbee6bdd 100644 --- a/test/keystore/InMemoryKeystore.test.ts +++ b/test/keystore/InMemoryKeystore.test.ts @@ -18,8 +18,8 @@ import { InMemoryPersistence } from '../../src/keystore/persistence' import Token from '../../src/authn/Token' import Long from 'long' import { CreateInviteResponse } from '@xmtp/proto/ts/dist/types/keystore_api/v1/keystore.pb' -import { ethers } from 'ethers' import { assert } from 'vitest' +import { toBytes } from 'viem' describe('InMemoryKeystore', () => { let aliceKeys: PrivateKeyBundleV1 @@ -526,7 +526,7 @@ describe('InMemoryKeystore', () => { it('generates known deterministic topic', async () => { aliceKeys = new PrivateKeyBundleV1( privateKey.PrivateKeyBundle.decode( - ethers.utils.arrayify( + toBytes( '0x0a8a030ac20108c192a3f7923112220a2068d2eb2ef8c50c4916b42ce638c5610e44ff4eb3ecb098' + 'c9dacf032625c72f101a940108c192a3f7923112460a440a40fc9822283078c323c9319c45e60ab4' + '2c65f6e1744ed8c23c52728d456d33422824c98d307e8b1c86a26826578523ba15fe6f04a17fca17' + @@ -546,7 +546,7 @@ describe('InMemoryKeystore', () => { ) bobKeys = new PrivateKeyBundleV1( privateKey.PrivateKeyBundle.decode( - ethers.utils.arrayify( + toBytes( '0x0a88030ac001088cd68df7923112220a209057f8d813314a2aae74e6c4c30f909c1c496b6037ce32' + 'a12c613558a8e961681a9201088cd68df7923112440a420a40501ae9b4f75d5bb5bae3ca4ecfda4e' + 'de9edc5a9b7fc2d56dc7325b837957c23235cc3005b46bb9ef485f106404dcf71247097ed5096355' + diff --git a/test/keystore/providers/NetworkKeystoreProvider.test.ts b/test/keystore/providers/NetworkKeystoreProvider.test.ts index 10c5c7e2..ba2449fa 100644 --- a/test/keystore/providers/NetworkKeystoreProvider.test.ts +++ b/test/keystore/providers/NetworkKeystoreProvider.test.ts @@ -10,10 +10,10 @@ import NetworkKeyManager, { storageSigRequestText, } from '../../../src/keystore/providers/NetworkKeyManager' import TopicPersistence from '../../../src/keystore/persistence/TopicPersistence' -import { hexToBytes } from '../../../src/crypto/utils' import { LocalAuthenticator } from '../../../src/authn' import crypto from '../../../src/crypto/crypto' import { vi } from 'vitest' +import { Hex, hexToBytes } from 'viem' describe('NetworkKeystoreProvider', () => { let apiClient: ApiClient @@ -59,7 +59,7 @@ describe('NetworkKeystoreProvider', () => { const walletAddr = await wallet.getAddress() let sig = await wallet.signMessage(input) - const secret = hexToBytes(sig) + const secret = hexToBytes(sig as Hex) const ciphertext = await encrypt(bytes, secret) const bytesToStore = privateKey.EncryptedPrivateKeyBundleV1.encode({ ciphertext, diff --git a/tsconfig.json b/tsconfig.json index 5cb2466d..110a2c9b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,15 +1,16 @@ { "compilerOptions": { - "target": "es2021", - "lib": ["dom"], - "sourceMap": true, - "noEmit": true, "downlevelIteration": true, - "strict": true, - "moduleResolution": "bundler", - "resolveJsonModule": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, + "lib": ["dom"], + "moduleResolution": "bundler", + "noEmit": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "es2021", "types": ["vitest/globals"] }, "include": ["src/**/*", "bench/**/*", "build/**/*", "test/**/*"] diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 00000000..978acf32 --- /dev/null +++ b/typedoc.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "entryPoints": ["src/index.ts"], + "excludePrivate": true, + "readme": "tmp/README.md" +} diff --git a/yarn.lock b/yarn.lock index 0bffe4bf..230efb0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19,6 +19,16 @@ __metadata: languageName: node linkType: hard +"@ampproject/remapping@npm:^2.2.1": + version: 2.2.1 + resolution: "@ampproject/remapping@npm:2.2.1" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.0" + "@jridgewell/trace-mapping": "npm:^0.3.9" + checksum: 10/e15fecbf3b54c988c8b4fdea8ef514ab482537e8a080b2978cc4b47ccca7140577ca7b65ad3322dcce65bc73ee6e5b90cbfe0bbd8c766dad04d5c62ec9634c42 + languageName: node + linkType: hard + "@arrows/array@npm:^1.4.1": version: 1.4.1 resolution: "@arrows/array@npm:1.4.1" @@ -73,6 +83,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: 10/c352082474a2ee1d2b812bd116a56b2e8b38065df9678a32a535f151ec6f58e54633cc778778374f10544b930703cca6ddf998803888a636afa27e2658068a9c + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-validator-identifier@npm:7.22.20" @@ -91,6 +108,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.23.6": + version: 7.23.9 + resolution: "@babel/parser@npm:7.23.9" + bin: + parser: ./bin/babel-parser.js + checksum: 10/727a7a807100f6a26df859e2f009c4ddbd0d3363287b45daa50bd082ccd0d431d0c4d0e610a91f806e04a1918726cd0f5a0592c9b902a815337feed12e1cafd9 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.13.8": version: 7.23.9 resolution: "@babel/runtime@npm:7.23.9" @@ -100,6 +126,24 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.23.6, @babel/types@npm:^7.8.3": + version: 7.23.9 + resolution: "@babel/types@npm:7.23.9" + dependencies: + "@babel/helper-string-parser": "npm:^7.23.4" + "@babel/helper-validator-identifier": "npm:^7.22.20" + to-fast-properties: "npm:^2.0.0" + checksum: 10/bed9634e5fd0f9dc63c84cfa83316c4cb617192db9fedfea464fca743affe93736d7bf2ebf418ee8358751a9d388e303af87a0c050cb5d87d5870c1b0154f6cb + languageName: node + linkType: hard + +"@bcoe/v8-coverage@npm:^0.2.3": + version: 0.2.3 + resolution: "@bcoe/v8-coverage@npm:0.2.3" + checksum: 10/1a1f0e356a3bb30b5f1ced6f79c413e6ebacf130421f15fac5fcd8be5ddf98aedb4404d7f5624e3285b700e041f9ef938321f3ca4d359d5b716f96afa120d88d + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -1093,6 +1137,13 @@ __metadata: languageName: node linkType: hard +"@istanbuljs/schema@npm:^0.1.2": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: 10/a9b1e49acdf5efc2f5b2359f2df7f90c5c725f2656f16099e8b2cd3a000619ecca9fc48cf693ba789cf0fd989f6e0df6a22bc05574be4223ecdbb7997d04384b + languageName: node + linkType: hard + "@jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" @@ -1154,6 +1205,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.12": + version: 0.3.23 + resolution: "@jridgewell/trace-mapping@npm:0.3.23" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10/eb8d167f8aeb3ac55e7726eda1bb6240787987fd66d480edbe15fc98ad594ec10cb584289f649e2074b9e117862c82efdec07db13850f3dc4cb242258bb2b67d + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.22 resolution: "@jridgewell/trace-mapping@npm:0.3.22" @@ -2241,6 +2302,13 @@ __metadata: languageName: node linkType: hard +"@types/istanbul-lib-coverage@npm:^2.0.1": + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 10/3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.12": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -2448,6 +2516,29 @@ __metadata: languageName: node linkType: hard +"@vitest/coverage-v8@npm:^1.3.1": + version: 1.3.1 + resolution: "@vitest/coverage-v8@npm:1.3.1" + dependencies: + "@ampproject/remapping": "npm:^2.2.1" + "@bcoe/v8-coverage": "npm:^0.2.3" + debug: "npm:^4.3.4" + istanbul-lib-coverage: "npm:^3.2.2" + istanbul-lib-report: "npm:^3.0.1" + istanbul-lib-source-maps: "npm:^4.0.1" + istanbul-reports: "npm:^3.1.6" + magic-string: "npm:^0.30.5" + magicast: "npm:^0.3.3" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.5.0" + test-exclude: "npm:^6.0.0" + v8-to-istanbul: "npm:^9.2.0" + peerDependencies: + vitest: 1.3.1 + checksum: 10/c524a3f094a8366a603f93c82576ebf402f1ea7cbdbdcd74a8cf36eb489608db42f87ef6f0231cffae33e70a3ec10faaa6f2f08f5f42742ba28a1328393c382c + languageName: node + linkType: hard + "@vitest/expect@npm:1.3.1": version: 1.3.1 resolution: "@vitest/expect@npm:1.3.1" @@ -2552,6 +2643,7 @@ __metadata: "@types/node": "npm:^18.19.18" "@typescript-eslint/eslint-plugin": "npm:^7.1.0" "@typescript-eslint/parser": "npm:^7.1.0" + "@vitest/coverage-v8": "npm:^1.3.1" "@xmtp/proto": "npm:3.34.0" "@xmtp/rollup-plugin-resolve-extensions": "npm:1.0.1" "@xmtp/user-preferences-bindings-wasm": "npm:^0.3.6" @@ -2580,7 +2672,7 @@ __metadata: semantic-release: "npm:^21.1.2" typedoc: "npm:^0.25.9" typescript: "npm:^5.3.3" - viem: "npm:^1.21.4" + viem: "npm:^2.7.8" vite: "npm:^5.1.4" vitest: "npm:^1.3.1" languageName: unknown @@ -2612,18 +2704,18 @@ __metadata: languageName: node linkType: hard -"abitype@npm:0.9.8": - version: 0.9.8 - resolution: "abitype@npm:0.9.8" +"abitype@npm:1.0.0": + version: 1.0.0 + resolution: "abitype@npm:1.0.0" peerDependencies: typescript: ">=5.0.4" - zod: ^3 >=3.19.1 + zod: ^3 >=3.22.0 peerDependenciesMeta: typescript: optional: true zod: optional: true - checksum: 10/90940804839b1b65cb5b427d934db9c1cc899157d6091f281b1ce94d9c0c08b1ae946ab43e984e70c031e94c49355f6677475a7242ec60cae5457c074dcd40f9 + checksum: 10/38c8d965c75c031854385f1c14da0410e271f1a8255332869a77a1ee836c4607420522c1f0077716c7ad7c4091f53c1b2681ed1d30b5161d1424fdb5a480f104 languageName: node linkType: hard @@ -3737,6 +3829,13 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10/c987be3ec061348cdb3c2bfb924bec86dea1eacad10550a85ca23edb0fe3556c3a61c7399114f3331ccb3499d7fd0285ab24566e5745929412983494c3926e15 + languageName: node + linkType: hard + "core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -3886,7 +3985,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5667,6 +5766,13 @@ __metadata: languageName: node linkType: hard +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: 10/034d74029dcca544a34fb6135e98d427acd73019796ffc17383eaa3ec2fe1c0471dcbbc8f8ed39e46e86d43ccd753a160631615e4048285e313569609b66d5b7 + languageName: node + linkType: hard + "http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" @@ -6252,6 +6358,45 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.2": + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 10/40bbdd1e937dfd8c830fa286d0f665e81b7a78bdabcd4565f6d5667c99828bda3db7fb7ac6b96a3e2e8a2461ddbc5452d9f8bc7d00cb00075fa6a3e99f5b6a81 + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0, istanbul-lib-report@npm:^3.0.1": + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" + dependencies: + istanbul-lib-coverage: "npm:^3.0.0" + make-dir: "npm:^4.0.0" + supports-color: "npm:^7.1.0" + checksum: 10/86a83421ca1cf2109a9f6d193c06c31ef04a45e72a74579b11060b1e7bb9b6337a4e6f04abfb8857e2d569c271273c65e855ee429376a0d7c91ad91db42accd1 + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^4.0.1": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" + dependencies: + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + source-map: "npm:^0.6.1" + checksum: 10/5526983462799aced011d776af166e350191b816821ea7bcf71cab3e5272657b062c47dc30697a22a43656e3ced78893a42de677f9ccf276a28c913190953b82 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.6": + version: 3.1.7 + resolution: "istanbul-reports@npm:3.1.7" + dependencies: + html-escaper: "npm:^2.0.0" + istanbul-lib-report: "npm:^3.0.0" + checksum: 10/f1faaa4684efaf57d64087776018d7426312a59aa6eeb4e0e3a777347d23cd286ad18f427e98f0e3dee666103d7404c9d7abc5f240406a912fa16bd6695437fa + languageName: node + linkType: hard + "jackspeak@npm:^2.3.5": version: 2.3.6 resolution: "jackspeak@npm:2.3.6" @@ -6922,6 +7067,26 @@ __metadata: languageName: node linkType: hard +"magicast@npm:^0.3.3": + version: 0.3.3 + resolution: "magicast@npm:0.3.3" + dependencies: + "@babel/parser": "npm:^7.23.6" + "@babel/types": "npm:^7.23.6" + source-map-js: "npm:^1.0.2" + checksum: 10/04af6f60d80a3a51344a864c6479af428e672c249b3f9211d78eee6ac8649257daad610ba4a68d4151e38d393b48b0f4a8f3db178422674fb5d418bc8c939055 + languageName: node + linkType: hard + +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 10/bf0731a2dd3aab4db6f3de1585cea0b746bb73eb5a02e3d8d72757e376e64e6ada190b1eddcde5b2f24a81b688a9897efd5018737d05e02e2a671dda9cff8a8a + languageName: node + linkType: hard + "make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" @@ -9731,6 +9896,17 @@ __metadata: languageName: node linkType: hard +"test-exclude@npm:^6.0.0": + version: 6.0.0 + resolution: "test-exclude@npm:6.0.0" + dependencies: + "@istanbuljs/schema": "npm:^0.1.2" + glob: "npm:^7.1.4" + minimatch: "npm:^3.0.4" + checksum: 10/8fccb2cb6c8fcb6bb4115394feb833f8b6cf4b9503ec2485c2c90febf435cac62abe882a0c5c51a37b9bbe70640cdd05acf5f45e486ac4583389f4b0855f69e5 + languageName: node + linkType: hard + "text-extensions@npm:^1.0.0": version: 1.9.0 resolution: "text-extensions@npm:1.9.0" @@ -9813,6 +9989,13 @@ __metadata: languageName: node linkType: hard +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: 10/be2de62fe58ead94e3e592680052683b1ec986c72d589e7b21e5697f8744cdbf48c266fa72f6c15932894c10187b5f54573a3bcf7da0bfd964d5caf23d436168 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -10227,6 +10410,17 @@ __metadata: languageName: node linkType: hard +"v8-to-istanbul@npm:^9.2.0": + version: 9.2.0 + resolution: "v8-to-istanbul@npm:9.2.0" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.12" + "@types/istanbul-lib-coverage": "npm:^2.0.1" + convert-source-map: "npm:^2.0.0" + checksum: 10/18dd8cebfb6790f27f4e41e7cff77c7ab1c8904085f354dd7875e2eb65f4261c4cf40939132502875779d92304bfea46b8336346ecb40b6f33c3a3979e6f5729 + languageName: node + linkType: hard + "validate-npm-package-license@npm:^3.0.1, validate-npm-package-license@npm:^3.0.4": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -10246,16 +10440,16 @@ __metadata: languageName: node linkType: hard -"viem@npm:^1.21.4": - version: 1.21.4 - resolution: "viem@npm:1.21.4" +"viem@npm:^2.7.8": + version: 2.7.15 + resolution: "viem@npm:2.7.15" dependencies: "@adraffy/ens-normalize": "npm:1.10.0" "@noble/curves": "npm:1.2.0" "@noble/hashes": "npm:1.3.2" "@scure/bip32": "npm:1.3.2" "@scure/bip39": "npm:1.2.1" - abitype: "npm:0.9.8" + abitype: "npm:1.0.0" isows: "npm:1.0.3" ws: "npm:8.13.0" peerDependencies: @@ -10263,7 +10457,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/2007a8a674301d790b3172a0a84bd1659f76332ac13a78d695f7cee0602388103a07b2d6a3fc46b4f27582f8b506f7c1f90f13c5e21e464daffc6cccb14fbc3a + checksum: 10/cf6923e8e042d626fc03e678757529dfa8e977d8079a744ffec88cdbdde7505bb9aadc73882ddf4c732eb6b380af478b13cc4363fdaa960d0ce2628da6941612 languageName: node linkType: hard