Selaa lähdekoodia

storage-node-v2: Add queries for query-node.

Shamil Gadelshin 3 vuotta sitten
vanhempi
commit
5a74d847ef

+ 1 - 0
storage-node-v2/.eslintignore

@@ -1,2 +1,3 @@
 /lib
 .eslintrc.js
+**/generated/*

+ 6 - 1
storage-node-v2/package.json

@@ -17,15 +17,17 @@
     "@types/base64url": "^2.0.0",
     "@types/express": "4.17.13",
     "@types/file-type": "^10.9.1",
+    "@types/lodash": "^4.14.171",
     "@types/multer": "^1.4.5",
     "@types/node-cache": "^4.2.5",
+    "@types/node-fetch": "^2.5.12",
     "@types/read-chunk": "^3.1.0",
     "@types/send": "^0.17.0",
+    "@types/url-join": "^4.0.1",
     "@types/winston": "^2.4.4",
     "await-lock": "^2.1.0",
     "base64url": "^3.0.1",
     "blake3": "^2.1.4",
-    "cross-fetch": "^3.1.4",
     "express": "4.17.1",
     "express-openapi-validator": "^4.12.4",
     "express-winston": "^4.1.0",
@@ -33,10 +35,13 @@
     "lodash": "^4.17.21",
     "multihashes": "^4.0.2",
     "node-cache": "^5.1.2",
+    "node-fetch": "^2.6.1",
     "openapi-editor": "^0.3.0",
     "read-chunk": "^3.2.0",
     "send": "^0.17.1",
+    "sleep-promise": "^9.1.0",
     "tslib": "^1",
+    "url-join": "^4.0.1",
     "winston": "^3.3.3"
   },
   "devDependencies": {

+ 12 - 7
storage-node-v2/src/commands/dev/sync.ts

@@ -1,5 +1,6 @@
 import { Command, flags } from '@oclif/command'
-import { QueryNodeApi } from '../../services/query-node/api'
+import { performSync } from '../../services/sync/synchronizer'
+import sleep from 'sleep-promise'
 
 export default class DevSync extends Command {
   static description = 'describe the command here'
@@ -13,16 +14,20 @@ export default class DevSync extends Command {
 
     console.log('Syncing...')
 
-    const queryNodeUrl = 'http://localhost:8081/graphql'
-    const api = new QueryNodeApi(queryNodeUrl)
-    const id = '0'
-
     try {
-      const bucket = await api.getStorageBucketDetails(id)
-      console.log(bucket)
+      await performSync()
     } catch (err) {
       console.log(err)
       console.log(JSON.stringify(err, null, 2))
     }
   }
 }
+
+export function runSyncWithInterval() {
+  setTimeout(async () => {
+    await sleep(5000)
+    console.log('Syncing with timeout...')
+    await performSync()
+    runSyncWithInterval()
+  }, 0)
+}

+ 0 - 30
storage-node-v2/src/services/query-node/generated/queries.ts

@@ -1,30 +0,0 @@
-import * as Types from './schema'
-
-import gql from 'graphql-tag'
-export type StorageBucketDetailsFragment = {
-  id: string
-  acceptingNewBags: boolean
-}
-
-export type GetStorageBucketDetailsQueryVariables = Types.Exact<{
-  id: Types.Scalars['ID']
-}>
-
-export type GetStorageBucketDetailsQuery = {
-  storageBucketByUniqueInput?: Types.Maybe<StorageBucketDetailsFragment>
-}
-
-export const StorageBucketDetails = gql`
-  fragment StorageBucketDetails on StorageBucket {
-    id
-    acceptingNewBags
-  }
-`
-export const GetStorageBucketDetails = gql`
-  query getStorageBucketDetails($id: ID!) {
-    storageBucketByUniqueInput(where: { id: $id }) {
-      ...StorageBucketDetails
-    }
-  }
-  ${StorageBucketDetails}
-`

+ 0 - 10
storage-node-v2/src/services/query-node/queries/queries.graphql

@@ -1,10 +0,0 @@
-fragment StorageBucketDetails on StorageBucket {
-  id
-  acceptingNewBags
-}
-
-query getStorageBucketDetails($id: ID!) {
-  storageBucketByUniqueInput(where: { id: $id }) {
-    ...StorageBucketDetails
-  }
-}

+ 47 - 3
storage-node-v2/src/services/query-node/api.ts → storage-node-v2/src/services/queryNode/api.ts

@@ -5,14 +5,29 @@ import {
   InMemoryCache,
   DocumentNode,
 } from '@apollo/client'
-import fetch from 'cross-fetch'
+import fetch from 'node-fetch'
 import {
   GetStorageBucketDetails,
   GetStorageBucketDetailsQuery,
   GetStorageBucketDetailsQueryVariables,
+  GetAllStorageBucketDetails,
+  GetAllStorageBucketDetailsQuery,
+  GetAllStorageBucketDetailsQueryVariables,
   StorageBucketDetailsFragment,
+  GetStorageBagDetailsQuery,
+  GetStorageBagDetails,
+  StorageBagDetailsFragment,
+  GetStorageBagDetailsQueryVariables,
+  DataObjectDetailsFragment,
+  GetDataObjectDetailsQuery,
+  GetDataObjectDetailsQueryVariables,
+  GetDataObjectDetails,
 } from './generated/queries'
-import { Maybe } from './generated/schema'
+import {
+  Maybe,
+  StorageBucketWhereInput,
+  StorageBagWhereInput,
+} from './generated/schema'
 
 export class QueryNodeApi {
   private apolloClient: ApolloClient<NormalizedCacheObject>
@@ -79,4 +94,33 @@ export class QueryNodeApi {
       GetStorageBucketDetailsQueryVariables
     >(GetStorageBucketDetails, { id: objectId }, 'storageBucketByUniqueInput')
   }
-}
+
+  public getAllStorageBucketDetails(): Promise<
+    Array<StorageBucketDetailsFragment>
+  > {
+    return this.multipleEntitiesQuery<
+      GetAllStorageBucketDetailsQuery,
+      GetAllStorageBucketDetailsQueryVariables
+    >(GetAllStorageBucketDetails, {}, 'storageBuckets')
+  }
+
+  public getStorageBagsDetails(
+    bucketIds: string[]
+  ): Promise<Array<StorageBagDetailsFragment>> {
+    const input: StorageBucketWhereInput = { id_in: bucketIds }
+    return this.multipleEntitiesQuery<
+      GetStorageBagDetailsQuery,
+      GetStorageBagDetailsQueryVariables
+    >(GetStorageBagDetails, { bucketIds: input }, 'storageBags')
+  }
+
+  public getDataObjectDetails(
+    bagIds: string[]
+  ): Promise<Array<DataObjectDetailsFragment>> {
+    const input: StorageBagWhereInput = { id_in: bagIds }
+    return this.multipleEntitiesQuery<
+      GetDataObjectDetailsQuery,
+      GetDataObjectDetailsQueryVariables
+    >(GetDataObjectDetails, { bagIds: input }, 'storageDataObjects')
+  }
+}

+ 0 - 0
storage-node-v2/src/services/query-node/codegen.yml → storage-node-v2/src/services/queryNode/codegen.yml


+ 113 - 0
storage-node-v2/src/services/queryNode/generated/queries.ts

@@ -0,0 +1,113 @@
+import * as Types from './schema'
+
+import gql from 'graphql-tag'
+export type StorageBucketDetailsFragment = {
+  id: string
+  operatorMetadata?: Types.Maybe<any>
+  operatorStatus: { workerId: number } | { workerId: number }
+}
+
+export type GetStorageBucketDetailsQueryVariables = Types.Exact<{
+  id: Types.Scalars['ID']
+}>
+
+export type GetStorageBucketDetailsQuery = {
+  storageBucketByUniqueInput?: Types.Maybe<StorageBucketDetailsFragment>
+}
+
+export type GetAllStorageBucketDetailsQueryVariables = Types.Exact<{
+  [key: string]: never
+}>
+
+export type GetAllStorageBucketDetailsQuery = {
+  storageBuckets: Array<StorageBucketDetailsFragment>
+}
+
+export type StorageBagDetailsFragment = {
+  id: string
+  storedBy: Array<{ id: string }>
+}
+
+export type GetStorageBagDetailsQueryVariables = Types.Exact<{
+  bucketIds?: Types.Maybe<Types.StorageBucketWhereInput>
+}>
+
+export type GetStorageBagDetailsQuery = {
+  storageBags: Array<StorageBagDetailsFragment>
+}
+
+export type DataObjectDetailsFragment = {
+  ipfsHash: string
+  storageBag: { id: string }
+}
+
+export type GetDataObjectDetailsQueryVariables = Types.Exact<{
+  bagIds?: Types.Maybe<Types.StorageBagWhereInput>
+}>
+
+export type GetDataObjectDetailsQuery = {
+  storageDataObjects: Array<DataObjectDetailsFragment>
+}
+
+export const StorageBucketDetails = gql`
+  fragment StorageBucketDetails on StorageBucket {
+    id
+    operatorMetadata
+    operatorStatus {
+      ... on StorageBucketOperatorStatusActive {
+        workerId
+      }
+      ... on StorageBucketOperatorStatusInvited {
+        workerId
+      }
+    }
+  }
+`
+export const StorageBagDetails = gql`
+  fragment StorageBagDetails on StorageBag {
+    id
+    storedBy {
+      id
+    }
+  }
+`
+export const DataObjectDetails = gql`
+  fragment DataObjectDetails on StorageDataObject {
+    ipfsHash
+    storageBag {
+      id
+    }
+  }
+`
+export const GetStorageBucketDetails = gql`
+  query getStorageBucketDetails($id: ID!) {
+    storageBucketByUniqueInput(where: { id: $id }) {
+      ...StorageBucketDetails
+    }
+  }
+  ${StorageBucketDetails}
+`
+export const GetAllStorageBucketDetails = gql`
+  query getAllStorageBucketDetails {
+    storageBuckets {
+      ...StorageBucketDetails
+    }
+  }
+  ${StorageBucketDetails}
+`
+export const GetStorageBagDetails = gql`
+  query getStorageBagDetails($bucketIds: StorageBucketWhereInput) {
+    storageBags(where: { storedBy_some: $bucketIds }) {
+      ...StorageBagDetails
+    }
+  }
+  ${StorageBagDetails}
+`
+export const GetDataObjectDetails = gql`
+  query getDataObjectDetails($bagIds: StorageBagWhereInput) {
+    storageDataObjects(where: { storageBag: $bagIds }) {
+      ...DataObjectDetails
+    }
+  }
+  ${DataObjectDetails}
+`

+ 0 - 0
storage-node-v2/src/services/query-node/generated/schema.ts → storage-node-v2/src/services/queryNode/generated/schema.ts


+ 50 - 0
storage-node-v2/src/services/queryNode/queries/queries.graphql

@@ -0,0 +1,50 @@
+fragment StorageBucketDetails on StorageBucket {
+  id
+  operatorMetadata
+  operatorStatus {
+    ... on StorageBucketOperatorStatusActive {
+      workerId
+    }
+    ... on StorageBucketOperatorStatusInvited {
+      workerId
+    }
+  }
+}
+
+query getStorageBucketDetails($id: ID!) {
+  storageBucketByUniqueInput(where: { id: $id }) {
+    ...StorageBucketDetails
+  }
+}
+
+query getAllStorageBucketDetails {
+  storageBuckets {
+    ...StorageBucketDetails
+  }
+}
+
+fragment StorageBagDetails on StorageBag {
+  id
+  storedBy {
+    id
+  }
+}
+
+query getStorageBagDetails($bucketIds: StorageBucketWhereInput) {
+  storageBags(where: { storedBy_some: $bucketIds }) {
+    ...StorageBagDetails
+  }
+}
+
+fragment DataObjectDetails on StorageDataObject {
+  ipfsHash
+  storageBag {
+    id
+  }
+}
+
+query getDataObjectDetails($bagIds: StorageBagWhereInput) {
+  storageDataObjects(where: { storageBag: $bagIds }) {
+    ...DataObjectDetails
+  }
+}

+ 61 - 0
storage-node-v2/src/services/sync/dataObjectsModel.ts

@@ -0,0 +1,61 @@
+import { QueryNodeApi } from '../../services/queryNode/api'
+import { u8aToString, hexToU8a } from '@polkadot/util'
+
+type Model = {
+  storageBuckets: StorageBucket[]
+  bags: Bag[]
+  dataObjects: DataObject[]
+}
+
+type StorageBucket = {
+  id: string
+  url: string
+  workerId: number
+}
+
+type Bag = {
+  id: string
+  buckets: string[]
+}
+
+type DataObject = {
+  cid: string
+  bagId: string
+}
+
+export async function getRuntimeModel(
+  queryNodeUrl: string,
+  workerId: number
+): Promise<Model> {
+  const api = new QueryNodeApi(queryNodeUrl)
+  // TODO: graphql response entries limit
+  // TODO: get accepted data objects only
+
+  let allBuckets = await api.getAllStorageBucketDetails()
+
+  let bucketIds = allBuckets
+    .filter((bucket) => bucket.operatorStatus?.workerId === workerId)
+    .map((bucket) => bucket.id)
+  let assignedBags = await api.getStorageBagsDetails(bucketIds)
+
+  let bagIds = assignedBags.map((bag) => bag.id)
+  let assignedDataObjects = await api.getDataObjectDetails(bagIds)
+
+  const model: Model = {
+    storageBuckets: allBuckets.map((bucket) => ({
+      id: bucket.id,
+      url: u8aToString(hexToU8a(bucket.operatorMetadata)), //TODO: catch error locally
+      workerId: bucket.operatorStatus?.workerId,
+    })),
+    bags: assignedBags.map((bag) => ({
+      id: bag.id,
+      buckets: bag.storedBy.map((bucketInBag) => bucketInBag.id),
+    })),
+    dataObjects: assignedDataObjects.map((dataObject) => ({
+      cid: dataObject.ipfsHash,
+      bagId: dataObject.storageBag.id,
+    })),
+  }
+
+  return model
+}

+ 69 - 0
storage-node-v2/src/services/sync/synchronizer.ts

@@ -0,0 +1,69 @@
+import { getRuntimeModel } from '../../services/sync/dataObjectsModel'
+import _ from 'lodash'
+import fs from 'fs'
+import path from 'path'
+import { pipeline } from 'stream'
+import { promisify } from 'util'
+import fetch from 'node-fetch'
+import urljoin from 'url-join'
+const fsPromises = fs.promises
+
+export async function performSync(): Promise<void> {
+  const queryNodeUrl = 'http://localhost:8081/graphql'
+  const workerId = 1
+  const uploadDirectory = '/Users/shamix/uploads'
+
+  // const model = await getRuntimeModel(queryNodeUrl, workerId)
+  // console.log(model)
+
+  // const files = await getLocalFileNames(uploadDirectory)
+  // console.log(files)
+
+  const [model, files] = await Promise.all([
+    getRuntimeModel(queryNodeUrl, workerId),
+    getLocalFileNames(uploadDirectory),
+  ])
+  console.log(model)
+  console.log(files)
+
+  const requiredCids = model.dataObjects.map((obj) => obj.cid)
+
+  const added = _.difference(requiredCids, files)
+  const deleted = _.difference(files, requiredCids)
+
+  console.log(`Added: ${added}`)
+  console.log(`Deleted: ${deleted}`)
+
+  const operatorUrl = 'http://localhost:3333/'
+
+  await Promise.all(
+    deleted.map((cid) => fsPromises.unlink(path.join(uploadDirectory, cid)))
+  )
+
+  await Promise.all(
+    added.map((cid) =>
+      download(
+        urljoin(operatorUrl, 'api/v1/files', cid),
+        path.join(uploadDirectory, cid)
+      )
+    )
+  )
+}
+
+async function getLocalFileNames(directory: string): Promise<string[]> {
+  return fsPromises.readdir(directory)
+}
+
+async function download(url: string, path: string): Promise<void> {
+  console.log('Downloading url:' + url)
+
+  const streamPipeline = promisify(pipeline)
+
+  const response = await fetch(url)
+
+  if (!response.ok)
+    throw new Error(`Unexpected response ${response.statusText}`)
+
+  // TODO: check for errors, both for response and filesystem
+  await streamPipeline(response.body, fs.createWriteStream(path))
+}

+ 48 - 100
yarn.lock

@@ -1849,13 +1849,6 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
-"@babel/runtime@^7.12.1":
-  version "7.14.8"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446"
-  integrity sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==
-  dependencies:
-    regenerator-runtime "^0.13.4"
-
 "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.9":
   version "7.13.10"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
@@ -4604,6 +4597,18 @@
     is-ipfs "^0.6.0"
     recursive-fs "^1.1.2"
 
+"@polkadot/api-contract@4.2.1":
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-4.2.1.tgz#8fbfd22e5369cceed9afd21bd3cd475b708b3783"
+  integrity sha512-ZHYIEox6pXrAVjZ99Te0kFlU5KcEblnq+dxGfvNisGKv5xieWFOBqJ/azXmm4LKMer/6uzglBv2IRh2QxE+MnA==
+  dependencies:
+    "@babel/runtime" "^7.13.10"
+    "@polkadot/api" "4.2.1"
+    "@polkadot/types" "4.2.1"
+    "@polkadot/util" "^6.0.5"
+    "@polkadot/x-rxjs" "^6.0.5"
+    bn.js "^4.11.9"
+
 "@polkadot/api-derive@4.2.1":
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-4.2.1.tgz#848a2a9ef947f08660af2571f72ca2b06969f2e3"
@@ -4769,13 +4774,6 @@
   dependencies:
     "@babel/runtime" "^7.13.9"
 
-"@polkadot/networks@^3.7.1":
-  version "3.7.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-3.7.1.tgz#01e568e0f7791c22eb896ffabc23e936ede57c43"
-  integrity sha512-kBPUxt3d1xXeJaFilyVI717TKOZJko/3pvFIDqbSc0i2qdXv8bmRR5r7KMnEB7MvTeMPKHVhcesWksAIdsYRew==
-  dependencies:
-    "@babel/runtime" "^7.12.1"
-
 "@polkadot/react-identicon@^0.57.3":
   version "0.57.3"
   resolved "https://registry.yarnpkg.com/@polkadot/react-identicon/-/react-identicon-0.57.3.tgz#f2f1a9b57faa66e1df47a0238daa9607f76d946c"
@@ -4914,7 +4912,7 @@
     "@babel/runtime" "^7.10.5"
     color "^3.1.2"
 
-"@polkadot/util-crypto@6.0.5", "@polkadot/util-crypto@^6.0.5":
+"@polkadot/util-crypto@6.0.5", "@polkadot/util-crypto@^3.0.1", "@polkadot/util-crypto@^6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-6.0.5.tgz#347ea2bf051d34087766cb43004062358cd43800"
   integrity sha512-NlzmZzJ1vq2bjnQUU0MUocaT9vuIBGTlB/XCrCw94MyYqX19EllkOKLVMgu6o89xhYeP5rmASRQvTx9ZL9EzRw==
@@ -4936,27 +4934,7 @@
     tweetnacl "^1.0.3"
     xxhashjs "^0.2.2"
 
-"@polkadot/util-crypto@^3.0.1":
-  version "3.7.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-3.7.1.tgz#69e1cca5adc521cf0880b244dc1ae0d086c42e4c"
-  integrity sha512-ZxQa10bo85YlxfS8ieDUzmFZMkKWwOp2dGQ0Xy94e4VBkWVPq9JjAfm8RnLy6D7k5KvMhzKuzJk7IcBDDdXGSw==
-  dependencies:
-    "@babel/runtime" "^7.12.1"
-    "@polkadot/networks" "^3.7.1"
-    "@polkadot/util" "^3.7.1"
-    "@polkadot/wasm-crypto" "^1.4.1"
-    base-x "^3.0.8"
-    blakejs "^1.1.0"
-    bn.js "^5.1.3"
-    create-hash "^1.2.0"
-    elliptic "^6.5.3"
-    js-sha3 "^0.8.0"
-    pbkdf2 "^3.1.1"
-    scryptsy "^2.1.0"
-    tweetnacl "^1.0.3"
-    xxhashjs "^0.2.2"
-
-"@polkadot/util@6.0.5", "@polkadot/util@^6.0.5":
+"@polkadot/util@6.0.5", "@polkadot/util@^3.0.1", "@polkadot/util@^6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-6.0.5.tgz#aa52995d3fe998eed218d26b243832a7a3e2944d"
   integrity sha512-0EnYdGAXx/Y2MLgCKtlfdKVcURV+Twx+M+auljTeMK8226pR7xMblYuVuO5bxhPWBa1W7+iQloEZ0VRQrIoMDw==
@@ -4969,19 +4947,6 @@
     camelcase "^5.3.1"
     ip-regex "^4.3.0"
 
-"@polkadot/util@^3.0.1", "@polkadot/util@^3.7.1":
-  version "3.7.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-3.7.1.tgz#b7585380a6177814f7e28dc2165814864ef2c67b"
-  integrity sha512-nvgzAbT/a213mpUd56YwK/zgbGKcQoMNLTmqcBHn1IP9u5J9XJcb1zPzqmCTg6mqnjrsgzJsWml9OpQftrcB6g==
-  dependencies:
-    "@babel/runtime" "^7.12.1"
-    "@polkadot/x-textdecoder" "^3.7.1"
-    "@polkadot/x-textencoder" "^3.7.1"
-    "@types/bn.js" "^4.11.6"
-    bn.js "^5.1.3"
-    camelcase "^5.3.1"
-    ip-regex "^4.2.0"
-
 "@polkadot/vanitygen@^0.18.1":
   version "0.18.1"
   resolved "https://registry.yarnpkg.com/@polkadot/vanitygen/-/vanitygen-0.18.1.tgz#44839473e3cd1490289cef57c05f0466a4e1db80"
@@ -5008,11 +4973,6 @@
   dependencies:
     "@babel/runtime" "^7.13.9"
 
-"@polkadot/wasm-crypto@^1.4.1":
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-1.4.1.tgz#0a053d0c2587da30fb5313cef81f8d9a52029c68"
-  integrity sha512-GPBCh8YvQmA5bobI4rqRkUhrEHkEWU1+lcJVPbZYsa7jiHFaZpzCLrGQfiqW/vtbU1aBS2wmJ0x1nlt33B9QqQ==
-
 "@polkadot/wasm-crypto@^4.0.2":
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.0.2.tgz#9649057adee8383cc86433d107ba526b718c5a3b"
@@ -5065,13 +5025,6 @@
     "@babel/runtime" "^7.13.9"
     "@polkadot/x-global" "6.0.5"
 
-"@polkadot/x-textdecoder@^3.7.1":
-  version "3.7.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-3.7.1.tgz#2d02bd33df0e5d4818b8d96892a5c8290e967573"
-  integrity sha512-GztrO7O880GR7C64PK30J7oLm+88OMxAUVW35njE+9qFUH6MGEKbtaLGUSn0JLCCtSme2f1i7DZ+1Pdbqowtnw==
-  dependencies:
-    "@babel/runtime" "^7.12.1"
-
 "@polkadot/x-textencoder@6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-6.0.5.tgz#fc851259de97a98f3417e51807c1f5ebe265fdf0"
@@ -5080,13 +5033,6 @@
     "@babel/runtime" "^7.13.9"
     "@polkadot/x-global" "6.0.5"
 
-"@polkadot/x-textencoder@^3.7.1":
-  version "3.7.1"
-  resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-3.7.1.tgz#1fe1884821f255565735b1b5dbb17ee61de51fa3"
-  integrity sha512-39jwEu+gok8hFl/UqBr6WDhSeSr4qblriwM++2Vwrw/298hd5uQ7xtJNZKdrbrPCkExPZhrxwVg/mJTHBpwSng==
-  dependencies:
-    "@babel/runtime" "^7.12.1"
-
 "@polkadot/x-ws@^6.0.5":
   version "6.0.5"
   resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-6.0.5.tgz#bafab6004d88d9273478332a3a040bfef3647619"
@@ -6384,6 +6330,11 @@
   resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.157.tgz#fdac1c52448861dfde1a2e1515dbc46e54926dc8"
   integrity sha512-Ft5BNFmv2pHDgxV5JDsndOWTRJ+56zte0ZpYLowp03tW+K+t8u8YMOzAnpuqPgzX6WO1XpDIUm7u04M8vdDiVQ==
 
+"@types/lodash@^4.14.171":
+  version "4.14.171"
+  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.171.tgz#f01b3a5fe3499e34b622c362a46a609fdb23573b"
+  integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
+
 "@types/long@^4.0.0":
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
@@ -6475,6 +6426,14 @@
     "@types/node" "*"
     form-data "^3.0.0"
 
+"@types/node-fetch@^2.5.12":
+  version "2.5.12"
+  resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66"
+  integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==
+  dependencies:
+    "@types/node" "*"
+    form-data "^3.0.0"
+
 "@types/node-fetch@^2.5.8":
   version "2.5.8"
   resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
@@ -6854,6 +6813,11 @@
   resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"
   integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==
 
+"@types/url-join@^4.0.1":
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/@types/url-join/-/url-join-4.0.1.tgz#4989c97f969464647a8586c7252d97b449cdc045"
+  integrity sha512-wDXw9LEEUHyV+7UWy7U315nrJGJ7p1BzaCxDpEoLr789Dk1WDVMMlf3iBfbG2F8NdWnYyFbtTxUn2ZNbm1Q4LQ==
+
 "@types/uuid@^3.4.4":
   version "3.4.9"
   resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.9.tgz#fcf01997bbc9f7c09ae5f91383af076d466594e1"
@@ -9498,21 +9462,11 @@ bluebird@^3.1.1, bluebird@^3.3.5, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
   integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
 
-bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0:
-  version "4.12.0"
-  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
-  integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
-
-bn.js@^5.1.2:
+bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.2.tgz#c9686902d3c9a27729f43ab10f9d79c2004da7b0"
   integrity sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==
 
-bn.js@^5.1.3, bn.js@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
-  integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
-
 body-parser@1.15.2:
   version "1.15.2"
   resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67"
@@ -11861,7 +11815,7 @@ cross-fetch@3.0.6, cross-fetch@^3.0.4:
   dependencies:
     node-fetch "2.6.1"
 
-cross-fetch@3.1.4, cross-fetch@^3.1.4:
+cross-fetch@3.1.4:
   version "3.1.4"
   resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
   integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ==
@@ -13442,7 +13396,7 @@ elliptic@^6.5.2:
     minimalistic-assert "^1.0.0"
     minimalistic-crypto-utils "^1.0.0"
 
-elliptic@^6.5.3, 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==
@@ -17862,7 +17816,7 @@ ip-regex@^4.0.0:
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.1.0.tgz#5ad62f685a14edb421abebc2fff8db94df67b455"
   integrity sha512-pKnZpbgCTfH/1NLIlOduP/V+WRXzC2MOz3Qo8xmxk8C5GudJLgK5QyLVXOSWy3ParAH7Eemurl3xjv/WXYFvMA==
 
-ip-regex@^4.2.0, ip-regex@^4.3.0:
+ip-regex@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5"
   integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==
@@ -24878,17 +24832,6 @@ pbkdf2@^3.0.3:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-pbkdf2@^3.1.1:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
-  integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
-  dependencies:
-    create-hash "^1.1.2"
-    create-hmac "^1.1.4"
-    ripemd160 "^2.0.1"
-    safe-buffer "^5.0.1"
-    sha.js "^2.4.8"
-
 peek-readable@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-3.1.3.tgz#932480d46cf6aa553c46c68566c4fb69a82cd2b1"
@@ -27988,7 +27931,7 @@ rxjs-compat@^6.6.0:
   resolved "https://registry.yarnpkg.com/rxjs-compat/-/rxjs-compat-6.6.2.tgz#23592564243cf24641a5d2e2d2acfc8f6b127186"
   integrity sha512-C3V7axnAkPd91sbW1XreL8ydLM+phUcKViM76GBuT3hCzHMSQbszE/h6ajkgcrDn9j4JZ/OdzklvfAJ9MmXRcg==
 
-rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.6:
+rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.2, rxjs@^6.5.3, rxjs@^6.6.0, rxjs@^6.6.2, rxjs@^6.6.6:
   version "6.6.7"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"
   integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==
@@ -28638,6 +28581,11 @@ slash@^3.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
+sleep-promise@^9.1.0:
+  version "9.1.0"
+  resolved "https://registry.yarnpkg.com/sleep-promise/-/sleep-promise-9.1.0.tgz#101ebe65700bcd184709da95d960967b02b79d03"
+  integrity sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==
+
 slice-ansi@0.0.4:
   version "0.0.4"
   resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
@@ -30977,12 +30925,7 @@ typescript-formatter@^7.2.2:
     commandpost "^1.0.0"
     editorconfig "^0.15.0"
 
-typescript@2.2.2:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.2.tgz#606022508479b55ffa368b58fee963a03dfd7b0c"
-  integrity sha1-YGAiUIR5tV/6NotY/uljoD39eww=
-
-typescript@^3.0.3, typescript@^3.3, typescript@^3.7.2, typescript@^3.7.5, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.6, typescript@^3.9.7:
+typescript@2.2.2, typescript@^3.0.3, typescript@^3.3, typescript@^3.7.2, typescript@^3.7.5, typescript@^3.8.3, typescript@^3.9.5, typescript@^3.9.6, typescript@^3.9.7:
   version "3.9.7"
   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa"
   integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==
@@ -31420,6 +31363,11 @@ urix@^0.1.0:
   resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
   integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
 
+url-join@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
+  integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
+
 url-loader@^1.0.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8"