diff --git a/.editorconfig b/.editorconfig new file mode 100755 index 0000000..cb8b48c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 88ea6ce..ddc53d8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -25,6 +25,7 @@ "no-shadow": "off", "import/no-duplicates": "error", "import/no-self-import": "error", + "import/no-cycle": "error", "import/order": [ "error", { @@ -36,12 +37,11 @@ "newlines-between": "always" } ], - "@typescript-eslint/strict-boolean-expressions": "error", + "@typescript-eslint/strict-boolean-expressions": "warn", "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/no-unnecessary-type-constraint": "off", - "@typescript-eslint/ban-ts-comment": "off", - "quotes": ["error", "single"] + "@typescript-eslint/ban-ts-comment": "off" }, "globals": { "localStorage": true diff --git a/package.json b/package.json index eb51313..f446d1d 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,11 @@ "author": "Inokentii Mazhara ", "license": "MIT", "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@stablelib/random": "^1.0.2", "@taquito/rpc": "14.0.0", "@taquito/taquito": "14.0.0", "@taquito/tzip12": "14.0.0", @@ -37,6 +42,7 @@ "ts": "tsc --pretty", "lint": "eslint ./src --ext .js,.ts", "lint:fix": "eslint ./src --ext .js,.ts --fix", + "lint:rules": "eslint --print-config", "clean": "rimraf dist/", "db-migration": "cd migrations/notifications && npx ts-node index.ts" }, diff --git a/src/index.ts b/src/index.ts index 6046687..8d86be7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,6 +12,7 @@ import swaggerUi from 'swagger-ui-express'; import { getAdvertisingInfo } from './advertising/advertising'; import { MIN_ANDROID_APP_VERSION, MIN_IOS_APP_VERSION } from './config'; import getDAppsStats from './getDAppsStats'; +import { getMagicSquareQuestParticipants, startMagicSquareQuest } from './magic-square'; import { basicAuth } from './middlewares/basic-auth.middleware'; import { Notification, PlatformType } from './notifications/notification.interface'; import { getImageFallback } from './notifications/utils/get-image-fallback.util'; @@ -28,10 +29,12 @@ import { getAliceBobEstimationPayload } from './utils/alice-bob/get-alice-bob-es import { getAliceBobOrderInfo } from './utils/alice-bob/get-alice-bob-order-info'; import { getAliceBobPairInfo } from './utils/alice-bob/get-alice-bob-pair-info'; import { getAliceBobPairsInfo } from './utils/alice-bob/get-alice-bob-pairs-info'; +import { CodedError } from './utils/errors'; import { coinGeckoTokens } from './utils/gecko-tokens'; import { getExternalApiErrorPayload, isDefined, isNonEmptyString } from './utils/helpers'; import logger from './utils/logger'; import { getSignedMoonPayUrl } from './utils/moonpay/get-signed-moonpay-url'; +import { getSigningNonce, SIGNING_NONCE_TTL } from './utils/signing-nonce'; import SingleQueryDataProvider from './utils/SingleQueryDataProvider'; import { tezExchangeRateProvider } from './utils/tezos'; import { getExchangeRatesFromDB } from './utils/tokens'; @@ -327,6 +330,55 @@ app.get('/api/advertising-info', (_req, res) => { app.use('/api/slise-ad-rules', sliseRulesRouter); +app.post('/api/magic-square-quest/start', async (req, res) => { + try { + await startMagicSquareQuest(req.body); + + res.status(200).send({ message: 'Quest successfully started' }); + } catch (error: any) { + console.error(error); + + if (error instanceof CodedError) { + res.status(error.code).send(error.buildResponse()); + } else { + res.status(500).send({ message: error?.message }); + } + } +}); + +app.get('/api/magic-square-quest/participants', basicAuth, async (req, res) => { + try { + res.status(200).send(await getMagicSquareQuestParticipants()); + } catch (error: any) { + console.error(error); + + if (error instanceof CodedError) { + res.status(error.code).send(error.buildResponse()); + } else { + res.status(500).send({ message: error?.message }); + } + } +}); + +app.get('/api/auth-nonce', async (req, res) => { + try { + const pkh = req.query.pkh; + if (!pkh || typeof pkh !== 'string') throw new Error('PKH is not a string'); + + const nonce = getSigningNonce(pkh); + + res.status(200).send({ nonce, ttl: SIGNING_NONCE_TTL }); + } catch (error: any) { + console.error(error); + + if (error instanceof CodedError) { + res.status(error.code).send(error.buildResponse()); + } else { + res.status(500).send({ message: error?.message }); + } + } +}); + const swaggerOptions = { swaggerDefinition: { openapi: '3.0.0', diff --git a/src/magic-square.ts b/src/magic-square.ts new file mode 100644 index 0000000..0d880ee --- /dev/null +++ b/src/magic-square.ts @@ -0,0 +1,82 @@ +import * as ethersAddressUtils from '@ethersproject/address'; +import * as ethersHashUtils from '@ethersproject/hash'; +import * as ethersStringsUtils from '@ethersproject/strings'; +import * as ethersTxUtils from '@ethersproject/transactions'; +import { STATUS_CODE } from '@taquito/http-utils'; +import { validateAddress, ValidationResult, verifySignature, getPkhfromPk } from '@taquito/utils'; + +import { redisClient } from './redis'; +import { CodedError } from './utils/errors'; +import { safeCheck } from './utils/helpers'; +import { getSigningNonce } from './utils/signing-nonce'; + +const REDIS_DB_KEY = 'magic_square_quest'; + +export function getMagicSquareQuestParticipants() { + return redisClient.lrange(REDIS_DB_KEY, 0, -1).then(records => records.map(r => JSON.parse(r))); +} + +interface StartQuestPayload { + pkh: string; + publicKey: string; + messageBytes: string; + signature: string; + evm: { + pkh: string; + messageBytes: string; + signature: string; + }; +} + +export async function startMagicSquareQuest({ pkh, publicKey, messageBytes, signature, evm }: StartQuestPayload) { + // Public Key Hashes + + if (!safeCheck(() => validateAddress(pkh) === ValidationResult.VALID && getPkhfromPk(publicKey) === pkh)) + throw new CodedError(STATUS_CODE.BAD_REQUEST, 'Invalid Tezos public key (hash)'); + + let evmPkh: string; + try { + // Corrects lower-cased addresses. Throws if invalid. + evmPkh = ethersAddressUtils.getAddress(evm.pkh); + } catch (err) { + console.error(err); + throw new CodedError(STATUS_CODE.BAD_REQUEST, 'Invalid EVM public key hash'); + } + + // Signatures + + if (!safeCheck(() => verifySignature(messageBytes, publicKey, signature))) + throw new CodedError(STATUS_CODE.UNAUTHORIZED, 'Invalid Tezos signature or message'); + + if ( + !safeCheck(() => { + const messageBytes = ethersStringsUtils.toUtf8String(evm.messageBytes); + const messageHash = ethersHashUtils.hashMessage(messageBytes); + + return ethersTxUtils.recoverAddress(messageHash, evm.signature) === evmPkh; + }) + ) + throw new CodedError(STATUS_CODE.UNAUTHORIZED, 'Invalid EVM signature or message'); + + // Presence check + + const exists = await redisClient + .lrange(REDIS_DB_KEY, 0, -1) + .then(items => items.some(item => item.includes(pkh) && item.includes(evmPkh))); + + if (exists) + throw new CodedError(STATUS_CODE.CONFLICT, 'Quest already started for the given credentials', 'QUEST_STARTED'); + + // Auth nonce + getSigningNonce.delete(pkh); + + // Registering + + const item = { + pkh, + evmPkh, + ts: new Date().toISOString() + }; + + await redisClient.lpush(REDIS_DB_KEY, JSON.stringify(item)); +} diff --git a/src/utils/errors.ts b/src/utils/errors.ts new file mode 100644 index 0000000..0181cca --- /dev/null +++ b/src/utils/errors.ts @@ -0,0 +1,17 @@ +interface CodedErrorForResponse { + message: string; + code?: string; +} + +export class CodedError extends Error { + constructor(public code: number, message: string, public errorCode?: string) { + super(message); + } + + buildResponse() { + const res: CodedErrorForResponse = { message: this.message }; + if (this.errorCode) res.code = this.errorCode; + + return res; + } +} diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 2335fba..6e2f2e7 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -45,3 +45,13 @@ export const getExternalApiErrorPayload = (error: unknown) => { return { status, data }; }; + +export function safeCheck(check: () => boolean, def = false) { + try { + return check(); + } catch (error) { + console.error(); + + return def; + } +} diff --git a/src/utils/signing-nonce.ts b/src/utils/signing-nonce.ts new file mode 100644 index 0000000..edeb0bf --- /dev/null +++ b/src/utils/signing-nonce.ts @@ -0,0 +1,23 @@ +import { randomStringForEntropy } from '@stablelib/random'; +import { validateAddress, ValidationResult } from '@taquito/utils'; +import memoizee from 'memoizee'; + +import { CodedError } from './errors'; + +export const SIGNING_NONCE_TTL = 5 * 60_000; + +const MEMOIZE_OPTIONS = { + max: 500, + maxAge: SIGNING_NONCE_TTL +}; + +export const getSigningNonce = memoizee((pkh: string) => { + if (validateAddress(pkh) !== ValidationResult.VALID) throw new CodedError(400, 'Invalid address'); + + return buildNonce(); +}, MEMOIZE_OPTIONS); + +function buildNonce() { + // The way it is done in SIWE.generateNonce() + return randomStringForEntropy(96); +} diff --git a/yarn.lock b/yarn.lock index 12090f5..87912e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -77,6 +77,168 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@firebase/app-types@0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.7.0.tgz#c9e16d1b8bed1a991840b8d2a725fb58d0b5899f" @@ -392,10 +554,10 @@ resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== -"@stablelib/random@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.1.tgz#4357a00cb1249d484a9a71e6054bc7b8324a7009" - integrity sha512-zOh+JHX3XG9MSfIB0LZl/YwPP9w3o6WBiJkZvjPoKKu5LKFW4OLV71vMxWp9qG5T43NaWyn0QQTWgqCdO+yOBQ== +"@stablelib/random@^1.0.1", "@stablelib/random@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" + integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== dependencies: "@stablelib/binary" "^1.0.1" "@stablelib/wipe" "^1.0.1" @@ -1078,6 +1240,11 @@ bn.js@^4.11.9: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -1556,7 +1723,7 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -elliptic@^6.5.4: +elliptic@6.5.4, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -2460,7 +2627,7 @@ hash-stream-validation@^0.2.2: resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== -hash.js@^1.0.0, hash.js@^1.0.3: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -2805,6 +2972,11 @@ joycon@^2.2.5: resolved "https://registry.yarnpkg.com/joycon/-/joycon-2.2.5.tgz#8d4cf4cbb2544d7b7583c216fcdfec19f6be1615" integrity sha512-YqvUxoOcVPnCp0VU1/56f+iKSdvIRJYPznH22BdXV3xMk75SFXhWeJkZ8C9XxUWt1b5x2X1SxuFygW1U0FmkEQ== +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"