diff --git a/package.json b/package.json index 957c89dc2..fabd12454 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "test": "jest --silent" }, "dependencies": { - "@polkadot/api": "^3.3.1", + "@polkadot/api": "^3.3.2", "@polkadot/apps-config": "^0.74.1", "@polkadot/util-crypto": "^5.2.3", "@substrate/calc": "^0.1.3", diff --git a/src/services/accounts/AccountsStakingPayoutsService.ts b/src/services/accounts/AccountsStakingPayoutsService.ts index 505328f75..83ce64c16 100644 --- a/src/services/accounts/AccountsStakingPayoutsService.ts +++ b/src/services/accounts/AccountsStakingPayoutsService.ts @@ -1,7 +1,13 @@ import { ApiPromise } from '@polkadot/api'; -import { DeriveEraExposure } from '@polkadot/api-derive/staking/types'; -import { BlockHash } from '@polkadot/types/interfaces'; import { + DeriveEraExposure, + DeriveEraExposureNominating, +} from '@polkadot/api-derive/staking/types'; +import { Option } from '@polkadot/types'; +import { + BalanceOf, + BlockHash, + EraIndex, EraRewardPoints, Perbill, StakingLedger, @@ -16,6 +22,33 @@ import { } from '../../types/responses'; import { AbstractService } from '../AbstractService'; +/** + * General information about an era, in tuple form because we initially get it + * by destructuring a Promise.all(...) + */ +type IErasGeneral = [DeriveEraExposure, EraRewardPoints, Option]; + +/** + * Commission and staking ledger of a validator + */ +interface ICommissionAndLedger { + commission: Perbill; + validatorLedger?: StakingLedger; +} + +/** + * All the data we need to calculate payouts for an address at a given era. + */ +interface IEraData { + deriveEraExposure: DeriveEraExposure; + eraRewardPoints: EraRewardPoints; + erasValidatorRewardOption: Option; + exposuresWithCommission?: (ICommissionAndLedger & { + validatorId: string; + })[]; + eraIndex: EraIndex; +} + export class AccountsStakingPayoutsService extends AbstractService { /** * Fetch and derive payouts for `address`. @@ -55,96 +88,189 @@ export class AccountsStakingPayoutsService extends AbstractService { 'than or equal to current_era - history_depth.' ); } + const at = { height: number.unwrap().toString(10), hash, }; - const erasPayouts: (IEraPayouts | { message: string })[] = []; - // User friendly - we don't error if the user specified era & depth combo <= 0, instead just start at 0 - const startEra = era - (depth - 1) < 0 ? 0 : era - (depth - 1); + const startEra = Math.max(0, era - (depth - 1)); - for (let e = startEra; e <= era; e += 1) { - erasPayouts.push( - await this.deriveEraPayouts( - api, - hash, + // Fetch general data about the era + const allErasGeneral = await this.fetchAllErasGeneral( + api, + hash, + startEra, + era + ); + + // With the general data, we can now fetch the commission of each validator `address` nominates + const allErasCommissions = await this.fetchAllErasCommissions( + api, + hash, + address, + startEra, + // Create an array of `DeriveEraExposure` + allErasGeneral.map((eraGeneral) => eraGeneral[0]) + ); + + // Group together data by Era so we can easily associate parts that are used congruently downstream + const allEraData = allErasGeneral.map( + ( + [ + deriveEraExposure, + eraRewardPoints, + erasValidatorRewardOption, + ]: IErasGeneral, + idx: number + ): IEraData => { + const eraCommissions = allErasCommissions[idx]; + + const nominatedExposures = this.deriveNominatedExposures( address, - e, - unclaimedOnly - ) - ); - } + deriveEraExposure + ); + + // Zip the `validatorId` with its associated `commission`, making the data easier to reason + // about downstream + const exposuresWithCommission = nominatedExposures?.map( + ({ validatorId }, idx) => { + return { + validatorId, + ...eraCommissions[idx], + }; + } + ); + + return { + deriveEraExposure, + eraRewardPoints, + erasValidatorRewardOption, + exposuresWithCommission, + eraIndex: api.createType('EraIndex', idx + startEra), + }; + } + ); return { at, - erasPayouts, + erasPayouts: allEraData.map((eraData) => + this.deriveEraPayouts(address, unclaimedOnly, eraData) + ), }; } /** - * Derive all the payouts for `address` at `era`. + * Fetch general info about eras in the inclusive range `startEra` .. `era`. * - * @param api + * @param api `ApiPromise` + * @param hash `BlockHash` to make call at + * @param startEra first era to get data for + * @param era the last era to get data for + */ + async fetchAllErasGeneral( + api: ApiPromise, + hash: BlockHash, + startEra: number, + era: number + ): Promise { + const allDeriveQuerys: Promise[] = []; + for (let e = startEra; e <= era; e += 1) { + const eraIndex = api.createType('EraIndex', e); + + const eraGeneralTuple = Promise.all([ + api.derive.staking.eraExposure(eraIndex), + api.query.staking.erasRewardPoints.at(hash, eraIndex), + api.query.staking.erasValidatorReward.at(hash, eraIndex), + ]); + + allDeriveQuerys.push(eraGeneralTuple); + } + + return Promise.all(allDeriveQuerys); + } + + /** + * Fetch the commission & staking ledger for each `validatorId` in `deriveErasExposures`. + * + * @param api `ApiPromise` * @param hash `BlockHash` to make call at * @param address address of the _Stash_ account to get the payouts of - * @param era the era to query - * @param unclaimedOnly whether or not to only show unclaimed payouts + * @param startEra first era to get data for + * @param deriveErasExposures exposures per era for `address` */ - async deriveEraPayouts( + fetchAllErasCommissions( api: ApiPromise, hash: BlockHash, address: string, - era: number, - unclaimedOnly: boolean - ): Promise { - const eraIndex = api.createType('EraIndex', era); + startEra: number, + deriveErasExposures: DeriveEraExposure[] + ): Promise { + // Cache StakingLedger to reduce redundant queries to node + const validatorLedgerCache: { [id: string]: StakingLedger } = {}; + + const allErasCommissions = deriveErasExposures.map( + (deriveEraExposure, idx) => { + const currEra = idx + startEra; + + const nominatedExposures = this.deriveNominatedExposures( + address, + deriveEraExposure + ); + + if (!nominatedExposures) { + return []; + } + + const singleEraCommissions = nominatedExposures.map( + ({ validatorId }) => + this.fetchCommissionAndLedger( + api, + validatorId, + currEra, + hash, + validatorLedgerCache + ) + ); + + return Promise.all(singleEraCommissions); + } + ); - const [ + return Promise.all(allErasCommissions); + } + + /** + * Derive all the payouts for `address` at `era`. + * + * @param address address of the _Stash_ account to get the payouts of + * @param era the era to query + * @param eraData data about the address and era we are calculating payouts for + */ + deriveEraPayouts( + address: string, + unclaimedOnly: boolean, + { deriveEraExposure, eraRewardPoints, erasValidatorRewardOption, - ] = await Promise.all([ - api.derive.staking.eraExposure(eraIndex), - api.query.staking.erasRewardPoints.at(hash, era), - api.query.staking.erasValidatorReward.at(hash, era), - ]); - - let nominatedExposures = deriveEraExposure.nominators[address]; - if (deriveEraExposure.validators[address]) { - // We treat an `address` that is a validator as nominating itself - nominatedExposures = nominatedExposures - ? nominatedExposures.concat({ - validatorId: address, - validatorIndex: 0, - }) - : [ - { - validatorId: address, - validatorIndex: 0, - }, - ]; - } - - if (!nominatedExposures) { + exposuresWithCommission, + eraIndex, + }: IEraData + ): IEraPayouts | { message: string } { + if (!exposuresWithCommission) { return { - message: `${address} has no nominations for the era ${era}`, + message: `${address} has no nominations for the era ${eraIndex.toString()}`, }; } if (erasValidatorRewardOption.isNone) { return { - message: `No ErasValidatorReward for the era ${era}`, + message: `No ErasValidatorReward for the era ${eraIndex.toString()}`, }; } - // Cache StakingLedger to reduce redundant queries to node - const validatorLedgerCache: { [id: string]: StakingLedger } = {}; - - // Payouts for the era - const payouts: IPayout[] = []; - const totalEraRewardPoints = eraRewardPoints.total; const totalEraPayout = erasValidatorRewardOption.unwrap(); const calcPayout = CalcPayout.from_params( @@ -152,8 +278,13 @@ export class AccountsStakingPayoutsService extends AbstractService { totalEraPayout.toString(10) ); - // Loop through validators that this nominator backs - for (const { validatorId } of nominatedExposures) { + // Iterate through validators that this nominator backs and calculate payouts for the era + const payouts: IPayout[] = []; + for (const { + validatorId, + commission: validatorCommission, + validatorLedger, + } of exposuresWithCommission) { const totalValidatorRewardPoints = this.extractTotalValidatorRewardPoints( eraRewardPoints, validatorId @@ -178,21 +309,9 @@ export class AccountsStakingPayoutsService extends AbstractService { continue; } - const { - commission: validatorCommission, - validatorLedger, - } = await this.fetchCommissionAndLedger( - api, - validatorId, - era, - hash, - validatorLedgerCache - ); - if (!validatorLedger) { continue; } - // Check if the reward has already been claimed const claimed = validatorLedger.claimedRewards.includes(eraIndex); if (unclaimedOnly && claimed) { @@ -241,10 +360,7 @@ export class AccountsStakingPayoutsService extends AbstractService { era: number, hash: BlockHash, validatorLedgerCache: { [id: string]: StakingLedger } - ): Promise<{ - commission: Perbill; - validatorLedger?: StakingLedger; - }> { + ): Promise { let commission; let validatorLedger; if (validatorId in validatorLedgerCache) { @@ -339,4 +455,29 @@ export class AccountsStakingPayoutsService extends AbstractService { nominatorExposure, }; } + + /** + * Derive the list of validators nominated by `address`. Note: we count validators as nominating + * themself. + * + * @param address address of the _Stash_ account to get the payouts of + * @param deriveEraExposure result of query api.derive.staking.eraExposure(eraIndex) + */ + deriveNominatedExposures( + address: string, + deriveEraExposure: DeriveEraExposure + ): DeriveEraExposureNominating[] | undefined { + let nominatedExposures: DeriveEraExposureNominating[] = + deriveEraExposure.nominators[address] ?? []; + if (deriveEraExposure.validators[address]) { + // We treat an `address` that is a validator as nominating itself + nominatedExposures = nominatedExposures.concat({ + validatorId: address, + // We put in an arbitrary number because we do not use the index + validatorIndex: 9999, + }); + } + + return nominatedExposures; + } } diff --git a/yarn.lock b/yarn.lock index 94843148c..51f702ba1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -559,36 +559,36 @@ resolved "https://registry.yarnpkg.com/@open-web3/orml-type-definitions/-/orml-type-definitions-0.8.2-5.tgz#f65b000affdb207a71f35adc9a7d10a8141a6eb7" integrity sha512-o/o7ucy4RkuQ5oZgGMBc7t7UuaTK/+rG6QHE7+ZPCRG6cnYzg6ZZ9pApOTmzG0e47k0GUZDg0m+IYzltgwJRMQ== -"@polkadot/api-derive@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-3.3.1.tgz#a091556074caa80bd941711eb1c3c4b6102c6cf9" - integrity sha512-q31gxAitW9XKTBpGxJ2pywtQ9QddRZJzgj4mpBAIXVF+CHsOtmM+nvwHtZRE4FiaVQAhvgoWij3ARLfH5eRV2g== +"@polkadot/api-derive@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-3.3.2.tgz#4c9d6c6801dda71a18d511fabb890779471f9a70" + integrity sha512-Zp7SuRfgjsGUGmMaSJLaXH3PcCLfieo3rhiwajcgZm6/ZBoX5eQlADpAuAhAcugrr60a0LTiiHgt6a2MsqsX6w== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/api" "3.3.1" - "@polkadot/rpc-core" "3.3.1" - "@polkadot/types" "3.3.1" + "@polkadot/api" "3.3.2" + "@polkadot/rpc-core" "3.3.2" + "@polkadot/types" "3.3.2" "@polkadot/util" "^5.2.3" "@polkadot/util-crypto" "^5.2.3" - "@polkadot/x-rxjs" "3.3.1" + "@polkadot/x-rxjs" "3.3.2" bn.js "^4.11.9" -"@polkadot/api@3.3.1", "@polkadot/api@^3.2.3", "@polkadot/api@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-3.3.1.tgz#4ccf614033a684633ac6abf5390eb0e2120659ba" - integrity sha512-52n7ZSOBD/JNEaKqtI4+G4PCMbrrVzl/sQZhjEV+OCdx0XMwf3Zm7f2X6zukLSo0zoHrBYBKWekN+xR2GHH1Tg== +"@polkadot/api@3.3.2", "@polkadot/api@^3.2.3", "@polkadot/api@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-3.3.2.tgz#c5d244fa1312c04a147322a14bc6eb43f93b8829" + integrity sha512-GUzuNFberFQjeqZq513sH8BMDx5Ez8JbgRF24HUJeWjpBZAwEgVr6mK+eUCnqE2P+PW7n971353nCUyfsixCxQ== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/api-derive" "3.3.1" + "@polkadot/api-derive" "3.3.2" "@polkadot/keyring" "^5.2.3" - "@polkadot/metadata" "3.3.1" - "@polkadot/rpc-core" "3.3.1" - "@polkadot/rpc-provider" "3.3.1" - "@polkadot/types" "3.3.1" - "@polkadot/types-known" "3.3.1" + "@polkadot/metadata" "3.3.2" + "@polkadot/rpc-core" "3.3.2" + "@polkadot/rpc-provider" "3.3.2" + "@polkadot/types" "3.3.2" + "@polkadot/types-known" "3.3.2" "@polkadot/util" "^5.2.3" "@polkadot/util-crypto" "^5.2.3" - "@polkadot/x-rxjs" "3.3.1" + "@polkadot/x-rxjs" "3.3.2" bn.js "^4.11.9" eventemitter3 "^4.0.7" @@ -616,14 +616,14 @@ "@polkadot/util" "5.2.3" "@polkadot/util-crypto" "5.2.3" -"@polkadot/metadata@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-3.3.1.tgz#255b8a0b62378c755fa7eb8452499a0cca86ed92" - integrity sha512-Ebzm+ctbp1mzol1e6C8ECG7NdLzeL4gQKxa1ilKF+wm2kiFwvzrxV21tYmvNI7I3IKMeHT0+X3pRlzUFyfXjSw== +"@polkadot/metadata@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-3.3.2.tgz#7654b9a612cace41fb87b5c6e508912f194c6eaa" + integrity sha512-+dIgGghwEl88uiRLZhEwlKyK0OX3TPGsXbyATWT6u8C388vNAyV2Her//Kluie/tESLVpjXzEeeqqvd9xm+RrA== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/types" "3.3.1" - "@polkadot/types-known" "3.3.1" + "@polkadot/types" "3.3.2" + "@polkadot/types-known" "3.3.2" "@polkadot/util" "^5.2.3" "@polkadot/util-crypto" "^5.2.3" bn.js "^4.11.9" @@ -635,25 +635,25 @@ dependencies: "@babel/runtime" "^7.12.5" -"@polkadot/rpc-core@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-3.3.1.tgz#60233fea680483a76af0d68d618eb2095b53e562" - integrity sha512-OBQZl1PNlLTMG1vte6YXwfC7HJqqyQSGLaxS1xJUFJVdIN9txwbsrrqbhgYwWfsWdacFZ11r/rB0HfZ89zdqDg== +"@polkadot/rpc-core@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-3.3.2.tgz#6d915afc75ca09f2a68c79129e42e27c324d1e2a" + integrity sha512-1f167jRxbsCoxzeG3Dq/bc3ZgtLWbbxO/RDJbAlUc/AWpP/pDLEMh/JuqossYtWzrByq0+t0y5aq+zEabhlYpw== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/metadata" "3.3.1" - "@polkadot/rpc-provider" "3.3.1" - "@polkadot/types" "3.3.1" + "@polkadot/metadata" "3.3.2" + "@polkadot/rpc-provider" "3.3.2" + "@polkadot/types" "3.3.2" "@polkadot/util" "^5.2.3" - "@polkadot/x-rxjs" "3.3.1" + "@polkadot/x-rxjs" "3.3.2" -"@polkadot/rpc-provider@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-3.3.1.tgz#5120b67bbd054804b13092cfe20abbc3479b376b" - integrity sha512-uniicLPOWGOr0mEgr3KO56ryUTcu+6Fe6vhTpklguB3J7uO2/hIaMpCPN3Yb1xUQ5buQIkp16YVr7SF3pL4PWQ== +"@polkadot/rpc-provider@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-3.3.2.tgz#cb23bb63c9998b4ffca4470cfb1c9297b37b5ee0" + integrity sha512-yNtffX2DkCOsgw2pks8C5XP2P458FRyMhIOJsYwushbCHpa3iHTeUHSRqpKz2JiTp3SIKPtcTA0+SSmgsZ+Kzg== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/types" "3.3.1" + "@polkadot/types" "3.3.2" "@polkadot/util" "^5.2.3" "@polkadot/util-crypto" "^5.2.3" "@polkadot/x-fetch" "^5.2.3" @@ -661,26 +661,26 @@ bn.js "^4.11.9" eventemitter3 "^4.0.7" -"@polkadot/types-known@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-3.3.1.tgz#ef5ee5c46cefdb9c050726e1b1072c0510dc52b0" - integrity sha512-foOt/NgYqerqvs7PNlfyNWYKym0CQvrDuF7SkZewW5Y1XF4Ulx60A3pXOfUNwCRHuN9PB/JI/D3g9Fth3Tr0eg== +"@polkadot/types-known@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-3.3.2.tgz#7618e96d977ba630ed6ecca969df2c21c4dc272f" + integrity sha512-PpvHrJD7SWZMTvvOTv8mq3uwZvspDbYQBGfUvDwG3sY6IeD8KBY00FM2atGFPcDW07SgX5pnsR+uMAZfOBErSQ== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/types" "3.3.1" + "@polkadot/types" "3.3.2" "@polkadot/util" "^5.2.3" bn.js "^4.11.9" -"@polkadot/types@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-3.3.1.tgz#bd94296b1f116ef52e531c1d7d5130626116b099" - integrity sha512-Qba8dqyTAZLbep1hCq9gkXmzOp28BXc6fsD7X5+9kibv3/L0ZKHbmKmoDDM4OsBX5Ar04AuxKomR77FJR1FymQ== +"@polkadot/types@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-3.3.2.tgz#5cae028994435b332da19cd67f7915b4bbe2a93f" + integrity sha512-QF18+OdhV06Xa1IhzQARRUTo1Hqkb7X4ACuUGOM463epekcPE4HkW0h/fa21wGjmEFYm/2ur76BVNuvXuIuL3A== dependencies: "@babel/runtime" "^7.12.5" - "@polkadot/metadata" "3.3.1" + "@polkadot/metadata" "3.3.2" "@polkadot/util" "^5.2.3" "@polkadot/util-crypto" "^5.2.3" - "@polkadot/x-rxjs" "3.3.1" + "@polkadot/x-rxjs" "3.3.2" "@types/bn.js" "^4.11.6" bn.js "^4.11.9" @@ -757,10 +757,10 @@ dependencies: "@babel/runtime" "^7.12.5" -"@polkadot/x-rxjs@3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-3.3.1.tgz#e1ff305b5c96b1a696c632c49561bafb6b4ceb21" - integrity sha512-nmvAikDWSkChlqFzTHR5PX2wANVz8IepN9zO4QrgO4PmX3wT/ZykehsLtTXrVsOe95iuoZY4Qyvi9fcnNw0PTQ== +"@polkadot/x-rxjs@3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-3.3.2.tgz#b29829f1c3df3ae63e2fcf70cefd477829ca8929" + integrity sha512-ktc78M7QCCykHYKGN1LMVkFhXiLkORYMC4d7+K1t/bM+oR7UQ6WXA6v2C7Zm10rPW5uS0O1JrAKjk+nxUPp5AQ== dependencies: "@babel/runtime" "^7.12.5" rxjs "^6.6.3" @@ -803,9 +803,9 @@ "@sinonjs/commons" "^1.7.0" "@sora-substrate/type-definitions@^0.3.1": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@sora-substrate/type-definitions/-/type-definitions-0.3.2.tgz#206637c3790cb3bd251e2ca8ee105251231761ea" - integrity sha512-5/NofwSPjQpFWEPDhqhcGSYSdvU5n8P6M+6oazwlfljSeFXNmhnEY1ejhUKeDvMTfHgtwfD9tms4KfkPd8pn5Q== + version "0.3.3" + resolved "https://registry.yarnpkg.com/@sora-substrate/type-definitions/-/type-definitions-0.3.3.tgz#8aa7e05d90339805aa689c2caeac0df7c9c459f6" + integrity sha512-rOVpjb5dexjM7JEcOxiGXNuMKMtYEuYd5wSZLiG0OkUfVjXfZu1pMfJwPT3bkXwRElyXnooNDhW3XhBrpkvJHQ== dependencies: "@open-web3/orml-type-definitions" "^0.6.0-beta.26" @@ -2933,9 +2933,9 @@ fs.realpath@^1.0.0: integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^2.1.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.2.1.tgz#1fb02ded2036a8ac288d507a65962bd87b97628d" - integrity sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.1.tgz#b209ab14c61012636c8863507edf7fb68cc54e9f" + integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw== function-bind@^1.1.1: version "1.1.1" @@ -3081,9 +3081,9 @@ globals@^12.1.0: type-fest "^0.8.1" globby@^11.0.1: - version "11.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" - integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== + version "11.0.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" + integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -6259,9 +6259,9 @@ tslib@^1.8.1, tslib@^1.9.0: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tsutils@^3.17.1: - version "3.18.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.18.0.tgz#38add50a28ec97e988cb43c5b32e55d1ff4a222a" - integrity sha512-D9Tu8nE3E7D1Bsf/V29oMHceMf+gnVO+pDguk/A5YRo1cLpkiQ48ZnbbS57pvvHeY+OIeNQx1vf4ASPlEtRpcA== + version "3.19.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.19.0.tgz#9387cb5fcb71579aa0909c509604f8a7fbe1cff1" + integrity sha512-A7BaLUPvcQ1cxVu72YfD+UMI3SQPTDv/w4ol6TOwLyI0hwfG9EC+cYlhdflJTmtYTgZ3KqdPSe/otxU4K3kArg== dependencies: tslib "^1.8.1"