update ESLint (#636)

* update ESLint

* f

* f

* f

* c

* f

* f

* f

* f

* r

* u

* f

* f

* u
This commit is contained in:
Felicio Mununga 2024-11-14 06:50:42 +01:00 committed by GitHub
parent 27ba1bd931
commit 3c71b8c8f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 1879 additions and 1228 deletions

View File

@ -0,0 +1,10 @@
---
'@status-im/eslint-config': patch
'@status-im/components': patch
'@status-im/js': patch
'@status-im/colors': patch
'connector': patch
'@status-im/icons': patch
---
update ESLint

View File

@ -1,6 +0,0 @@
**/dist
**/node_modules
**/protos
**/proto
**/coverage
**/storybook-static

View File

@ -1,7 +1,13 @@
{ {
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"npm.packageManager": "pnpm", "npm.packageManager": "pnpm",
"eslint.useESLintClass": true,
"eslint.workingDirectories": [ "eslint.workingDirectories": [
"./packages/colors",
"./packages/icons",
"./packages/components",
"./packages/status-js",
"./apps/connector",
{ {
"mode": "auto", "mode": "auto",
"#comment": "See https://github.com/microsoft/vscode-eslint/issues/1161 for reason (i.e. multiple .eslintrc config files)" "#comment": "See https://github.com/microsoft/vscode-eslint/issues/1161 for reason (i.e. multiple .eslintrc config files)"

View File

@ -1,39 +0,0 @@
module.exports = {
root: true,
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: ['@status-im/eslint-config', 'plugin:tailwindcss/recommended'],
rules: {
'no-constant-binary-expression': 'error',
'no-restricted-globals': ['error', 'process'],
'jsx-a11y/alt-text': [
1,
{
img: [],
},
],
},
},
{
// parser: 'esprima',
files: ['*.mjs'],
// env: {
// browser: true,
// es2021: true,
// },
// extends: ['eslint:recommended', 'plugin:import/recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
},
{
files: ['*.js'],
parserOptions: {
ecmaVersion: 'latest',
},
},
],
}

View File

@ -62,10 +62,10 @@ pipeline {
expression { changesDetected } expression { changesDetected }
} }
steps { steps {
dir("${env.WORKSPACE}/apps/connector") { dir("${env.WORKSPACE}") {
script { script {
nix.shell( nix.shell(
'pnpm build:chrome', 'pnpm turbo run build --filter=connector',
pure: false, pure: false,
entryPoint: "${env.WORKSPACE}/apps/connector/shell.nix" entryPoint: "${env.WORKSPACE}/apps/connector/shell.nix"
) )

View File

@ -0,0 +1,37 @@
import configs, { tailwindcssConfigs } from '@status-im/eslint-config'
/** @type {import('eslint').Linter.Config[]} */
export default [
...configs,
...tailwindcssConfigs,
{
files: ['*.ts', '*.tsx'],
rules: {
'no-constant-binary-expression': 'error',
'no-restricted-globals': ['error', 'process'],
'jsx-a11y/alt-text': [
1,
{
img: [],
},
],
},
},
{
files: ['*.mjs'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
},
},
{
files: ['*.js'],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
},
},
},
]

View File

@ -13,9 +13,10 @@
"dev:chrome": "plasmo dev --target=chrome-mv3", "dev:chrome": "plasmo dev --target=chrome-mv3",
"dev:safari": "plasmo dev --target=safari-mv3", "dev:safari": "plasmo dev --target=safari-mv3",
"dev:firefox": "plasmo dev --target=firefox-mv3", "dev:firefox": "plasmo dev --target=firefox-mv3",
"build": "pnpm build:chrome",
"build:chrome": "plasmo build --target=chrome-mv3", "build:chrome": "plasmo build --target=chrome-mv3",
"build:firefox": "plasmo build --target=firefox-mv3 --zip", "build:firefox": "plasmo build --target=firefox-mv3 --zip",
"lint": "eslint ./src", "lint": "eslint src",
"format": "prettier --write .", "format": "prettier --write .",
"package": "plasmo package", "package": "plasmo package",
"clean": "rimraf apps build .plasmo node_modules" "clean": "rimraf apps build .plasmo node_modules"
@ -25,7 +26,7 @@
"@plasmohq/storage": "^1.11.0", "@plasmohq/storage": "^1.11.0",
"@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-switch": "^1.1.0",
"@status-im/colors": "^0.4.0", "@status-im/colors": "workspace:*",
"cva": "^1.0.0-beta.1", "cva": "^1.0.0-beta.1",
"ethers": "^6.13.0", "ethers": "^6.13.0",
"plasmo": "0.88.0", "plasmo": "0.88.0",
@ -37,7 +38,7 @@
"devDependencies": { "devDependencies": {
"@ianvs/prettier-plugin-sort-imports": "4.1.1", "@ianvs/prettier-plugin-sort-imports": "4.1.1",
"@parcel/bundler-experimental": "^2.7.0", "@parcel/bundler-experimental": "^2.7.0",
"@status-im/eslint-config": "^0.3.0", "@status-im/eslint-config": "workspace:*",
"@tailwindcss/typography": "^0.5.13", "@tailwindcss/typography": "^0.5.13",
"@types/chrome": "0.0.258", "@types/chrome": "0.0.258",
"@types/node": "20.11.5", "@types/node": "20.11.5",
@ -45,7 +46,6 @@
"@types/react-dom": "18.2.18", "@types/react-dom": "18.2.18",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"husky": "^8.0.3", "husky": "^8.0.3",
"lint-staged": "^13.2.0",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"prettier": "3.2.4", "prettier": "3.2.4",
"prettier-plugin-tailwindcss": "^0.6.1", "prettier-plugin-tailwindcss": "^0.6.1",

View File

@ -9,8 +9,8 @@ const styles = cva({
base: 'flex h-6 items-center gap-1 rounded-[20px] border px-2 text-13', base: 'flex h-6 items-center gap-1 rounded-[20px] border px-2 text-13',
variants: { variants: {
status: { status: {
on: 'border-success-/20 bg-success-/10 text-success-60', on: 'border-success-50/20 bg-success-50/10 text-success-60',
off: 'border-danger-/20 bg-danger-/10 text-danger-60', off: 'border-danger-50/20 bg-danger-50/10 text-danger-60',
}, },
}, },
}) })

View File

@ -127,7 +127,7 @@ export class Provider {
return return
} }
} }
} catch (error) { } catch {
// we don't reject here because incoming message is not from the proxy // we don't reject here because incoming message is not from the proxy
return return
} }

13
eslint.config.mjs Normal file
View File

@ -0,0 +1,13 @@
/** @type {import('eslint').Linter.Config[]} */
export default [
{
ignores: [
'**/dist',
'**/node_modules',
'**/protos',
'**/proto',
'**/coverage',
'**/storybook-static',
],
},
]

View File

@ -7,7 +7,8 @@
"packages/status-js", "packages/status-js",
"packages/colors", "packages/colors",
"packages/icons", "packages/icons",
"packages/components" "packages/components",
"apps/connector"
] ]
}, },
"keywords": [], "keywords": [],
@ -16,8 +17,8 @@
"prepare": "husky install", "prepare": "husky install",
"test": "turbo run test --filter=@status-im/* -- --run", "test": "turbo run test --filter=@status-im/* -- --run",
"dev": "turbo run dev --filter=@status-im/* --parallel", "dev": "turbo run dev --filter=@status-im/* --parallel",
"build": "turbo run build --filter=@status-im/* && node --version", "build": "turbo run build --filter=@status-im/* --filter=connector",
"lint": "turbo run lint --filter=@status-im/* --filter=web", "lint": "turbo run lint --filter=@status-im/* --filter=connector",
"typecheck": "turbo run typecheck", "typecheck": "turbo run typecheck",
"format": "prettier --write .", "format": "prettier --write .",
"clean": "turbo run clean && rimraf node_modules", "clean": "turbo run clean && rimraf node_modules",
@ -32,8 +33,9 @@
"@status-im/eslint-config": "workspace:*", "@status-im/eslint-config": "workspace:*",
"@tsconfig/strictest": "^2.0.0", "@tsconfig/strictest": "^2.0.0",
"@types/prettier": "^2.7.2", "@types/prettier": "^2.7.2",
"eslint": "^9.14.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"lint-staged": "^13.2.0", "lint-staged": "^15.2.10",
"prettier": "^3.3.3", "prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6", "prettier-plugin-tailwindcss": "^0.6.6",
"@types/react": "^18.0.33", "@types/react": "^18.0.33",

View File

@ -1,4 +0,0 @@
{
"root": true,
"extends": ["@status-im/eslint-config"]
}

View File

@ -0,0 +1,4 @@
import configs from '@status-im/eslint-config'
/** @type {import('eslint').Linter.Config[]} */
export default [...configs]

View File

@ -1,7 +0,0 @@
{
"root": true,
"extends": ["plugin:tailwindcss/recommended", "@status-im/eslint-config"],
"rules": {
"tailwindcss/classnames-order": "off"
}
}

View File

@ -0,0 +1,4 @@
import configs, { tailwindcssConfigs } from '@status-im/eslint-config'
/** @type {import('eslint').Linter.Config[]} */
export default [...configs, ...tailwindcssConfigs]

View File

@ -31,7 +31,7 @@
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"dev": "vite build --watch --mode development", "dev": "vite build --watch --mode development",
"build": "vite build", "build": "vite build",
"postbuild": "pnpm build:types && node --version", "postbuild": "pnpm build:types",
"build:types": "tsc --noEmit false --emitDeclarationOnly", "build:types": "tsc --noEmit false --emitDeclarationOnly",
"typecheck": "tsc", "typecheck": "tsc",
"lint": "eslint src", "lint": "eslint src",

View File

@ -14,6 +14,7 @@ const sizesAvatar = {
} }
const renderVariant = (variant: AvatarProps['type']) => { const renderVariant = (variant: AvatarProps['type']) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const content = (props: any) => { const content = (props: any) => {
const sizes = sizesAvatar[variant] const sizes = sizesAvatar[variant]
@ -47,7 +48,7 @@ const meta = {
}, },
render: props => ( render: props => (
<div className="grid gap-4 theme-yellow"> <div className="grid gap-4">
<h1 className="text-19">User Avatar</h1> <h1 className="text-19">User Avatar</h1>
{renderVariant('user')({ {renderVariant('user')({
...props, ...props,

View File

@ -7,7 +7,7 @@ import type { Meta, StoryObj } from '@storybook/react'
const sizes = ['40', '32', '24'] as const const sizes = ['40', '32', '24'] as const
// eslint-disable-next-line react/display-name // eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderVariant = (variant: string) => (props: any) => ( const renderVariant = (variant: string) => (props: any) => (
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
{sizes.map(size => ( {sizes.map(size => (

View File

@ -6,7 +6,7 @@ import type { Meta, StoryObj } from '@storybook/react'
const variants = ['primary', 'secondary', 'gray'] as const const variants = ['primary', 'secondary', 'gray'] as const
// eslint-disable-next-line react/display-name // eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderVariant = (variant: (typeof variants)[number]) => (props: any) => ( const renderVariant = (variant: (typeof variants)[number]) => (props: any) => (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Shortcut {...props} variant={variant} icon={<CommandIcon />} /> <Shortcut {...props} variant={variant} icon={<CommandIcon />} />

View File

@ -1,5 +1,6 @@
import { cva, cx, type VariantProps } from 'cva' import { cva, cx, type VariantProps } from 'cva'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const styles = cva({ const styles = cva({
base: 'animate-skeleton overflow-hidden', base: 'animate-skeleton overflow-hidden',

View File

@ -27,13 +27,7 @@ function Tag(
props: Props & (ButtonProps | DivProps), props: Props & (ButtonProps | DivProps),
ref: Ref<HTMLButtonElement | HTMLDivElement>, ref: Ref<HTMLButtonElement | HTMLDivElement>,
) { ) {
const { const { size = '32', icon, iconPlacement = 'left', label, ...rest } = props
size = '32',
icon,
iconPlacement = 'left',
label,
...rest
} = props
const iconOnly = Boolean(icon && !label) const iconOnly = Boolean(icon && !label)

View File

@ -8,4 +8,4 @@ export type CustomisationColorType = keyof typeof customisation
export type Prettify<T> = { export type Prettify<T> = {
[K in keyof T]: T[K] [K in keyof T]: T[K]
} & {} // eslint-disable-line @typescript-eslint/ban-types } & {}

View File

@ -0,0 +1,9 @@
import js from '@eslint/js'
/** @type {import('eslint').Linter.Config[]} */
export default [
{
...js.configs.recommended,
files: ['**/*.js', '**/*.mjs', '**/*.cjs'],
},
]

View File

@ -1,103 +1,174 @@
module.exports = { // // @ts-check
parser: '@typescript-eslint/parser',
parserOptions: { import js from '@eslint/js'
// TODO: Enable type-aware linting (https://typescript-eslint.io/docs/linting/type-linting) import globals from 'globals'
// "tsconfigRootDir": ".", import {
// "project": ["./packages/*/tsconfig.json"], parser as typescriptParser,
sourceType: 'module', plugin as typescriptPlugin,
ecmaFeatures: { configs as typescriptConfigs,
jsx: true, } from 'typescript-eslint'
}, import commentsPlugin from 'eslint-plugin-eslint-comments'
warnOnUnsupportedTypeScriptVersion: true, import * as importPlugin from 'eslint-plugin-import'
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'
import reactPlugin from 'eslint-plugin-react'
import reactHooksPlugin from 'eslint-plugin-react-hooks'
import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'
import prettierPlugin from 'eslint-plugin-prettier/recommended'
import tailwindcssPlugin from 'eslint-plugin-tailwindcss'
/** @type {import('eslint').Linter.Config[]} */
export default [
{
name: '@status-im/eslint-config',
}, },
env: { {
browser: true, plugins: {
node: true, import: importPlugin,
},
plugins: [
'@typescript-eslint',
'import',
'simple-import-sort',
'react',
'jsx-a11y',
],
extends: [
'plugin:@typescript-eslint/recommended',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
// "plugin:@typescript-eslint/recommended-requiring-type-checking",
'plugin:eslint-comments/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:jsx-a11y/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:react/jsx-runtime',
'prettier',
],
overrides: [
{
files: ['examples/**/*.tsx'],
rules: {
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
},
}, },
], rules: {
rules: { ...importPlugin.configs.recommended.rules,
'react/prop-types': 0, },
// "@typescript-eslint/consistent-type-definitions": ["error", "interface"], files: [
'@typescript-eslint/consistent-type-imports': 'error', '**/*.js',
// TODO: turn on this rul '**/*.cjs',
'@typescript-eslint/no-non-null-assertion': 'off', '**/*.mjs',
// "@typescript-eslint/consistent-type-exports": "error", '**/*.jsx',
'simple-import-sort/imports': [ '**/*.ts',
'error', '**/*.mts',
{ '**/*.tsx',
groups: [ '**/*.mdx',
// Side effect imports. ], // js, ts, mdx
['^\\u0000'], },
// `react` related packages come first. {
['^react$', '^react-dom$'], ...js.configs.recommended,
// Things that start with a letter (or digit or underscore), or `@` followed by a letter. files: ['**/*.js', '**/*.mjs', '**/*.cjs', '**/*.ts', '**/*.tsx'], // js
['^@?\\w'], },
// Absolute imports and other imports such as Vue-style `@/foo`. ...typescriptConfigs.recommended.map(config => ({
// Anything not matched in another group. ...config,
['^'], files: ['**/*.ts', '**/*.mts', '**/*.tsx'],
// Relative imports. })),
// Anything that starts with a dot. {
['^\\.'], ...jsxA11yPlugin.flatConfigs.recommended,
// type imports last as a separate group files: ['**/*.js', '**/*.mjs', '**/*.cjs', '**/*.ts', '**/*.tsx'],
['^.+\\u0000$'], },
], {
}, ...reactPlugin.configs.flat['jsx-runtime'],
files: ['**/*.js', '**/*.mjs', '**/*.cjs', '**/*.ts', '**/*.tsx'],
},
{
...prettierPlugin,
files: [
'**/*.js',
'**/*.mjs',
'**/*.cjs',
'**/*.ts',
'**/*.tsx',
'**/*.md',
'**/*.mdx',
], ],
'simple-import-sort/exports': 'error',
'import/first': 'error',
'import/newline-after-import': 'error',
'import/no-duplicates': 'error',
}, },
settings: { {
react: { files: ['**/*.ts', '**/*.mts', '**/*.tsx'], // ts
version: 'detect', languageOptions: {
}, parser: typescriptParser,
'import/resolver': { parserOptions: {
node: { // TODO: Enable type-aware linting (https://typescript-eslint.io/docs/linting/type-linting)
extensions: ['.js', '.jsx', '.ts', '.tsx'], // "tsconfigRootDir": ".",
project: [ // "project": ["./packages/*/tsconfig.json"],
'tsconfig.base.json', // sourceType: 'module',
'packages/*/tsconfig.json', ecmaFeatures: {
'apps/*/tsconfig.json', jsx: true,
], },
// warnOnUnsupportedTypeScriptVersion: true,
}, },
typescript: { globals: {
alwaysTryTypes: true, ...globals.browser,
project: [ ...globals.node,
'tsconfig.base.json', },
'packages/*/tsconfig.json', },
'apps/*/tsconfig.json', plugins: {
], '@typescript-eslint': typescriptPlugin,
'eslint-comments': commentsPlugin,
react: reactPlugin,
'react-hooks': reactHooksPlugin,
'simple-import-sort': simpleImportSortPlugin,
},
rules: {
...importPlugin.configs.typescript.rules,
...reactHooksPlugin.configs.recommended.rules,
'react/prop-types': 0,
// "@typescript-eslint/consistent-type-definitions": ["error", "interface"],
'@typescript-eslint/consistent-type-imports': 'error',
// TODO: turn on this rul
'@typescript-eslint/no-non-null-assertion': 'off',
// "@typescript-eslint/consistent-type-exports": "error",
'simple-import-sort/imports': [
'error',
{
groups: [
// Side effect imports.
['^\\u0000'],
// `react` related packages come first.
['^react$', '^react-dom$'],
// Things that start with a letter (or digit or underscore), or `@` followed by a letter.
['^@?\\w'],
// Absolute imports and other imports such as Vue-style `@/foo`.
// Anything not matched in another group.
['^'],
// Relative imports.
// Anything that starts with a dot.
['^\\.'],
// type imports last as a separate group
['^.+\\u0000$'],
],
},
],
'simple-import-sort/exports': 'error',
'import/first': 'error',
'import/newline-after-import': 'error',
'import/no-duplicates': 'error',
},
settings: {
react: {
version: 'detect',
},
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
project: [
'tsconfig.base.json',
'packages/*/tsconfig.json',
'apps/*/tsconfig.json',
],
},
typescript: {
alwaysTryTypes: true,
project: [
'tsconfig.base.json',
'packages/*/tsconfig.json',
'apps/*/tsconfig.json',
],
},
}, },
}, },
}, },
} ]
/** @type {import('eslint').Linter.Config[]} */
export const tailwindcssConfigs = [
{
files: ['**/*.ts', '**/*.mts', '**/*.tsx'],
},
...tailwindcssPlugin.configs['flat/recommended'],
{
rules: {
'tailwindcss/classnames-order': 'off',
'tailwindcss/migration-from-tailwind-2': 'off',
},
settings: {
tailwindcss: {
callees: ['cx', 'cva'],
},
},
},
]

View File

@ -3,6 +3,7 @@
"version": "1.0.1", "version": "1.0.1",
"main": "index.js", "main": "index.js",
"license": "MPL-2.0", "license": "MPL-2.0",
"type": "module",
"exports": { "exports": {
".": { ".": {
"import": "./index.js", "import": "./index.js",
@ -15,23 +16,28 @@
}, },
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm", "preinstall": "npx only-allow pnpm",
"clean": "rimraf node_modules" "clean": "rimraf node_modules",
"lint": "eslint ."
},
"peerDependencies": {
"eslint": "^9.14.0"
}, },
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "^5.59.11", "@eslint/eslintrc": "^3.1.0",
"@typescript-eslint/parser": "^5.59.11", "@eslint/js": "^9.14.0",
"eslint": "^8.42.0", "eslint-config-prettier": "^9.1.0",
"eslint-config-next": "^13.2.4", "eslint-import-resolver-node": "^0.3.9",
"eslint-config-prettier": "^8.8.0", "eslint-import-resolver-typescript": "^3.6.3",
"eslint-import-resolver-node": "^0.3.7",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.27.5", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react": "^7.37.2",
"eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-tailwindcss": "^3.12.1" "eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-tailwindcss": "^3.17.5",
"globals": "^15.12.0",
"typescript-eslint": "^8.13.0"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -1,5 +0,0 @@
{
"root": true,
"extends": ["@status-im/eslint-config"],
"ignorePatterns": ["dist", "index.js", "index.es.js"]
}

View File

@ -0,0 +1,9 @@
import configs from '@status-im/eslint-config'
/** @type {import('eslint').Linter.Config[]} */
export default [
...configs,
{
ignores: ['dist', 'index.js', 'index.es.js'],
},
]

View File

@ -62,11 +62,11 @@
"generate": "rimraf src && svgr svg --out-dir src && pnpm lint:fix && pnpm format", "generate": "rimraf src && svgr svg --out-dir src && pnpm lint:fix && pnpm format",
"dev": "vite build --watch --mode development", "dev": "vite build --watch --mode development",
"build": "vite build", "build": "vite build",
"postbuild": "pnpm build:types && node --version", "postbuild": "pnpm build:types",
"build:types": "tsc src/**/index.ts --emitDeclarationOnly --declaration --jsx react-jsx --skipLibCheck --declarationDir ./dist", "build:types": "tsc src/**/index.ts --emitDeclarationOnly --declaration --jsx react-jsx --skipLibCheck --declarationDir ./dist",
"#test": "vitest", "#test": "vitest",
"typecheck": "tsc", "typecheck": "tsc",
"lint": "eslint 'src/**/*.{ts,tsx}'", "lint": "eslint src",
"lint:fix": "pnpm lint --fix", "lint:fix": "pnpm lint --fix",
"format": "prettier --write 'src/**/*.{ts,tsx}'", "format": "prettier --write 'src/**/*.{ts,tsx}'",
"clean": "rimraf dist node_modules .turbo", "clean": "rimraf dist node_modules .turbo",

View File

@ -1,13 +0,0 @@
{
"root": true,
"extends": ["@status-im/eslint-config"],
"overrides": [
{
"files": ["./src/protos/**/*_pb.ts"],
"rules": {
"eslint-comments/disable-enable-pair": "off",
"eslint-comments/no-unlimited-disable": "off"
}
}
]
}

View File

@ -0,0 +1,13 @@
import configs from '@status-im/eslint-config'
/** @type {import('eslint').Linter.Config[]} */
export default [
...configs,
{
files: ['./src/protos/**/*_pb.ts'],
rules: {
'eslint-comments/disable-enable-pair': 'off',
'eslint-comments/no-unlimited-disable': 'off',
},
},
]

View File

@ -86,7 +86,7 @@ export class ActivityCenter {
public addMessageNotification = ( public addMessageNotification = (
newMessage: ChatMessage, newMessage: ChatMessage,
referencedMessage?: ChatMessage referencedMessage?: ChatMessage,
) => { ) => {
let isMention: boolean | undefined let isMention: boolean | undefined
let isReply: boolean | undefined let isReply: boolean | undefined

View File

@ -101,7 +101,7 @@ export class Chat {
client: Client, client: Client,
uuid: string, uuid: string,
type: MessageType.COMMUNITY_CHAT, type: MessageType.COMMUNITY_CHAT,
description: CommunityChat description: CommunityChat,
) => { ) => {
const id = `${community.publicKey}${uuid}` const id = `${community.publicKey}${uuid}`
const contentTopic = idToContentTopic(id) const contentTopic = idToContentTopic(id)
@ -171,7 +171,7 @@ export class Chat {
} }
public onMessage = ( public onMessage = (
callback: (messages: ChatMessage[]) => void callback: (messages: ChatMessage[]) => void,
): (() => void) => { ): (() => void) => {
this.messageCallbacks.add(callback) this.messageCallbacks.add(callback)
// todo?: set from ui, think use case without an ui // todo?: set from ui, think use case without an ui
@ -227,7 +227,7 @@ export class Chat {
pageSize: 50, pageSize: 50,
// most recent page first // most recent page first
pageDirection: PageDirection.BACKWARD, pageDirection: PageDirection.BACKWARD,
} },
) )
this.#previousFetchedStartTime = startTime this.#previousFetchedStartTime = startTime
@ -278,7 +278,7 @@ export class Chat {
this.#oldestFetchedMessage = this.getOldestFetchedMessage( this.#oldestFetchedMessage = this.getOldestFetchedMessage(
this.#oldestFetchedMessage, this.#oldestFetchedMessage,
newMessage.messageId, newMessage.messageId,
timestamp timestamp,
) )
} }
@ -340,7 +340,7 @@ export class Chat {
if (!this.#isActive && !isAuthor) { if (!this.#isActive && !isAuthor) {
this.client.activityCenter.addMessageNotification( this.client.activityCenter.addMessageNotification(
newMessage, newMessage,
this.#messages.get(newMessage.responseTo) this.#messages.get(newMessage.responseTo),
) )
} }
} }
@ -349,7 +349,7 @@ export class Chat {
messageId: string, messageId: string,
text: string, text: string,
clock: bigint, clock: bigint,
signerPublicKey: string signerPublicKey: string,
) => { ) => {
const message = this.#messages.get(messageId) const message = this.#messages.get(messageId)
@ -374,7 +374,7 @@ export class Chat {
public handleDeletedMessage = ( public handleDeletedMessage = (
messageId: string, messageId: string,
clock: bigint, clock: bigint,
signerPublicKey: string signerPublicKey: string,
) => { ) => {
const message = this.#messages.get(messageId) const message = this.#messages.get(messageId)
if (message && this.isAuthor(message, signerPublicKey)) { if (message && this.isAuthor(message, signerPublicKey)) {
@ -394,7 +394,7 @@ export class Chat {
public handlePinnedMessage = ( public handlePinnedMessage = (
messageId: string, messageId: string,
clock: bigint, clock: bigint,
pinned?: boolean pinned?: boolean,
) => { ) => {
const message = this.#messages.get(messageId) const message = this.#messages.get(messageId)
if (message) { if (message) {
@ -419,14 +419,14 @@ export class Chat {
messageId: string, messageId: string,
reaction: EmojiReaction, reaction: EmojiReaction,
clock: bigint, clock: bigint,
signerPublicKey: string signerPublicKey: string,
) => { ) => {
const message = this.#messages.get(messageId) const message = this.#messages.get(messageId)
if (message) { if (message) {
const reactions = getReactions( const reactions = getReactions(
reaction, reaction,
message.reactions, message.reactions,
signerPublicKey signerPublicKey,
) )
message.reactions = reactions message.reactions = reactions
@ -447,7 +447,7 @@ export class Chat {
SAD: new Set<string>(), SAD: new Set<string>(),
ANGRY: new Set<string>(), ANGRY: new Set<string>(),
}, },
signerPublicKey signerPublicKey,
) )
this.#reactEvents.set(messageId, { clock, reactions }) this.#reactEvents.set(messageId, { clock, reactions })
@ -455,7 +455,7 @@ export class Chat {
const reactions = getReactions( const reactions = getReactions(
reaction, reaction,
reactEvent.reactions, reactEvent.reactions,
signerPublicKey signerPublicKey,
) )
this.#reactEvents.set(messageId, { clock, reactions }) this.#reactEvents.set(messageId, { clock, reactions })
@ -489,7 +489,7 @@ export class Chat {
ApplicationMetadataMessage_Type.CHAT_MESSAGE, ApplicationMetadataMessage_Type.CHAT_MESSAGE,
payload, payload,
this.contentTopic, this.contentTopic,
this.symmetricKey this.symmetricKey,
) )
} }
@ -518,7 +518,7 @@ export class Chat {
ApplicationMetadataMessage_Type.CHAT_MESSAGE, ApplicationMetadataMessage_Type.CHAT_MESSAGE,
payload, payload,
this.contentTopic, this.contentTopic,
this.symmetricKey this.symmetricKey,
) )
} }
@ -554,14 +554,14 @@ export class Chat {
ApplicationMetadataMessage_Type.EDIT_MESSAGE, ApplicationMetadataMessage_Type.EDIT_MESSAGE,
payload, payload,
this.contentTopic, this.contentTopic,
this.symmetricKey this.symmetricKey,
) )
} }
public deleteMessage = async (messageId: string) => { public deleteMessage = async (messageId: string) => {
if (!this.client.account) { if (!this.client.account) {
throw new Error( throw new Error(
'Text message cannot be deleted without a created account' 'Text message cannot be deleted without a created account',
) )
} }
@ -587,13 +587,13 @@ export class Chat {
ApplicationMetadataMessage_Type.DELETE_MESSAGE, ApplicationMetadataMessage_Type.DELETE_MESSAGE,
payload, payload,
this.contentTopic, this.contentTopic,
this.symmetricKey this.symmetricKey,
) )
} }
public sendReaction = async ( public sendReaction = async (
messageId: string, messageId: string,
reaction: keyof ChatMessage['reactions'] reaction: keyof ChatMessage['reactions'],
) => { ) => {
if (!this.client.account) { if (!this.client.account) {
throw new Error('Account not initialized') throw new Error('Account not initialized')
@ -606,7 +606,7 @@ export class Chat {
} }
const retracted = message.reactions[reaction].has( const retracted = message.reactions[reaction].has(
`0x${this.client.account.publicKey}` `0x${this.client.account.publicKey}`,
) )
const payload = new EmojiReaction({ const payload = new EmojiReaction({
@ -623,13 +623,13 @@ export class Chat {
ApplicationMetadataMessage_Type.EMOJI_REACTION, ApplicationMetadataMessage_Type.EMOJI_REACTION,
payload, payload,
this.contentTopic, this.contentTopic,
this.symmetricKey this.symmetricKey,
) )
} }
public isAuthor = ( public isAuthor = (
message: ChatMessage, message: ChatMessage,
signerPublicKey: string signerPublicKey: string,
): boolean => { ): boolean => {
return message.signer === signerPublicKey return message.signer === signerPublicKey
} }
@ -637,7 +637,7 @@ export class Chat {
private getOldestFetchedMessage( private getOldestFetchedMessage(
oldestMessage: FetchedMessage | undefined, oldestMessage: FetchedMessage | undefined,
messageId: string, messageId: string,
messageTimestamp?: Date messageTimestamp?: Date,
): FetchedMessage { ): FetchedMessage {
let message: FetchedMessage let message: FetchedMessage

View File

@ -177,7 +177,7 @@ class Client {
await waitForRemotePeer( await waitForRemotePeer(
waku, waku,
[Protocols.Store, Protocols.Filter, Protocols.LightPush], [Protocols.Store, Protocols.Filter, Protocols.LightPush],
10 * 1000 10 * 1000,
) )
// Client // Client
@ -250,7 +250,7 @@ class Client {
type: ApplicationMetadataMessage_Type, type: ApplicationMetadataMessage_Type,
payload: Uint8Array, payload: Uint8Array,
contentTopic: string, contentTopic: string,
symKey: Uint8Array symKey: Uint8Array,
) => { ) => {
if (!this.waku) { if (!this.waku) {
throw new Error('Waku not started') throw new Error('Waku not started')
@ -278,7 +278,7 @@ class Client {
shard: 32, shard: 32,
}, },
}), }),
{ payload: message } { payload: message },
) )
} }

View File

@ -106,7 +106,7 @@ export class Community {
if (this.description) { if (this.description) {
return true return true
} }
} },
) )
return this.description return this.description
@ -120,12 +120,12 @@ export class Community {
shard: 32, shard: 32,
}), }),
], ],
this.client.handleWakuMessage this.client.handleWakuMessage,
) )
} }
private observeChatMessages = async ( private observeChatMessages = async (
chatDescriptions: CommunityDescription['chats'] chatDescriptions: CommunityDescription['chats'],
) => { ) => {
const chatPromises = Object.entries(chatDescriptions).map( const chatPromises = Object.entries(chatDescriptions).map(
async ([chatUuid, chatDescription]: [string, CommunityChat]) => { async ([chatUuid, chatDescription]: [string, CommunityChat]) => {
@ -134,7 +134,7 @@ export class Community {
this.client, this.client,
chatUuid, chatUuid,
MessageType.COMMUNITY_CHAT, MessageType.COMMUNITY_CHAT,
chatDescription chatDescription,
) )
this.chats.set(chatUuid, chat) this.chats.set(chatUuid, chat)
@ -146,18 +146,18 @@ export class Community {
shard: 32, shard: 32,
}), }),
], ],
this.client.handleWakuMessage this.client.handleWakuMessage,
) )
this.#chatUnobserveFns.set(chat.contentTopic, unobserveFn) this.#chatUnobserveFns.set(chat.contentTopic, unobserveFn)
} },
) )
await Promise.all(chatPromises) await Promise.all(chatPromises)
} }
private unobserveChatMessages = ( private unobserveChatMessages = (
chatDescription: CommunityDescription['chats'] chatDescription: CommunityDescription['chats'],
) => { ) => {
const contentTopics: string[] = [] const contentTopics: string[] = []
@ -179,7 +179,7 @@ export class Community {
} }
contentTopics.forEach(contentTopic => contentTopics.forEach(contentTopic =>
this.#chatUnobserveFns.get(contentTopic)?.() this.#chatUnobserveFns.get(contentTopic)?.(),
) )
} }
@ -207,7 +207,7 @@ export class Community {
// observe // observe
const removedChats = getDifferenceByKeys( const removedChats = getDifferenceByKeys(
this.description.chats, this.description.chats,
description.chats description.chats,
) )
if (Object.keys(removedChats).length) { if (Object.keys(removedChats).length) {
this.unobserveChatMessages(removedChats) this.unobserveChatMessages(removedChats)
@ -215,7 +215,7 @@ export class Community {
const addedChats = getDifferenceByKeys( const addedChats = getDifferenceByKeys(
description.chats, description.chats,
this.description.chats this.description.chats,
) )
if (Object.keys(addedChats).length) { if (Object.keys(addedChats).length) {
this.observeChatMessages(addedChats) this.observeChatMessages(addedChats)
@ -232,7 +232,7 @@ export class Community {
const members = getObjectsDifference( const members = getObjectsDifference(
this.description.members, this.description.members,
description.members description.members,
) )
this.addMembers(members.added) this.addMembers(members.added)
@ -251,7 +251,7 @@ export class Community {
// handle // handle
Object.entries(this.description.chats).forEach( Object.entries(this.description.chats).forEach(
([chatUuid, chatDescription]) => ([chatUuid, chatDescription]) =>
this.chats.get(chatUuid)?.handleChange(chatDescription) this.chats.get(chatUuid)?.handleChange(chatDescription),
) )
} }
@ -275,13 +275,13 @@ export class Community {
ApplicationMetadataMessage_Type.COMMUNITY_REQUEST_TO_JOIN, ApplicationMetadataMessage_Type.COMMUNITY_REQUEST_TO_JOIN,
payload, payload,
this.contentTopic, this.contentTopic,
this.symmetricKey this.symmetricKey,
) )
} }
public isOwner = ( public isOwner = (
/** Uncompressed. */ /** Uncompressed. */
signerPublicKey: string signerPublicKey: string,
): boolean => { ): boolean => {
return this.publicKey === `0x${compressPublicKey(signerPublicKey)}` return this.publicKey === `0x${compressPublicKey(signerPublicKey)}`
} }

View File

@ -14,7 +14,7 @@ export type Reactions = {
export function getReactions( export function getReactions(
reaction: EmojiReaction, reaction: EmojiReaction,
reactions: Reactions, reactions: Reactions,
publicKey: string publicKey: string,
): Reactions { ): Reactions {
const { type, retracted } = reaction const { type, retracted } = reaction

View File

@ -34,7 +34,7 @@ export function handleWakuMessage(
// state // state
client: Client, client: Client,
community: Community, community: Community,
account?: Account account?: Account,
): void { ): void {
// decode (layers) // decode (layers)
// validate // validate
@ -57,9 +57,8 @@ export function handleWakuMessage(
if (decodedProtocol) { if (decodedProtocol) {
messageToDecode = decodedProtocol.publicMessage messageToDecode = decodedProtocol.publicMessage
} }
} catch {
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} } catch {}
const decodedMetadata = ApplicationMetadataMessage.fromBinary(messageToDecode) const decodedMetadata = ApplicationMetadataMessage.fromBinary(messageToDecode)
if (!decodedMetadata.payload) { if (!decodedMetadata.payload) {
@ -69,12 +68,12 @@ export function handleWakuMessage(
const signerPublicKeyBytes = recoverPublicKey( const signerPublicKeyBytes = recoverPublicKey(
decodedMetadata.signature, decodedMetadata.signature,
decodedMetadata.payload decodedMetadata.payload,
) )
const messageId = payloadToId( const messageId = payloadToId(
decodedProtocol?.publicMessage ?? wakuMessage.payload, decodedProtocol?.publicMessage ?? wakuMessage.payload,
signerPublicKeyBytes signerPublicKeyBytes,
) )
const messageTimestamp = wakuMessage.timestamp const messageTimestamp = wakuMessage.timestamp
const signerPublicKey = `0x${bytesToHex(signerPublicKeyBytes)}` const signerPublicKey = `0x${bytesToHex(signerPublicKeyBytes)}`
@ -181,7 +180,7 @@ export function handleWakuMessage(
messageId, messageId,
decodedPayload.text, decodedPayload.text,
decodedPayload.clock, decodedPayload.clock,
signerPublicKey signerPublicKey,
) )
chat.setClock(decodedPayload.clock) chat.setClock(decodedPayload.clock)
@ -220,7 +219,7 @@ export function handleWakuMessage(
chat.handleDeletedMessage( chat.handleDeletedMessage(
messageId, messageId,
decodedPayload.clock, decodedPayload.clock,
signerPublicKey signerPublicKey,
) )
chat.setClock(decodedPayload.clock) chat.setClock(decodedPayload.clock)
@ -259,7 +258,7 @@ export function handleWakuMessage(
chat.handlePinnedMessage( chat.handlePinnedMessage(
messageId, messageId,
decodedPayload.clock, decodedPayload.clock,
decodedPayload.pinned decodedPayload.pinned,
) )
chat.setClock(decodedPayload.clock) chat.setClock(decodedPayload.clock)
@ -299,7 +298,7 @@ export function handleWakuMessage(
messageId, messageId,
decodedPayload, decodedPayload,
decodedPayload.clock, decodedPayload.clock,
signerPublicKey signerPublicKey,
) )
chat.setClock(decodedPayload.clock) chat.setClock(decodedPayload.clock)
@ -320,7 +319,6 @@ export function handleWakuMessage(
} }
} catch { } catch {
// protons-runtime throws when trying to decode invalid protocol buffers // protons-runtime throws when trying to decode invalid protocol buffers
// eslint-disable-next-line no-empty
} }
return return

View File

@ -4,10 +4,10 @@ import {
} from '../../protos/communities_pb' } from '../../protos/communities_pb'
export function isEncrypted( export function isEncrypted(
tokenPermissions: CommunityDescription['tokenPermissions'] tokenPermissions: CommunityDescription['tokenPermissions'],
): boolean { ): boolean {
return Object.values(tokenPermissions).some( return Object.values(tokenPermissions).some(
permission => permission =>
permission.type === CommunityTokenPermission_Type.BECOME_MEMBER permission.type === CommunityTokenPermission_Type.BECOME_MEMBER,
) )
} }

View File

@ -10,7 +10,7 @@ export function mapChatMessage(
signerPublicKey: string signerPublicKey: string
community: Community community: Community
chat: Chat chat: Chat
} },
): ChatMessage { ): ChatMessage {
const { messageId, chatUuid, signerPublicKey, community, chat } = props const { messageId, chatUuid, signerPublicKey, community, chat } = props

View File

@ -33,7 +33,7 @@ export class LocalStorage implements Storage {
try { try {
window.localStorage.setItem( window.localStorage.setItem(
this.#getStorageKey(key), this.#getStorageKey(key),
JSON.stringify(value) JSON.stringify(value),
) )
return true return true
} catch { } catch {

View File

@ -6,14 +6,14 @@ export const pbkdf2: PBKDF2 = async (
password: Uint8Array, password: Uint8Array,
salt: Uint8Array, salt: Uint8Array,
iterations: number, iterations: number,
keylen: number keylen: number,
): Promise<Uint8Array> => { ): Promise<Uint8Array> => {
const cryptoKey = await window.crypto.subtle.importKey( const cryptoKey = await window.crypto.subtle.importKey(
'raw', 'raw',
password, password,
{ name: 'PBKDF2' }, { name: 'PBKDF2' },
false, false,
['deriveBits'] ['deriveBits'],
) )
const derivedKey = await window.crypto.subtle.deriveBits( const derivedKey = await window.crypto.subtle.deriveBits(
@ -26,7 +26,7 @@ export const pbkdf2: PBKDF2 = async (
}, },
}, },
cryptoKey, cryptoKey,
keylen << 3 keylen << 3,
) )
return new Uint8Array(derivedKey) return new Uint8Array(derivedKey)

View File

@ -16,7 +16,7 @@ export class EthereumClient {
async resolvePublicKey( async resolvePublicKey(
ensName: string, ensName: string,
options: { compress: boolean } options: { compress: boolean },
): Promise<string | undefined> { ): Promise<string | undefined> {
try { try {
const resolver = await this.#provider.getResolver(ensName) const resolver = await this.#provider.getResolver(ensName)
@ -51,22 +51,22 @@ export class EthereumClient {
async resolveOwner( async resolveOwner(
registryContractAddress: string, registryContractAddress: string,
communityPublicKey: string communityPublicKey: string,
): Promise<string | undefined> { ): Promise<string | undefined> {
try { try {
const registryContract = new ethers.Contract( const registryContract = new ethers.Contract(
registryContractAddress, registryContractAddress,
['function getEntry(address _communityAddress) view returns (address)'], ['function getEntry(address _communityAddress) view returns (address)'],
this.#provider this.#provider,
) )
const ownerContractAddress = await registryContract.getEntry( const ownerContractAddress = await registryContract.getEntry(
publicKeyToETHAddress(communityPublicKey) publicKeyToETHAddress(communityPublicKey),
) )
const ownerContract = new ethers.Contract( const ownerContract = new ethers.Contract(
ownerContractAddress, ownerContractAddress,
['function signerPublicKey() view returns (bytes)'], ['function signerPublicKey() view returns (bytes)'],
this.#provider this.#provider,
) )
const owner = await ownerContract.signerPublicKey() const owner = await ownerContract.signerPublicKey()

View File

@ -1,6 +1,6 @@
export function getDifferenceByKeys<T extends Record<string, unknown>>( export function getDifferenceByKeys<T extends Record<string, unknown>>(
a: T, a: T,
b: T b: T,
): T { ): T {
const initialValue: Record<string, unknown> = {} const initialValue: Record<string, unknown> = {}

View File

@ -2,7 +2,7 @@ type Input<Value> = Record<string, Value>
export function getObjectsDifference<Value>( export function getObjectsDifference<Value>(
oldObject: Input<Value>, oldObject: Input<Value>,
newObject: Input<Value> newObject: Input<Value>,
) { ) {
const added: Record<string, Value> = {} const added: Record<string, Value> = {}
const removed: string[] = [] const removed: string[] = []

View File

@ -16,7 +16,7 @@ export type ChannelInfo = {
export function mapChannel( export function mapChannel(
communityChat: CommunityChat, communityChat: CommunityChat,
communityDescription: CommunityDescription communityDescription: CommunityDescription,
): ChannelInfo | undefined { ): ChannelInfo | undefined {
const community = mapCommunity(communityDescription) const community = mapCommunity(communityDescription)

View File

@ -18,7 +18,7 @@ export type Tag = {
} }
export function mapCommunity( export function mapCommunity(
communityDescription: CommunityDescription communityDescription: CommunityDescription,
): CommunityInfo | undefined { ): CommunityInfo | undefined {
const { identity, tags, members } = communityDescription const { identity, tags, members } = communityDescription

View File

@ -15,7 +15,7 @@ export type UserInfo = {
export function mapUser( export function mapUser(
contactCodeAdvertisement: ContactCodeAdvertisement, contactCodeAdvertisement: ContactCodeAdvertisement,
userPublicKey: string userPublicKey: string,
): UserInfo | undefined { ): UserInfo | undefined {
const { chatIdentity: identity } = contactCodeAdvertisement const { chatIdentity: identity } = contactCodeAdvertisement

View File

@ -62,7 +62,7 @@ class RequestClient {
contractAddresses?: Record<number, Record<string, string>> contractAddresses?: Record<number, Record<string, string>>
started?: boolean started?: boolean
environment?: 'development' | 'preview' | 'production' environment?: 'development' | 'preview' | 'production'
} },
) { ) {
const { environment = 'development' } = options const { environment = 'development' } = options
@ -136,7 +136,7 @@ class RequestClient {
await Promise.all([ await Promise.all([
async () => this.waku.stop(), async () => this.waku.stop(),
[...this.#ethereumClients.values()].map(async provider => [...this.#ethereumClients.values()].map(async provider =>
provider.stop() provider.stop(),
), ),
]) ])
@ -163,7 +163,7 @@ class RequestClient {
public fetchCommunity = async ( public fetchCommunity = async (
/** Compressed */ /** Compressed */
publicKey: string publicKey: string,
): Promise<CommunityInfo | undefined> => { ): Promise<CommunityInfo | undefined> => {
const communityDescription = await this.fetchCommunityDescription(publicKey) const communityDescription = await this.fetchCommunityDescription(publicKey)
@ -177,7 +177,7 @@ class RequestClient {
public fetchChannel = async ( public fetchChannel = async (
/** Compressed */ /** Compressed */
publicKey: string, publicKey: string,
uuid: string uuid: string,
): Promise<ChannelInfo | undefined> => { ): Promise<ChannelInfo | undefined> => {
const communityDescription = await this.fetchCommunityDescription(publicKey) const communityDescription = await this.fetchCommunityDescription(publicKey)
@ -192,11 +192,10 @@ class RequestClient {
public fetchUser = async ( public fetchUser = async (
/** Uncompressed */ /** Uncompressed */
publicKey: string publicKey: string,
): Promise<UserInfo | undefined> => { ): Promise<UserInfo | undefined> => {
const contactCodeAdvertisement = await this.fetchContactCodeAdvertisement( const contactCodeAdvertisement =
publicKey await this.fetchContactCodeAdvertisement(publicKey)
)
if (!contactCodeAdvertisement) { if (!contactCodeAdvertisement) {
return return
@ -207,7 +206,7 @@ class RequestClient {
public fetchCommunityDescription = async ( public fetchCommunityDescription = async (
/** Compressed */ /** Compressed */
communityPublicKey: string communityPublicKey: string,
): Promise<CommunityDescription | undefined> => { ): Promise<CommunityDescription | undefined> => {
const contentTopic = idToContentTopic(communityPublicKey) const contentTopic = idToContentTopic(communityPublicKey)
const symmetricKey = await generateKeyFromPassword(communityPublicKey) const symmetricKey = await generateKeyFromPassword(communityPublicKey)
@ -238,24 +237,25 @@ class RequestClient {
// decode // decode
const decodedCommunityDescription = CommunityDescription.fromBinary( const decodedCommunityDescription = CommunityDescription.fromBinary(
message.payload message.payload,
) )
// validate // validate
if ( if (
!isClockValid( !isClockValid(
BigInt(decodedCommunityDescription.clock), BigInt(decodedCommunityDescription.clock),
message.timestamp message.timestamp,
) )
) { ) {
continue continue
} }
const ownerTokenPermission = Object.values( const ownerTokenPermission = Object.values(
decodedCommunityDescription.tokenPermissions decodedCommunityDescription.tokenPermissions,
).find( ).find(
permission => permission =>
permission.type === CommunityTokenPermission_Type.BECOME_TOKEN_OWNER permission.type ===
CommunityTokenPermission_Type.BECOME_TOKEN_OWNER,
) )
if (ownerTokenPermission) { if (ownerTokenPermission) {
const criteria = ownerTokenPermission.tokenCriteria[0] const criteria = ownerTokenPermission.tokenCriteria[0]
@ -281,7 +281,7 @@ class RequestClient {
const ownerPublicKey = await ethereumClient.resolveOwner( const ownerPublicKey = await ethereumClient.resolveOwner(
this.#contractAddresses[Number(chainId)] this.#contractAddresses[Number(chainId)]
.CommunityOwnerTokenRegistry, .CommunityOwnerTokenRegistry,
communityPublicKey communityPublicKey,
) )
if (ownerPublicKey !== message.signerPublicKey) { if (ownerPublicKey !== message.signerPublicKey) {
@ -301,11 +301,11 @@ class RequestClient {
} }
private fetchContactCodeAdvertisement = async ( private fetchContactCodeAdvertisement = async (
publicKey: string publicKey: string,
): Promise<ContactCodeAdvertisement | undefined> => { ): Promise<ContactCodeAdvertisement | undefined> => {
const contentTopic = idToContentTopic(`${publicKey}-contact-code`) const contentTopic = idToContentTopic(`${publicKey}-contact-code`)
const symmetricKey = await generateKeyFromPassword( const symmetricKey = await generateKeyFromPassword(
`${publicKey}-contact-code` `${publicKey}-contact-code`,
) )
const wakuMessageGenerator = this.waku.store.queryGenerator([ const wakuMessageGenerator = this.waku.store.queryGenerator([
@ -336,7 +336,7 @@ class RequestClient {
// decode // decode
const decodedContactCode = ContactCodeAdvertisement.fromBinary( const decodedContactCode = ContactCodeAdvertisement.fromBinary(
message.payload message.payload,
) )
// validate // validate
@ -347,7 +347,7 @@ class RequestClient {
if ( if (
!isClockValid( !isClockValid(
BigInt(decodedContactCode.chatIdentity.clock), BigInt(decodedContactCode.chatIdentity.clock),
message.timestamp message.timestamp,
) )
) { ) {
continue continue
@ -364,7 +364,7 @@ class RequestClient {
} }
private handleWakuMessage = ( private handleWakuMessage = (
wakuMessage: DecodedMessage wakuMessage: DecodedMessage,
): ):
| { | {
timestamp: Date timestamp: Date
@ -394,17 +394,17 @@ class RequestClient {
if (decodedSegment) { if (decodedSegment) {
const unsegmentedMessageHash = bytesToHex( const unsegmentedMessageHash = bytesToHex(
decodedSegment.entireMessageHash decodedSegment.entireMessageHash,
) )
const segmentedWakuMessages = this.#segmentedWakuMessages.get( const segmentedWakuMessages = this.#segmentedWakuMessages.get(
unsegmentedMessageHash unsegmentedMessageHash,
) )
if (!segmentedWakuMessages) { if (!segmentedWakuMessages) {
this.#segmentedWakuMessages.set( this.#segmentedWakuMessages.set(
unsegmentedMessageHash, unsegmentedMessageHash,
new Map([[decodedSegment.index, decodedSegment]]) new Map([[decodedSegment.index, decodedSegment]]),
) )
return return
@ -430,13 +430,12 @@ class RequestClient {
messageToDecode = unsegmentedPayload messageToDecode = unsegmentedPayload
this.#segmentedWakuMessages.delete(unsegmentedMessageHash) this.#segmentedWakuMessages.delete(unsegmentedMessageHash)
} catch (error) { } catch {
return return
} }
} }
} catch {
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} } catch {}
let decodedProtocol let decodedProtocol
try { try {
@ -447,9 +446,8 @@ class RequestClient {
} else if (decodedProtocol) { } else if (decodedProtocol) {
messageToDecode = decodedProtocol.publicMessage messageToDecode = decodedProtocol.publicMessage
} }
} catch {
// eslint-disable-next-line no-empty // eslint-disable-next-line no-empty
} } catch {}
let decodedMetadata let decodedMetadata
try { try {
@ -468,12 +466,12 @@ class RequestClient {
const signerPublicKeyBytes = recoverPublicKey( const signerPublicKeyBytes = recoverPublicKey(
decodedMetadata.signature, decodedMetadata.signature,
decodedMetadata.payload decodedMetadata.payload,
) )
const messageId = payloadToId( const messageId = payloadToId(
decodedProtocol?.publicMessage ?? wakuMessage.payload, decodedProtocol?.publicMessage ?? wakuMessage.payload,
signerPublicKeyBytes signerPublicKeyBytes,
) )
// already handled // already handled
@ -493,7 +491,7 @@ class RequestClient {
} }
export async function createRequestClient( export async function createRequestClient(
options: RequestClientOptions options: RequestClientOptions,
): Promise<RequestClient> { ): Promise<RequestClient> {
return await RequestClient.start(options) return await RequestClient.start(options)
} }

View File

@ -4,7 +4,7 @@ export function compressPublicKey(publicKey: string): string {
try { try {
const pk = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix" const pk = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix"
return secp.Point.fromHex(pk).toHex(true) return secp.Point.fromHex(pk).toHex(true)
} catch (error) { } catch {
throw new Error('Invalid public key') throw new Error('Invalid public key')
} }
} }

View File

@ -46,7 +46,7 @@ describe('Create URLs', () => {
} as unknown as CommunityDescription } as unknown as CommunityDescription
expect(createCommunityURLWithChatKey(community.chatKey).toString()).toBe( expect(createCommunityURLWithChatKey(community.chatKey).toString()).toBe(
'https://status.app/c#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11' 'https://status.app/c#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11',
) )
expect( expect(
( (
@ -58,11 +58,11 @@ describe('Create URLs', () => {
membersCount: 446_744, membersCount: 446_744,
tagIndices: [1, 33, 51], tagIndices: [1, 33, 51],
}, },
community.chatKey community.chatKey,
) )
).toString() ).toString(),
).toBe( ).toBe(
'https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11' 'https://status.app/c/iyKACkQKB0Rvb2RsZXMSJ0NvbG9yaW5nIHRoZSB3b3JsZCB3aXRoIGpveSDigKIg4bSXIOKAohiYohsiByMxMzFEMkYqAwEhMwM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11',
) )
}) })
@ -95,9 +95,9 @@ describe('Create URLs', () => {
} as unknown as CommunityChat } as unknown as CommunityChat
expect( expect(
createChannelURLWithChatKey(chat.uuid, community.chatKey).toString() createChannelURLWithChatKey(chat.uuid, community.chatKey).toString(),
).toBe( ).toBe(
'https://status.app/cc/003cdcd5-e065-48f9-b166-b1a94ac75a11#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11' 'https://status.app/cc/003cdcd5-e065-48f9-b166-b1a94ac75a11#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11',
) )
expect( expect(
( (
@ -112,11 +112,11 @@ describe('Create URLs', () => {
displayName: community.description.identity!.displayName, displayName: community.description.identity!.displayName,
}, },
} as unknown as PlainMessage<ChannelProto>, } as unknown as PlainMessage<ChannelProto>,
community.chatKey community.chatKey,
) )
).toString() ).toString(),
).toBe( ).toBe(
'https://status.app/cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11' 'https://status.app/cc/G54AAKwObLdpiGjXnckYzRcOSq0QQAS_CURGfqVU42ceGHCObstUIknTTZDOKF3E8y2MSicncpO7fTskXnoACiPKeejvjtLTGWNxUhlT7fyQS7Jrr33UVHluxv_PLjV2ePGw5GQ33innzeK34pInIgUGs5RjdQifMVmURalxxQKwiuoY5zwIjixWWRHqjHM=#zQ3shYSHp7GoiXaauJMnDcjwU2yNjdzpXLosAWapPS4CFxc11',
) )
}) })
@ -134,10 +134,10 @@ describe('Create URLs', () => {
} as unknown as ContactCodeAdvertisement } as unknown as ContactCodeAdvertisement
expect(createUserURLWithENS(account.ensName).toString()).toBe( expect(createUserURLWithENS(account.ensName).toString()).toBe(
'https://status.app/u#testing.stateofus.eth' 'https://status.app/u#testing.stateofus.eth',
) )
expect(createUserURLWithChatKey(account.chatKey).toString()).toBe( expect(createUserURLWithChatKey(account.chatKey).toString()).toBe(
'https://status.app/u#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj' 'https://status.app/u#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj',
) )
expect( expect(
( (
@ -147,11 +147,11 @@ describe('Create URLs', () => {
displayName: account.description.chatIdentity!.displayName, displayName: account.description.chatIdentity!.displayName,
color: account.description.chatIdentity!.color, color: account.description.chatIdentity!.color,
}, },
account.chatKey account.chatKey,
) )
).toString() ).toString(),
).toBe( ).toBe(
'https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj' 'https://status.app/u/G10A4B0JdgwyRww90WXtnP1oNH1ZLQNM0yX0Ja9YyAMjrqSZIYINOHCbFhrnKRAcPGStPxCMJDSZlGCKzmZrJcimHY8BbcXlORrElv_BbQEegnMDPx1g9C5VVNl0fE4y#zQ3shwQPhRuDJSjVGVBnTjCdgXy5i9WQaeVPdGJD6yTarJQSj',
) )
}) })
}) })

View File

@ -15,7 +15,7 @@ export function createCommunityURLWithChatKey(communityChatKey: string): URL {
export async function createCommunityURLWithData( export async function createCommunityURLWithData(
communityData: PlainMessage<Community>, communityData: PlainMessage<Community>,
communityChatKey: string communityChatKey: string,
): Promise<URL> { ): Promise<URL> {
const encodedURLData = encodeCommunityURLData(communityData) const encodedURLData = encodeCommunityURLData(communityData)
@ -24,14 +24,14 @@ export async function createCommunityURLWithData(
export function createChannelURLWithChatKey( export function createChannelURLWithChatKey(
channelUuid: string, channelUuid: string,
communityChatKey: string communityChatKey: string,
): URL { ): URL {
return new URL(`${BASE_URL}/cc/${channelUuid}#${communityChatKey}`) return new URL(`${BASE_URL}/cc/${channelUuid}#${communityChatKey}`)
} }
export async function createChannelURLWithData( export async function createChannelURLWithData(
channelData: PlainMessage<Channel>, channelData: PlainMessage<Channel>,
communityChatKey: string communityChatKey: string,
): Promise<URL> { ): Promise<URL> {
const encodedURLData = encodeChannelURLData(channelData) const encodedURLData = encodeChannelURLData(channelData)
@ -48,7 +48,7 @@ export function createUserURLWithChatKey(chatKey: string): URL {
export async function createUserURLWithData( export async function createUserURLWithData(
userData: PlainMessage<User>, userData: PlainMessage<User>,
userChatKey: string userChatKey: string,
): Promise<URL> { ): Promise<URL> {
const encodedURLData = encodeUserURLData(userData) const encodedURLData = encodeUserURLData(userData)

View File

@ -4,7 +4,7 @@ export function decompressPublicKey(publicKey: string): string {
try { try {
const pk = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix" const pk = publicKey.replace(/^0[xX]/, '') // ensures hexadecimal digits without "base prefix"
return secp.Point.fromHex(pk).toHex() return secp.Point.fromHex(pk).toHex()
} catch (error) { } catch {
throw new Error('Invalid public key') throw new Error('Invalid public key')
} }
} }

View File

@ -4,42 +4,42 @@ import { deserializePublicKey } from './deserialize-public-key'
test('should deserialize public key from compressed base58btc encoding', () => { test('should deserialize public key from compressed base58btc encoding', () => {
expect( expect(
deserializePublicKey('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') deserializePublicKey('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU'),
).toEqual( ).toEqual(
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) )
}) })
test('should deserialize public key from compressed hexadecimal encoding', () => { test('should deserialize public key from compressed hexadecimal encoding', () => {
expect( expect(
deserializePublicKey( deserializePublicKey(
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) ),
).toEqual( ).toEqual(
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) )
}) })
test('should deserialize public key from uncompressed hexadecimal encoding', () => { test('should deserialize public key from uncompressed hexadecimal encoding', () => {
expect( expect(
deserializePublicKey( deserializePublicKey(
'0x049f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb454295513305b23fcf11d005ee622144fc402b713a8928f80d705781e2e78d701c6e01bfc4' '0x049f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb454295513305b23fcf11d005ee622144fc402b713a8928f80d705781e2e78d701c6e01bfc4',
) ),
).toEqual( ).toEqual(
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) )
}) })
test('should throw when deserializing unsupported multibase encoding', () => { test('should throw when deserializing unsupported multibase encoding', () => {
expect(() => expect(() =>
deserializePublicKey('ZQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') deserializePublicKey('ZQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU'),
).toThrowError() ).toThrowError()
}) })
test('should throw when deserializing invalid public key', () => { test('should throw when deserializing invalid public key', () => {
expect(() => expect(() =>
deserializePublicKey( deserializePublicKey(
'0x019f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x019f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) ),
).toThrowError() ).toThrowError()
}) })

View File

@ -30,7 +30,7 @@ type MulticodecCode = (typeof VALID_MULTICODEC_CODES)[number]
*/ */
export function deserializePublicKey( export function deserializePublicKey(
publicKey: string, // uncompressed, compressed, or compressed & encoded publicKey: string, // uncompressed, compressed, or compressed & encoded
options = { compress: true } options = { compress: true },
): string { ): string {
const multibasePublicKey = publicKey.replace(/^0[xX]/, 'f') // ensure multibase code for hexadecimal encoding const multibasePublicKey = publicKey.replace(/^0[xX]/, 'f') // ensure multibase code for hexadecimal encoding
const multibaseCode = multibasePublicKey[0] as MultibaseCode const multibaseCode = multibasePublicKey[0] as MultibaseCode
@ -52,7 +52,7 @@ export function deserializePublicKey(
} }
hexadecimalPublicKey = toHex( hexadecimalPublicKey = toHex(
base58btcPublicKey.slice(multicodecCodeByteLength) base58btcPublicKey.slice(multicodecCodeByteLength),
) )
break break

View File

@ -27,7 +27,7 @@ describe('Encode URL data', () => {
const decodedData = decodeCommunityURLData(encodedData) const decodedData = decodeCommunityURLData(encodedData)
expect(encodedData).toBe( expect(encodedData).toBe(
'G8QAgC0OzDOfHB4N5V1zajCKmHvbUAXB6XK6XYLS60WrOmCEEVgFEJaHsLkpTevR-XHc03r4B2pKTOoYJwqbLrLw9u2DhyzlK5rEWE09Dy7oPbVSPhwlOKozCQuAsMX84eJimcwKWNer82gPcCrbhPM-Zx1s3-glfEojrEYRDp61MM2DTNiD92_BDIN3eYvvcQsfT-quKYmaf1_i9Kpzk0Fi' 'G8QAgC0OzDOfHB4N5V1zajCKmHvbUAXB6XK6XYLS60WrOmCEEVgFEJaHsLkpTevR-XHc03r4B2pKTOoYJwqbLrLw9u2DhyzlK5rEWE09Dy7oPbVSPhwlOKozCQuAsMX84eJimcwKWNer82gPcCrbhPM-Zx1s3-glfEojrEYRDp61MM2DTNiD92_BDIN3eYvvcQsfT-quKYmaf1_i9Kpzk0Fi',
) )
expect(decodedData).toEqual(data) expect(decodedData).toEqual(data)
}) })
@ -73,12 +73,12 @@ describe('Encode URL data', () => {
} }
const encodedData = encodeChannelURLData( const encodedData = encodeChannelURLData(
data as unknown as PlainMessage<Channel> data as unknown as PlainMessage<Channel>,
) )
const decodedData = decodeChannelURLData(encodedData) const decodedData = decodeChannelURLData(encodedData)
expect(encodedData).toBe( expect(encodedData).toBe(
'GxoBQCwO7MbOG73h9C_ECmmNLFveFT5wVETFRTal3e2y0Xyou1sfFAV-SsZH0MTwwDRpTuEnp26giuDkQ9algElBJsdfwJYmFggG1GoJJJjnNgaO49Oj0C6qYIaxnbTEvF-6xH6jxmPg5oHSFAguuFhgFpIIby42hURPGM87X47XATSzJGec5_OsF9ZthVfGzWIIRgcltFjTBbPZTiKBcdj_5iQ5DTbPW4LaTsu46RK2OuuSPOXd-ddgstj0g6uYHm2WBUDBjYa1oPniW2ZdVpFpY-ubJq587eM-JytEhXc_Kuq8tiU=' 'GxoBQCwO7MbOG73h9C_ECmmNLFveFT5wVETFRTal3e2y0Xyou1sfFAV-SsZH0MTwwDRpTuEnp26giuDkQ9algElBJsdfwJYmFggG1GoJJJjnNgaO49Oj0C6qYIaxnbTEvF-6xH6jxmPg5oHSFAguuFhgFpIIby42hURPGM87X47XATSzJGec5_OsF9ZthVfGzWIIRgcltFjTBbPZTiKBcdj_5iQ5DTbPW4LaTsu46RK2OuuSPOXd-ddgstj0g6uYHm2WBUDBjYa1oPniW2ZdVpFpY-ubJq587eM-JytEhXc_Kuq8tiU=',
) )
expect(decodedData).toEqual(data) expect(decodedData).toEqual(data)
}) })
@ -150,7 +150,7 @@ describe('Encode URL data', () => {
const decodedData = decodeUserURLData(encodedData) const decodedData = decodeUserURLData(encodedData)
expect(encodedData).toBe( expect(encodedData).toBe(
'GxgBoJwHdsOLl4DWt55mGELN6clGsb1UKTEkT0KUMDfwhWFpUyWH_cefTnvlcSf2JUXCOAWoY5ywzry-LnJ-PjgOGT1Pkb8riQp7ghv6Zu-x70x4m8lncZaRWpDN-sEfT85idUCWvppT_QFNa2A6J3Gr69UJGvWmL3S4DBwX2Jr7LBTNOvFPo6lejNUb-xizlAMUTrokunCH-qNmgtU6UK0J6Vkn8Ce35XGBFObxpxnAtnC_J_D-SrBCBnjiUlwH0ViNr3lHBg==' 'GxgBoJwHdsOLl4DWt55mGELN6clGsb1UKTEkT0KUMDfwhWFpUyWH_cefTnvlcSf2JUXCOAWoY5ywzry-LnJ-PjgOGT1Pkb8riQp7ghv6Zu-x70x4m8lncZaRWpDN-sEfT85idUCWvppT_QFNa2A6J3Gr69UJGvWmL3S4DBwX2Jr7LBTNOvFPo6lejNUb-xizlAMUTrokunCH-qNmgtU6UK0J6Vkn8Ce35XGBFObxpxnAtnC_J_D-SrBCBnjiUlwH0ViNr3lHBg==',
) )
expect(decodedData).toEqual(data) expect(decodedData).toEqual(data)
}) })

View File

@ -8,7 +8,7 @@ test('should create symmetric key from password', async () => {
const symKey = await generateKeyFromPassword(password) const symKey = await generateKeyFromPassword(password)
expect(bytesToHex(symKey)).toEqual( expect(bytesToHex(symKey)).toEqual(
'c49ad65ebf2a7b7253bf400e3d27719362a91b2c9b9f54d50a69117021666c33' 'c49ad65ebf2a7b7253bf400e3d27719362a91b2c9b9f54d50a69117021666c33',
) )
}) })
@ -18,6 +18,6 @@ test('should generate symmetric key from chat ID', async () => {
const symKey = await generateKeyFromPassword(chatId) const symKey = await generateKeyFromPassword(chatId)
expect(bytesToHex(symKey)).toEqual( expect(bytesToHex(symKey)).toEqual(
'76ff5bf0a74a8e724367c7fc003f066d477641f468768a8da2817addf5c2ce76' '76ff5bf0a74a8e724367c7fc003f066d477641f468768a8da2817addf5c2ce76',
) )
}) })

View File

@ -8,13 +8,13 @@ const AES_KEY_LENGTH = 32 // bytes
* status-go: https://github.com/status-im/status-go/blob/a471fed6a64e01a1aba8d925377fba045a5aa9f9/wakuv2/waku.go#L713 * status-go: https://github.com/status-im/status-go/blob/a471fed6a64e01a1aba8d925377fba045a5aa9f9/wakuv2/waku.go#L713
*/ */
export async function generateKeyFromPassword( export async function generateKeyFromPassword(
password: string password: string,
): Promise<Uint8Array> { ): Promise<Uint8Array> {
return await pbkdf2( return await pbkdf2(
utf8ToBytes(password), utf8ToBytes(password),
utf8ToBytes(''), utf8ToBytes(''),
65356, 65356,
AES_KEY_LENGTH, AES_KEY_LENGTH,
'sha256' 'sha256',
) )
} }

View File

@ -2,7 +2,7 @@ const MAX_OFFSET = BigInt(120 * 1000)
export function isClockValid( export function isClockValid(
messageClock: bigint, messageClock: bigint,
messageTimestamp: Date messageTimestamp: Date,
): boolean { ): boolean {
if (messageClock <= 0) { if (messageClock <= 0) {
return false return false

View File

@ -3,7 +3,7 @@ import { bytesToHex, concatBytes } from 'ethereum-cryptography/utils'
export function payloadToId( export function payloadToId(
payload: Uint8Array, payload: Uint8Array,
publicKey: Uint8Array publicKey: Uint8Array,
): string { ): string {
const hash = keccak256(concatBytes(publicKey, payload)) // order matters const hash = keccak256(concatBytes(publicKey, payload)) // order matters
const hex = bytesToHex(hash) const hex = bytesToHex(hash)

View File

@ -8,8 +8,8 @@ import {
test('should return color hash from public key', () => { test('should return color hash from public key', () => {
expect( expect(
publicKeyToColorHash( publicKeyToColorHash(
'0x04e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8' '0x04e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8',
) ),
).toEqual([ ).toEqual([
[3, 30], [3, 30],
[2, 10], [2, 10],
@ -30,13 +30,13 @@ test('should throw for invalid public keys', () => {
expect(() => publicKeyToColorHash('0x01')).toThrow() expect(() => publicKeyToColorHash('0x01')).toThrow()
expect(() => expect(() =>
publicKeyToColorHash( publicKeyToColorHash(
'0x01e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8' '0x01e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8',
) ),
).toThrow() ).toThrow()
expect(() => expect(() =>
publicKeyToColorHash( publicKeyToColorHash(
'0x04425da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8' '0x04425da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8',
) ),
).toThrow() ).toThrow()
}) })

View File

@ -17,7 +17,7 @@ export function publicKeyToColorHash(publicKey: string): ColorHash {
const colorHash = hexToColorHash( const colorHash = hexToColorHash(
colorHashHex, colorHashHex,
COLOR_HASH_COLORS_COUNT, COLOR_HASH_COLORS_COUNT,
COLOR_HASH_SEGMENT_MAX_LENGTH COLOR_HASH_SEGMENT_MAX_LENGTH,
) )
return colorHash return colorHash
@ -26,11 +26,11 @@ export function publicKeyToColorHash(publicKey: string): ColorHash {
export function hexToColorHash( export function hexToColorHash(
hex: string, hex: string,
colorsCount: number, colorsCount: number,
segmentLength: number segmentLength: number,
): ColorHash { ): ColorHash {
const colorIndices = numberToIndices( const colorIndices = numberToIndices(
BigInt(`0x${hex}`), BigInt(`0x${hex}`),
BigInt(colorsCount * segmentLength) BigInt(colorsCount * segmentLength),
) )
const colorHash = colorIndicesToColorHash(colorIndices, colorsCount) const colorHash = colorIndicesToColorHash(colorIndices, colorsCount)
@ -57,7 +57,7 @@ export function numberToIndices(number: bigint, base: bigint): bigint[] {
function colorIndicesToColorHash( function colorIndicesToColorHash(
colorIndices: bigint[], colorIndices: bigint[],
colorsCount: number colorsCount: number,
): ColorHash { ): ColorHash {
const colorHash: ColorHash = [] const colorHash: ColorHash = []
let previousColorIndex: number | undefined = undefined let previousColorIndex: number | undefined = undefined

View File

@ -5,23 +5,23 @@ import { publicKeyToEmojiHash } from './public-key-to-emoji-hash'
test('should return emoji hash from public key', () => { test('should return emoji hash from public key', () => {
expect( expect(
publicKeyToEmojiHash( publicKeyToEmojiHash(
'0x04e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8' '0x04e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8',
) ),
).toEqual('👦🏽🦹🏻👶🏿🛁🌁🙌🏻🙇🏽‍♂️🙌🏾🤥🐛👩🏽‍🔧🔧⚙️🧒🏽') ).toEqual('👦🏽🦹🏻👶🏿🛁🌁🙌🏻🙇🏽‍♂️🙌🏾🤥🐛👩🏽‍🔧🔧⚙️🧒🏽')
expect( expect(
publicKeyToEmojiHash( publicKeyToEmojiHash(
'0x0400000000000000000000000000000000000000000000000000000000000000014218F20AE6C646B363DB68605822FB14264CA8D2587FDD6FBC750D587E76A7EE' '0x0400000000000000000000000000000000000000000000000000000000000000014218F20AE6C646B363DB68605822FB14264CA8D2587FDD6FBC750D587E76A7EE',
) ),
).toEqual('😀😀😀😀😀😀😀😀😀😀😀😀😀😀') ).toEqual('😀😀😀😀😀😀😀😀😀😀😀😀😀😀')
expect( expect(
publicKeyToEmojiHash( publicKeyToEmojiHash(
'0x04000000000000000000000000000000000000000010000000000000000000000033600332D373318ECC2F212A30A5750D2EAC827B6A32B33D326CCF369B12B1BE' '0x04000000000000000000000000000000000000000010000000000000000000000033600332D373318ECC2F212A30A5750D2EAC827B6A32B33D326CCF369B12B1BE',
) ),
).toEqual('😀😀😀😀😀😀😀😀😀😀😀😀😀😃') ).toEqual('😀😀😀😀😀😀😀😀😀😀😀😀😀😃')
expect( expect(
publicKeyToEmojiHash( publicKeyToEmojiHash(
'0x040000000000000000000000000000000000000000200000000000000000000000353050BFE33B724E60A0C600FBA565A9B62217B1BD35BF9848F2AB847C598B30' '0x040000000000000000000000000000000000000000200000000000000000000000353050BFE33B724E60A0C600FBA565A9B62217B1BD35BF9848F2AB847C598B30',
) ),
).toEqual('😀😀😀😀😀😀😀😀😀😀😀😀😀😄') ).toEqual('😀😀😀😀😀😀😀😀😀😀😀😀😀😄')
}) })
@ -30,12 +30,12 @@ test('should throw for invalid public keys', () => {
expect(() => publicKeyToEmojiHash('0x01')).toThrow() expect(() => publicKeyToEmojiHash('0x01')).toThrow()
expect(() => expect(() =>
publicKeyToEmojiHash( publicKeyToEmojiHash(
'0x01e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8' '0x01e25da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8',
) ),
).toThrow() ).toThrow()
expect(() => expect(() =>
publicKeyToEmojiHash( publicKeyToEmojiHash(
'0x04425da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8' '0x04425da6994ea2dc4ac70727e07eca153ae92bf7609db7befb7ebdceaad348f4fc55bbe90abf9501176301db5aa103fc0eb3bc3750272a26c424a10887db2a7ea8',
) ),
).toThrow() ).toThrow()
}) })

View File

@ -19,7 +19,7 @@ export function publicKeyToEmojiHash(publicKey: string): string {
const emojiHash = hexToEmojiHash( const emojiHash = hexToEmojiHash(
emojiHashHex, emojiHashHex,
MIN_EMOJI_HASH_EMOJIS_COUNT, MIN_EMOJI_HASH_EMOJIS_COUNT,
EMOJI_HASH_LENGTH EMOJI_HASH_LENGTH,
) )
return emojiHash return emojiHash
@ -28,7 +28,7 @@ export function publicKeyToEmojiHash(publicKey: string): string {
export function hexToEmojiHash( export function hexToEmojiHash(
hex: string, hex: string,
emojisCount: number, emojisCount: number,
hashLength: number hashLength: number,
): string { ): string {
const emojiIndices = numberToIndices(BigInt(`0x${hex}`), BigInt(emojisCount)) const emojiIndices = numberToIndices(BigInt(`0x${hex}`), BigInt(emojisCount))
const emojiHash = emojiIndicesToEmojiHash(emojiIndices, hashLength) const emojiHash = emojiIndicesToEmojiHash(emojiIndices, hashLength)

View File

@ -5,7 +5,7 @@ import { publicKeyToETHAddress } from './public-key-to-eth-address'
test('should return ETH address from public key', () => { test('should return ETH address from public key', () => {
expect( expect(
publicKeyToETHAddress( publicKeyToETHAddress(
'0x02bcbe39785b55a22383f82ac631ea7500e204627369c4ea01d9296af0ea573f57' '0x02bcbe39785b55a22383f82ac631ea7500e204627369c4ea01d9296af0ea573f57',
) ),
).toEqual('0x0A1ec0002dDB927B03049F1aD8D589aBEA4Ba4b3') ).toEqual('0x0A1ec0002dDB927B03049F1aD8D589aBEA4Ba4b3')
}) })

View File

@ -18,7 +18,7 @@ test('should recover public key', async () => {
const signature = await account.sign(payload) const signature = await account.sign(payload)
expect(bytesToHex(recoverPublicKey(signature, payload))).toEqual( expect(bytesToHex(recoverPublicKey(signature, payload))).toEqual(
account.publicKey account.publicKey,
) )
}) })
@ -56,7 +56,7 @@ test('should recover public key from fixture', async () => {
const result = recoverPublicKey( const result = recoverPublicKey(
metadataFixture.signature, metadataFixture.signature,
metadataFixture.payload metadataFixture.payload,
) )
expect(result).toEqual(publicKeySnapshot) expect(result).toEqual(publicKeySnapshot)
@ -80,6 +80,6 @@ test('should throw error when signature length is not 65 bytes', async () => {
]) ])
expect(() => expect(() =>
recoverPublicKey(signature, payload) recoverPublicKey(signature, payload),
).toThrowErrorMatchingInlineSnapshot(`"Signature must be 65 bytes long"`) ).toThrowErrorMatchingInlineSnapshot(`"Signature must be 65 bytes long"`)
}) })

View File

@ -8,7 +8,7 @@ import { recoverPublicKey as secpRecoverPublicKey } from 'ethereum-cryptography/
*/ */
export function recoverPublicKey( export function recoverPublicKey(
sig: Uint8Array, sig: Uint8Array,
payload: Uint8Array payload: Uint8Array,
): Uint8Array { ): Uint8Array {
if (sig.length !== 65) { if (sig.length !== 65) {
throw new Error('Signature must be 65 bytes long') throw new Error('Signature must be 65 bytes long')

View File

@ -5,35 +5,35 @@ import { serializePublicKey } from './serialize-public-key'
test('should serialize compressed public key to base58btc encoding', () => { test('should serialize compressed public key to base58btc encoding', () => {
expect( expect(
serializePublicKey( serializePublicKey(
'0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x029f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) ),
).toEqual('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') ).toEqual('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU')
}) })
test('should serialize uncompressed public key to base58btc encoding', () => { test('should serialize uncompressed public key to base58btc encoding', () => {
expect( expect(
serializePublicKey( serializePublicKey(
'0x049f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb454295513305b23fcf11d005ee622144fc402b713a8928f80d705781e2e78d701c6e01bfc4' '0x049f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb454295513305b23fcf11d005ee622144fc402b713a8928f80d705781e2e78d701c6e01bfc4',
) ),
).toEqual('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') ).toEqual('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU')
}) })
test('should return the public key if already serialized to base58btc encoding', () => { test('should return the public key if already serialized to base58btc encoding', () => {
expect( expect(
serializePublicKey('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') serializePublicKey('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU'),
).toEqual('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') ).toEqual('zQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU')
}) })
test('should throw when serializing unsupported multibase encoding', () => { test('should throw when serializing unsupported multibase encoding', () => {
expect(() => expect(() =>
serializePublicKey('ZQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU') serializePublicKey('ZQ3shY7r4cAdg4eUF5dfcuCqCFzWmdjHW4SX5hspM9ucAarfU'),
).toThrowError() ).toThrowError()
}) })
test('should throw when serializing invalid public key', () => { test('should throw when serializing invalid public key', () => {
expect(() => expect(() =>
serializePublicKey( serializePublicKey(
'0x019f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133' '0x019f196bbfef4fa6a5eb81dd802133a63498325445ca1af1d154b1bb4542955133',
) ),
).toThrowError() ).toThrowError()
}) })

View File

@ -7,10 +7,10 @@ import { deserializePublicKey } from './deserialize-public-key'
* @see https://specs.status.im/spec/2#public-key-serialization for specification * @see https://specs.status.im/spec/2#public-key-serialization for specification
*/ */
export function serializePublicKey( export function serializePublicKey(
publicKey: string // uncompressed, compressed, or compressed & encoded publicKey: string, // uncompressed, compressed, or compressed & encoded
): string { ): string {
const hexadecimalPublicKey = hexToBytes( const hexadecimalPublicKey = hexToBytes(
deserializePublicKey(publicKey).replace(/^0[xX]/, '') deserializePublicKey(publicKey).replace(/^0[xX]/, ''),
) // validated and compressed ) // validated and compressed
return base58btc.encode(new Uint8Array([231, 1, ...hexadecimalPublicKey])) return base58btc.encode(new Uint8Array([231, 1, ...hexadecimalPublicKey]))

View File

@ -13,7 +13,7 @@ import { recoverPublicKey } from './recover-public-key'
*/ */
export async function signData( export async function signData(
data: Uint8Array | string, data: Uint8Array | string,
privateKey: Uint8Array | string privateKey: Uint8Array | string,
): Promise<Uint8Array> { ): Promise<Uint8Array> {
const bytes = ensureBytes(data) const bytes = ensureBytes(data)
const hash = keccak256(bytes) const hash = keccak256(bytes)
@ -29,7 +29,7 @@ export async function signData(
export function verifySignedData( export function verifySignedData(
signature: Uint8Array, signature: Uint8Array,
data: Uint8Array | string, data: Uint8Array | string,
publicKey?: string publicKey?: string,
): boolean { ): boolean {
const bytes = ensureBytes(data) const bytes = ensureBytes(data)
const hash = keccak256(bytes) const hash = keccak256(bytes)

File diff suppressed because it is too large Load Diff

View File

@ -9,6 +9,9 @@
"dependsOn": ["^build"], "dependsOn": ["^build"],
"outputs": ["dist/**", "storybook-static/**"] "outputs": ["dist/**", "storybook-static/**"]
}, },
"connector#build": {
"dependsOn": ["^build"]
},
"dev": { "dev": {
"cache": false "cache": false
}, },