import { ObservableChainQuery , ObservableChainQueryMap , } from "../../chain-query" ; import { BondStatus , Validators , Validator } from "./types" ; import { KVStore } from "@keplr-wallet/common" ; import { ChainGetter } from "../../../common" ; import { autorun , computed , makeObservable , observable , runInAction , } from "mobx" ; import { ObservableQuery , QueryResponse } from "../../../common" ; import Axios , { CancelToken } from "axios" ; import PQueue from "p-queue" ; import { CoinPretty , Dec } from "@keplr-wallet/unit" ; import { computedFn } from "mobx-utils" ; interface KeybaseResult { status : { code : number ; name : string ; } ; them ? : [ { id ? : string ; pictures ? : { primary ? : { url ? : string ; } ; } ; } ] ; } /**
 * Fetch the validator's thumbnail from keybase if the identity exists.
 */ export class ObservableQueryValidatorThumbnail extends ObservableQuery < KeybaseResult > { protected protected static fetchingThumbnailQueue : PQueue = new PQueue ( { concurrency : 3 , } ) ; protected readonly validator : Validator ; constructor ( kvStore , validator ) { const instance = Axios . create ( { baseURL : "https://keybase.io/" , } ) ; super ( kvStore , instance , `_/api/1.0/user/lookup.json?fields=pictures&key_suffix=${ validator . description . identity }` ) ; makeObservable ( this ) ; this . validator = validator ; } protected canFetch ( ) { return this . validator . description . identity !== "" ; } protected async fetchResponse ( cancelToken ) { return await ObservableQueryValidatorThumbnail . fetchingThumbnailQueue . add ( ( ) => { return super . fetchResponse ( cancelToken ) ; } ) ; } @ computed get thumbnail ( ) : string { if ( this . response ?. data . status . code === 0 ) { if ( this . response . data . them && this . response . data . them . length > 0 ) { return this . response . data . them [ 0 ] . pictures ?. primary ?. url ?? "" ; } } return "" ; } } export class ObservableQueryValidatorsInner extends ObservableChainQuery < Validators > { @ observable . shallow protected thumbnailMap : Map < string , ObservableQueryValidatorThumbnail > = new Map ( ) ; constructor ( kvStore , chainId , chainGetter , protected readonly status ) { super ( kvStore , chainId , chainGetter , `/staking/validators?status=${ status }` ) ; makeObservable ( this ) ; autorun ( ( ) => { const chainInfo = this . chainGetter . getChain ( this . chainId ) ; if ( chainInfo . features && chainInfo . features . includes ( "stargate" ) ) { const url = ( ( ) => { switch ( this . status ) { case BondStatus . Bonded : return `/staking/validators?status=BOND_STATUS_BONDED` ; case BondStatus . Unbonded : return `/staking/validators?status=BOND_STATUS_UNBONDED&limit=500` ; case BondStatus . Unbonding : return `/staking/validators?status=BOND_STATUS_UNBONDING&limit=500` ; } } ) ( ) ; this . setUrl ( url ) ; } } ) ; } @ computed get validators ( ) : Validator [ ] { if ( ! this . response ?. data . result ) { return [ ] ; } return this . response . data . result ; } readonly getValidator = computedFn ( ( validatorAddress ) : Validator | undefined => { const validators = this . validators ; return validators . find ( ( val ) => val . operator_address === validatorAddress ) ; } ) ; @ computed get validatorsSortedByVotingPower ( ) : Validator [ ] { const validators = this . validators ; return validators . sort ( ( v1 , v2 ) => { return new Dec ( v1 . delegator_shares ) . gt ( new Dec ( v2 . delegator_shares ) ) ? - 1 : 1 ; } ) ; } readonly getValidatorThumbnail = computedFn ( ( operatorAddress ) : string => { const validators = this . validators ; const validator = validators . find ( ( val ) => val . operator_address === operatorAddress ) ; if ( ! validator ) { return "" ; } if ( ! validator . description . identity ) { return "" ; } const identity = validator . description . identity ; if ( ! this . thumbnailMap . has ( identity ) ) { runInAction ( ( ) => { this . thumbnailMap . set ( identity , new ObservableQueryValidatorThumbnail ( this . kvStore , validator ) ) ; } ) ; } return this . thumbnailMap . get ( identity ) ! . thumbnail ; } ) ; /**
     * Return the validator's voting power as human friendly (considering the coin decimals).
     */ readonly getValidatorShare = computedFn ( ( operatorAddress ) : CoinPretty | undefined => { const validators = this . validators ; const validator = validators . find ( ( val ) => val . operator_address === operatorAddress ) ; if ( ! validator ) { return ; } const chainInfo = this . chainGetter . getChain ( this . chainId ) ; const stakeCurrency = chainInfo . stakeCurrency ; const power = new Dec ( validator . delegator_shares ) . truncate ( ) ; return new CoinPretty ( stakeCurrency , power ) ; } ) ; } export class ObservableQueryValidators extends ObservableChainQueryMap < Validators > { constructor ( protected readonly kvStore , protected readonly chainId , protected readonly chainGetter ) { super ( kvStore , chainId , chainGetter , ( status ) => { return new ObservableQueryValidatorsInner ( this . kvStore , this . chainId , this . chainGetter , status as BondStatus ) ; } ) ; } getQueryStatus ( status = BondStatus . Bonded ) { return this . get ( status ) as ObservableQueryValidatorsInner ; } }