Browse Source

query node - content nft mappings

ondratra 3 years ago
parent
commit
d9cb735611

+ 1 - 3
query-node/mappings/common.ts

@@ -1,10 +1,9 @@
 import { DatabaseManager, SubstrateEvent, SubstrateExtrinsic, ExtrinsicArg } from '@joystream/hydra-common'
 import { Bytes } from '@polkadot/types'
-import { Network } from 'query-node/dist/model'
+import { Event, Network } from 'query-node/dist/model'
 import { BaseModel } from '@joystream/warthog'
 import { metaToObject } from '@joystream/metadata-protobuf/utils'
 import { AnyMetadataClass, DecodedMetadataObject } from '@joystream/metadata-protobuf/types'
-import { Event } from 'query-node/dist/model'
 
 export const CURRENT_NETWORK = Network.GIZA
 /*
@@ -31,7 +30,6 @@ class Logger {
 
 export const logger = new Logger()
 
-
 export function genericEventFields(substrateEvent: SubstrateEvent): Partial<BaseModel & Event> {
   const { blockNumber, indexInBlock, extrinsic, blockTimestamp } = substrateEvent
   const eventTime = new Date(blockTimestamp)

+ 2 - 2
query-node/mappings/content/curatorGroup.ts

@@ -28,7 +28,7 @@ async function createCurator(store: DatabaseManager, curatorId: string): Promise
 }
 
 async function ensureCurator(store: DatabaseManager, curatorId: string): Promise<Curator> {
-  return await getCurator(store, curatorId) || await createCurator(store, curatorId)
+  return (await getCurator(store, curatorId)) || (await createCurator(store, curatorId))
 }
 
 export async function content_CuratorGroupCreated({ store, event }: EventContext & StoreContext): Promise<void> {
@@ -132,7 +132,7 @@ export async function content_CuratorRemoved({ store, event }: EventContext & St
     return inconsistentState('Non-existing curator removal from curator group requested', curatorGroupId)
   }
 
-  const curatorIndex = curatorGroup.curators.findIndex(item => item.id.toString() == curator.toString())
+  const curatorIndex = curatorGroup.curators.findIndex((item) => item.id.toString() === curator.toString())
 
   // ensure curator group exists
   if (curatorIndex < 0) {

+ 299 - 14
query-node/mappings/content/nft.ts

@@ -1,6 +1,21 @@
+// TODO: solve events' relations to videos and other entites that can be changed or deleted
+// TODO: solve transactionalStatus for OwnedNFT + how to set it up for first time auctioned NFT?
+// TODO: walkthrough bidding events once again and ensure all data are saved properly
+
 import { EventContext, StoreContext, DatabaseManager } from '@joystream/hydra-common'
-import { genericEventFields } from '../common'
+import { genericEventFields, inconsistentState } from '../common'
 import {
+  // entities
+  Auction,
+  AuctionType,
+  AuctionTypeEnglish,
+  AuctionTypeOpen,
+  Bid,
+  Membership,
+  OwnedNft,
+  Video,
+
+  // events
   AuctionStartedEvent,
   NftIssuedEvent,
   AuctionBidMadeEvent,
@@ -16,7 +31,91 @@ import {
   NftBoughtEvent,
   BuyNowCanceledEvent,
 } from 'query-node/dist/model'
+import * as joystreamTypes from '@joystream/types/augment/all/types'
 import { Content } from '../generated/types'
+import BN from 'bn.js'
+
+// definition of generic type for Hydra DatabaseManager's methods
+type EntityType<T> = {
+  new (...args: any[]): T
+}
+
+async function getExistingEntity<Type extends Video | Membership>(
+  store: DatabaseManager,
+  entityType: EntityType<Type>,
+  id: string,
+  relations: string[] = []
+): Promise<Type | undefined> {
+  // load entity
+  const entity = await store.get(entityType, { where: { id } })
+
+  return entity
+}
+
+async function getRequiredExistingEntity<Type extends Video | Membership>(
+  store: DatabaseManager,
+  entityType: EntityType<Type>,
+  id: string,
+  errorMessage: string,
+  relations: string[] = []
+): Promise<Type> {
+  const entity = await getExistingEntity(store, entityType, id, relations)
+
+  // ensure video exists
+  if (!entity) {
+    return inconsistentState(errorMessage, id)
+  }
+
+  return entity
+}
+
+async function getAuctionFromVideo(
+  store: DatabaseManager,
+  videoId: string,
+  errorMessageForVideo: string,
+  errorMessageForAuction: string
+): Promise<{ video: Video; auction: Auction }> {
+  // load video
+  const video = await getRequiredExistingEntity(store, Video, videoId.toString(), errorMessageForVideo, ['auction'])
+
+  // get auction
+  const auction = video.auction
+
+  // ensure auction exists
+  if (!auction) {
+    return inconsistentState(errorMessageForAuction, videoId)
+  }
+
+  return {
+    video,
+    auction,
+  }
+}
+
+async function getRequiredExistingEntites<Type extends Video | Membership>(
+  store: DatabaseManager,
+  entityType: EntityType<Type>,
+  ids: string[],
+  errorMessage: string
+): Promise<Type[]> {
+  // load entities
+  const entities = await store.getMany(entityType, { where: { id: ids } })
+
+  // assess loaded entity ids
+  const loadedEntityIds = entities.map((item) => item.id.toString())
+
+  // ensure all entities exists
+  if (loadedEntityIds.length !== ids.length) {
+    const missingIds = ids.filter((item) => !loadedEntityIds.includes(item))
+
+    return inconsistentState(errorMessage, missingIds)
+  }
+
+  // ensure entities are ordered as requested
+  entities.sort((a, b) => ids.indexOf(a.id.toString()) - ids.indexOf(b.id.toString()))
+
+  return entities
+}
 
 export async function contentNft_AuctionStarted({ event, store }: EventContext & StoreContext): Promise<void> {
   // common event processing
@@ -31,12 +130,73 @@ export async function contentNft_AuctionStarted({ event, store }: EventContext &
 
   // specific event processing
 
+  // load video
+  const video = await getRequiredExistingEntity(
+    store,
+    Video,
+    videoId.toString(),
+    `Non-existing video's auction started`
+  )
+
+  // load member
+  const member = await getRequiredExistingEntity(
+    store,
+    Membership,
+    ownerId.toString(),
+    'Non-existing member started video auction'
+  )
+
+  const whitelistedMembers = await getRequiredExistingEntites(
+    store,
+    Membership,
+    Array.from(auctionParams.whitelist.values()).map((item) => item.toString()),
+    'Non-existing members whitelisted'
+  )
+
+  // prepare auction record
+  const auction = new Auction({
+    video,
+    initialOwner: member,
+    startingPrice: auctionParams.starting_price,
+    buyNowPrice: new BN(auctionParams.buy_now_price.toString()),
+    auctionType: createAuctionType(auctionParams.auction_type),
+    minimalBidStep: auctionParams.minimal_bid_step,
+    startsAtBlock: auctionParams.starts_at.isSome ? auctionParams.starts_at.unwrap().toNumber() : event.blockNumber,
+    isCanceled: false,
+    isCompleted: false,
+    whitelistedMembers,
+  })
+
+  // save auction
+  await store.save<Auction>(auction)
+}
+
+// create auction type variant from raw runtime auction type
+function createAuctionType(rawAuctionType: joystreamTypes.AuctionType): typeof AuctionType {
+  // auction type `english`
+  if (rawAuctionType.isEnglish) {
+    const rawType = rawAuctionType.asEnglish
+
+    // prepare auction variant
+    const auctionType = new AuctionTypeEnglish()
+    auctionType.duration = rawType.auction_duration.toNumber()
+    auctionType.extensionPeriod = rawType.extension_period.toNumber()
+    return auctionType
+  }
+
+  // auction type `open`
+  const rawType = rawAuctionType.asOpen
+
+  // prepare auction variant
+  const auctionType = new AuctionTypeOpen()
+  auctionType.bidLockingTime = rawType.bid_lock_duration.toNumber()
+  return auctionType
 }
 
 export async function contentNft_NftIssued({ event, store }: EventContext & StoreContext): Promise<void> {
   // common event processing
 
-  const [actor, videoId, royalty, metadata, newOwner] = new Content.NftIssuedEvent(event).params
+  const [actor, videoId, royalty, metadata, mbNewOwnerId] = new Content.NftIssuedEvent(event).params
 
   const announcingPeriodStartedEvent = new NftIssuedEvent({
     ...genericEventFields(event),
@@ -46,12 +206,30 @@ export async function contentNft_NftIssued({ event, store }: EventContext & Stor
 
   // specific event processing
 
+  // load video
+  const video = await getRequiredExistingEntity(store, Video, videoId.toString(), 'Non-existing video auction started')
+
+  // load owner
+  const newOwner = await getExistingEntity(store, Membership, mbNewOwnerId.toString())
+
+  const creatorRoyalty = royalty.isSome ? royalty.unwrap().toNumber() : undefined
+
+  // prepare nft record
+  const ownedNft = new OwnedNft({
+    video,
+    ownerMember: newOwner,
+    creatorRoyalty,
+    metadata: metadata.toString(),
+  })
+
+  // save nft
+  await store.save<OwnedNft>(ownedNft)
 }
 
 export async function contentNft_AuctionBidMade({ event, store }: EventContext & StoreContext): Promise<void> {
   // common event processing
 
-  const [member, video, bidAmount, extendsAuction] = new Content.AuctionBidMadeEvent(event).params
+  const [memberId, videoId, bidAmount, extendsAuction] = new Content.AuctionBidMadeEvent(event).params
 
   const announcingPeriodStartedEvent = new AuctionBidMadeEvent({
     ...genericEventFields(event),
@@ -61,12 +239,45 @@ export async function contentNft_AuctionBidMade({ event, store }: EventContext &
 
   // specific event processing
 
+  // load member
+  const member = await getRequiredExistingEntity(
+    store,
+    Membership,
+    memberId.toString(),
+    'Non-existing member bid in auction'
+  )
+
+  // load video and auction
+  const { video, auction } = await getAuctionFromVideo(
+    store,
+    videoId.toString(),
+    'Non-existing video got bid',
+    'Non-existing auction got bid canceled'
+  )
+
+  // prepare bid record
+  const bid = new Bid({
+    auction,
+    bidder: member,
+    amount: new BN(bidAmount.toString()),
+    createdAt: new Date(event.blockTimestamp),
+    createdInBlock: event.blockNumber,
+    isCanceled: false,
+  })
+
+  // save bid
+  await store.save<Bid>(bid)
+
+  // update last bid in auction
+  auction.lastBid = bid
+
+  await store.save<Auction>(auction)
 }
 
 export async function contentNft_AuctionBidCanceled({ event, store }: EventContext & StoreContext): Promise<void> {
   // common event processing
 
-  const [member, video] = new Content.AuctionBidCanceledEvent(event).params
+  const [memberId, videoId] = new Content.AuctionBidCanceledEvent(event).params
 
   const announcingPeriodStartedEvent = new AuctionBidCanceledEvent({
     ...genericEventFields(event),
@@ -76,12 +287,31 @@ export async function contentNft_AuctionBidCanceled({ event, store }: EventConte
 
   // specific event processing
 
+  // load video and auction
+  const { video, auction } = await getAuctionFromVideo(
+    store,
+    videoId.toString(),
+    'Non-existing video got bid canceled',
+    'Non-existing auction got bid canceled'
+  )
+
+  // ensure bid exists
+  if (!auction.lastBid) {
+    return inconsistentState('Non-existing bid got canceled', auction.id.toString())
+  }
+
+  // TOOD: should bid placed before `lastBid` be loaded and replaced here?
+  // update auction's last bid
+  auction.lastBid = undefined
+
+  // save auction
+  await store.save<Auction>(auction)
 }
 
 export async function contentNft_AuctionCanceled({ event, store }: EventContext & StoreContext): Promise<void> {
   // common event processing
 
-  const [contentActor, video] = new Content.AuctionCanceledEvent(event).params
+  const [contentActor, videoId] = new Content.AuctionCanceledEvent(event).params
 
   const announcingPeriodStartedEvent = new AuctionCanceledEvent({
     ...genericEventFields(event),
@@ -91,12 +321,25 @@ export async function contentNft_AuctionCanceled({ event, store }: EventContext
 
   // specific event processing
 
+  // load video and auction
+  const { video, auction } = await getAuctionFromVideo(
+    store,
+    videoId.toString(),
+    'Non-existing video got bid canceled',
+    'Non-existing auction got bid canceled'
+  )
+
+  // mark auction as canceled
+  auction.isCanceled = true
+
+  // save auction
+  await store.save<Auction>(auction)
 }
 
 export async function contentNft_EnglishAuctionCompleted({ event, store }: EventContext & StoreContext): Promise<void> {
   // common event processing
 
-  const [member, video] = new Content.EnglishAuctionCompletedEvent(event).params
+  const [memberId, videoId] = new Content.EnglishAuctionCompletedEvent(event).params
 
   const announcingPeriodStartedEvent = new EnglishAuctionCompletedEvent({
     ...genericEventFields(event),
@@ -106,12 +349,37 @@ export async function contentNft_EnglishAuctionCompleted({ event, store }: Event
 
   // specific event processing
 
+  // load member
+  const member = await getRequiredExistingEntity(
+    store,
+    Membership,
+    memberId.toString(),
+    'Non-existing english-type auction was completed'
+  )
+
+  // load video and auction
+  const { video, auction } = await getAuctionFromVideo(
+    store,
+    videoId.toString(),
+    `Non-existing video's english-type auction was completed`,
+    'Non-existing english-type auction was completed'
+  )
+
+  // update auction
+  auction.isCompleted = true
+  auction.winningMember = member
+
+  // save auction
+  await store.save<Auction>(auction)
 }
 
-export async function contentNft_BidMadeCompletingAuction({ event, store }: EventContext & StoreContext): Promise<void> {
+export async function contentNft_BidMadeCompletingAuction({
+  event,
+  store,
+}: EventContext & StoreContext): Promise<void> {
   // common event processing
 
-  const [member, video] = new Content.BidMadeCompletingAuctionEvent(event).params
+  const [memberId, videoId] = new Content.BidMadeCompletingAuctionEvent(event).params
 
   const announcingPeriodStartedEvent = new BidMadeCompletingAuctionEvent({
     ...genericEventFields(event),
@@ -121,6 +389,28 @@ export async function contentNft_BidMadeCompletingAuction({ event, store }: Even
 
   // specific event processing
 
+  // load member
+  const member = await getRequiredExistingEntity(
+    store,
+    Membership,
+    memberId.toString(),
+    'Non-existing auction was completed by buy-now bid'
+  )
+
+  // load video and auction
+  const { video, auction } = await getAuctionFromVideo(
+    store,
+    videoId.toString(),
+    `Non-existing video's auction was completed by buy-now bid`,
+    `Non-existing auction was completed by buy-now bid`
+  )
+
+  // update auction
+  auction.isCompleted = true
+  auction.winningMember = member
+
+  // save auction
+  await store.save<Auction>(auction)
 }
 
 export async function contentNft_OpenAuctionBidAccepted({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -136,6 +426,7 @@ export async function contentNft_OpenAuctionBidAccepted({ event, store }: EventC
 
   // specific event processing
 
+  // TODO: what exactly should happen here?
 }
 
 export async function contentNft_OfferStarted({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -150,7 +441,6 @@ export async function contentNft_OfferStarted({ event, store }: EventContext & S
   await store.save<OfferStartedEvent>(announcingPeriodStartedEvent)
 
   // specific event processing
-
 }
 
 export async function contentNft_OfferAccepted({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -165,7 +455,6 @@ export async function contentNft_OfferAccepted({ event, store }: EventContext &
   await store.save<OfferAcceptedEvent>(announcingPeriodStartedEvent)
 
   // specific event processing
-
 }
 
 export async function contentNft_OfferCanceled({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -180,7 +469,6 @@ export async function contentNft_OfferCanceled({ event, store }: EventContext &
   await store.save<OfferCanceledEvent>(announcingPeriodStartedEvent)
 
   // specific event processing
-
 }
 
 export async function contentNft_NftSellOrderMade({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -195,7 +483,6 @@ export async function contentNft_NftSellOrderMade({ event, store }: EventContext
   await store.save<NftSellOrderMadeEvent>(announcingPeriodStartedEvent)
 
   // specific event processing
-
 }
 
 export async function contentNft_NftBought({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -210,7 +497,6 @@ export async function contentNft_NftBought({ event, store }: EventContext & Stor
   await store.save<NftBoughtEvent>(announcingPeriodStartedEvent)
 
   // specific event processing
-
 }
 
 export async function contentNft_BuyNowCanceled({ event, store }: EventContext & StoreContext): Promise<void> {
@@ -225,5 +511,4 @@ export async function contentNft_BuyNowCanceled({ event, store }: EventContext &
   await store.save<BuyNowCanceledEvent>(announcingPeriodStartedEvent)
 
   // specific event processing
-
 }

+ 6 - 5
query-node/package.json

@@ -1,7 +1,7 @@
 {
   "name": "query-node-root",
   "version": "0.1.0",
-  "description": "GraphQL server and mappings. Generated with \u2665 by Hydra-CLI",
+  "description": "GraphQL server and mappings. Generated with  by Hydra-CLI",
   "scripts": {
     "build": "./build.sh",
     "rebuild": "yarn db:drop && yarn clean:query-node && yarn codegen:query-node && yarn db:prepare && yarn db:migrate",
@@ -36,12 +36,13 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
-    "tslib": "^2.0.0",
+    "@joystream/hydra-processor": "3.1.0-alpha.13",
+    "@polkadot/metadata": "^4.17.1",
     "@types/bn.js": "^4.11.6",
     "bn.js": "^5.1.2",
-    "@joystream/hydra-processor": "3.1.0-alpha.13"
+    "tslib": "^2.0.0"
   },
   "volta": {
-		"extends": "../package.json"
-	}
+    "extends": "../package.json"
+  }
 }

+ 2 - 0
query-node/schemas/content.graphql

@@ -140,6 +140,8 @@ type Video @entity {
 
   "Is video featured or not"
   isFeatured: Boolean!
+
+  auction: Auction @derivedFrom(field: "video")
 }
 
 type VideoMediaMetadata @entity {

+ 33 - 7
query-node/schemas/contentNft.graphql

@@ -56,17 +56,23 @@ type Curator @entity {
 
 "Represents NFT details"
 type OwnedNft @entity { # NFT in name can't be UPPERCASE because it causes codegen errors
+  "NFT's video"
+  video: Video!
+
   "Member owning the NFT (if any)"
   ownerMember: Membership
 
   "Curator group owning the NFT (if any)"
   ownerCuratorGroup: CuratorGroup
 
+  "NFT's metadata"
+  metadata: String!
+
   "NFT transactional status"
   transactionalStatus: TransactionalStatus!
 
   "Creator royalty"
-  creatorRoyalty: Float
+  creatorRoyalty: Float!
 }
 
 "NFT transactional state"
@@ -95,7 +101,7 @@ type TransactionalStatusAuction @variant {
   "Type needs to have at least one non-relation entity. This value is not used."
   dummy: Int
 
-  "Member identifier"
+  "Auction"
   auction: Auction!
 }
 
@@ -113,7 +119,7 @@ type AuctionTypeEnglish @variant {
   duration: Int!
 
   "Auction extension time"
-  extensionTime: Int
+  extensionPeriod: Int
 }
 
 "Represents Open auction details"
@@ -124,6 +130,15 @@ type AuctionTypeOpen @variant {
 
 "Represents NFT auction"
 type Auction @entity {
+  "NFT's video"
+  video: Video!
+
+  "Member starting NFT auction"
+  initialOwner: Membership!
+
+  "Member that won this auction"
+  winningMember: Membership
+
   "Auction starting price"
   startingPrice: BigInt!
 
@@ -139,29 +154,40 @@ type Auction @entity {
   "Auction last bid (if exists)"
   lastBid: Bid
 
+  bids: [Bid!]! @derivedFrom(field: "auction")
+
   "Block when auction starts"
   startsAtBlock: Int!
 
   "Block when auction starts"
   endedAtBlock: Int!
 
+  "Is auction canceled"
+  isCanceled: Boolean!
+
+  "Is auction completed"
+  isCompleted: Boolean!
+
   "Auction participants whitelist"
   whitelistedMembers: [Membership!]! @derivedFrom(field: "whitelistedInAuctions")
 }
 
 "Represents bid in NFT auction"
 type Bid @entity {
+  "NFT's auction"
+  auction: Auction!
+
   "Bidder membership"
   bidder: Membership!
 
-  "Bidder account, used to pay for NFT"
-  bidderAccount: String!
+  # TODO: probably remove that
+  #"Bidder account, used to pay for NFT"
+  #bidderAccount: String!
 
   "Amount bidded"
   amount: BigInt!
 
-  "Bid time"
-  createdInTime: DateTime!
+  isCanceled: Boolean!
 
   "Block in which the bid was placed"
   createdInBlock: Int!

+ 1 - 1
types/augment/all/defs.json

@@ -854,7 +854,7 @@
     "AuctionType": {
         "_enum": {
             "English": "EnglishAuctionDetails",
-            "Open": "CuratorId"
+            "Open": "OpenAuctionDetails"
         }
     },
     "AuctionParams": {

+ 1 - 1
types/augment/all/types.ts

@@ -163,7 +163,7 @@ export interface AuctionType extends Enum {
   readonly isEnglish: boolean;
   readonly asEnglish: EnglishAuctionDetails;
   readonly isOpen: boolean;
-  readonly asOpen: CuratorId;
+  readonly asOpen: OpenAuctionDetails;
 }
 
 /** @name Backer */

+ 1 - 1
types/src/content/index.ts

@@ -195,7 +195,7 @@ export class OpenAuctionDetails extends JoyStructDecorated({
 
 export class AuctionType extends JoyEnum({
   English: EnglishAuctionDetails,
-  Open: CuratorId,
+  Open: OpenAuctionDetails,
 }) {}
 
 export class AuctionParams extends JoyStructDecorated({

+ 22 - 4
yarn.lock

@@ -1842,6 +1842,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.14.6":
+  version "7.16.5"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a"
+  integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/runtime@^7.6.3":
   version "7.7.4"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.4.tgz#b23a856751e4bf099262f867767889c0e3fe175b"
@@ -4544,6 +4551,17 @@
     "@polkadot/util" "7.3.1"
     "@polkadot/util-crypto" "7.3.1"
 
+"@polkadot/metadata@^4.17.1":
+  version "4.17.1"
+  resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-4.17.1.tgz#4da9ee5b2b816493910abfd302a50b58141ceca2"
+  integrity sha512-219isiCWVfbu5JxZnOPj+cV4T+S0XHS4+Jal3t3xz9y4nbgr+25Pa4KInEsJPx0u8EZAxMeiUCX3vd5U7oe72g==
+  dependencies:
+    "@babel/runtime" "^7.14.6"
+    "@polkadot/types" "4.17.1"
+    "@polkadot/types-known" "4.17.1"
+    "@polkadot/util" "^6.11.1"
+    "@polkadot/util-crypto" "^6.11.1"
+
 "@polkadot/networks@7.3.1", "@polkadot/networks@^7.3.1", "@polkadot/networks@^7.4.1":
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-7.3.1.tgz#4d4f7269ff9c285363946175ca95d6aaa08bdacc"
@@ -4621,7 +4639,7 @@
     websocket "^1.0.34"
     yargs "^17.1.1"
 
-"@polkadot/types-known@5.9.1":
+"@polkadot/types-known@4.17.1", "@polkadot/types-known@5.9.1":
   version "5.9.1"
   resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-5.9.1.tgz#e52fc7b803bc7cb3f41028f88963deb4ccee40af"
   integrity sha512-7lpLuIVGaKziQRzPMnTxyjlYy3spL6WqUg3CcEzmJUKQeUonHglOliQh8JSSz1bcP+YuNHGXK1cKsTjHb+GYxA==
@@ -4639,7 +4657,7 @@
     "@babel/runtime" "^7.15.4"
     "@polkadot/util" "^7.3.1"
 
-"@polkadot/types@5.9.1":
+"@polkadot/types@4.17.1", "@polkadot/types@5.9.1":
   version "5.9.1"
   resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-5.9.1.tgz#74cf4695795f2aa365ff85d3873e22c430100bc9"
   integrity sha512-30vcSlNBxPyWYZaxKDr/BoMhfLCRKB265XxpnnNJmbdZZsL+N4Zp2mJR9/UbA6ypmJBkUjD7b1s9AYsLwUs+8w==
@@ -4691,7 +4709,7 @@
     "@babel/runtime" "^7.10.5"
     color "^3.1.2"
 
-"@polkadot/util-crypto@7.3.1", "@polkadot/util-crypto@^3.0.1", "@polkadot/util-crypto@^7.3.1":
+"@polkadot/util-crypto@7.3.1", "@polkadot/util-crypto@^3.0.1", "@polkadot/util-crypto@^6.11.1", "@polkadot/util-crypto@^7.3.1":
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-7.3.1.tgz#a597145b061eddaafd69adc6c1ce19224542307f"
   integrity sha512-sR+BxlV2Da0xfQcCXQTz+ohTaagixM+qYHytaQzilytbKHgYIyvnOyk5wFrHDNFzcLuXo15AbULa3TCoNDvh5Q==
@@ -4714,7 +4732,7 @@
     tweetnacl "^1.0.3"
     xxhashjs "^0.2.2"
 
-"@polkadot/util@7.3.1", "@polkadot/util@7.4.1", "@polkadot/util@^3.0.1", "@polkadot/util@^7.3.1", "@polkadot/util@^7.4.1":
+"@polkadot/util@7.3.1", "@polkadot/util@7.4.1", "@polkadot/util@^3.0.1", "@polkadot/util@^6.11.1", "@polkadot/util@^7.3.1", "@polkadot/util@^7.4.1":
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-7.3.1.tgz#1a33a8d4ef2dcbc3e14a9a919f30bb16360a6fae"
   integrity sha512-fjz5yjgZgfgRXZw9zMufmPBHhjAVtk/M2+lgl1a6Fck43Q4TG2Ux1haXMlaoe37cFeh8XgDAzDEQVIYBIPy6sg==