diff --git a/packages/api-derive/src/accounts/idAndIndex.ts b/packages/api-derive/src/accounts/idAndIndex.ts index d272ab38b2..ba45994eb7 100644 --- a/packages/api-derive/src/accounts/idAndIndex.ts +++ b/packages/api-derive/src/accounts/idAndIndex.ts @@ -12,7 +12,7 @@ import { decodeAddress } from '@polkadot/util-crypto'; import { memo } from '../util'; -function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIndex | string | null | undefined): Observable { +function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIndex | Uint8Array | string | null | undefined): Observable { try { // yes, this can fail, don't care too much, catch will catch it const decoded = isU8a(address) @@ -39,7 +39,7 @@ function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIn /** * @name idAndIndex - * @param {(Address | AccountId | AccountIndex | string | null)} address - An accounts address in various formats. + * @param {(Address | AccountId | AccountIndex | Uint8Array | string | null)} address - An accounts address in various formats. * @description An array containing the [[AccountId]] and [[AccountIndex]] as optional values. * @example *
@@ -50,7 +50,7 @@ function retrieve (api: ApiInterfaceRx, address: Address | AccountId | AccountIn * }); * ``` */ -export function idAndIndex (instanceId: string, api: ApiInterfaceRx): (address?: Address | AccountId | AccountIndex | string | null) => Observable { - return memo(instanceId, (address?: Address | AccountId | AccountIndex | string | null): Observable => +export function idAndIndex (instanceId: string, api: ApiInterfaceRx): (address?: Address | AccountId | AccountIndex | Uint8Array | string | null) => Observable { + return memo(instanceId, (address?: Address | AccountId | AccountIndex | Uint8Array | string | null): Observable => retrieve(api, address)); } diff --git a/packages/api-derive/src/accounts/identity.ts b/packages/api-derive/src/accounts/identity.ts new file mode 100644 index 0000000000..18a90956e2 --- /dev/null +++ b/packages/api-derive/src/accounts/identity.ts @@ -0,0 +1,127 @@ +// Copyright 2017-2020 @polkadot/api-derive authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { ApiInterfaceRx } from '@polkadot/api/types'; +import { AccountId, IdentityInfoAdditional, Registration } from '@polkadot/types/interfaces'; +import { ITuple } from '@polkadot/types/types'; +import { DeriveAccountRegistration, DeriveHasIdentity } from '../types'; + +import { Observable, combineLatest, of } from 'rxjs'; +import { map, switchMap } from 'rxjs/operators'; +import { Data, Option } from '@polkadot/types'; +import { u8aToString } from '@polkadot/util'; + +import { memo } from '../util'; + +const UNDEF_HEX = { toHex: () => undefined }; + +function dataAsString (data: Data): string | undefined { + return data.isRaw + ? u8aToString(data.asRaw.toU8a(true)) + : data.isNone + ? undefined + : data.toHex(); +} + +function extractOther (additional: IdentityInfoAdditional[]): Record { + return additional.reduce((other: Record, [_key, _value]): Record => { + const key = dataAsString(_key); + const value = dataAsString(_value); + + if (key && value) { + other[key] = value; + } + + return other; + }, {}); +} + +function extractIdentity (identityOfOpt?: Option, superOf?: [AccountId, Data]): DeriveAccountRegistration { + if (!identityOfOpt?.isSome) { + return { judgements: [] }; + } + + const { info, judgements } = identityOfOpt.unwrap(); + const topDisplay = dataAsString(info.display); + + return { + display: (superOf && dataAsString(superOf[1])) || topDisplay, + displayParent: superOf && topDisplay, + email: dataAsString(info.email), + image: dataAsString(info.image), + judgements, + legal: dataAsString(info.legal), + other: extractOther(info.additional), + parent: superOf && superOf[0], + pgp: info.pgpFingerprint.unwrapOr(UNDEF_HEX).toHex(), + riot: dataAsString(info.riot), + twitter: dataAsString(info.twitter), + web: dataAsString(info.web) + }; +} + +function getParent (api: ApiInterfaceRx, identityOfOpt: Option | undefined, superOfOpt: Option> | undefined): Observable<[Option | undefined, [AccountId, Data] | undefined]> { + if (identityOfOpt?.isSome) { + // this identity has something set + return of([identityOfOpt, undefined]); + } else if (superOfOpt?.isSome) { + const superOf = superOfOpt.unwrap(); + + // we have a super + return combineLatest([ + api.query.identity.identityOf>(superOf[0]), + of(superOf) + ]); + } + + // nothing of value returned + return of([undefined, undefined]); +} + +export function _identityBase (instanceId: string, api: ApiInterfaceRx): (accountId?: AccountId | Uint8Array | string) => Observable<[Option | undefined, Option> | undefined]> { + return memo(instanceId, (accountId?: AccountId | Uint8Array | string): Observable<[Option | undefined, Option> | undefined]> => + accountId && api.query.identity?.identityOf + ? api.queryMulti<[Option, Option>]>([ + [api.query.identity.identityOf, accountId], + [api.query.identity.superOf, accountId] + ]) + : of([undefined, undefined]) + ); +} + +/** + * @name identity + * @description Returns identity info for an account + */ +export function identity (instanceId: string, api: ApiInterfaceRx): (accountId?: AccountId | Uint8Array | string) => Observable { + return memo(instanceId, (accountId?: AccountId | Uint8Array | string): Observable => + api.derive.accounts._identityBase(accountId).pipe( + switchMap(([identityOfOpt, superOfOpt]) => getParent(api, identityOfOpt, superOfOpt)), + map(([identityOfOpt, superOf]) => extractIdentity(identityOfOpt, superOf)) + ) + ); +} + +export function hasIdentity (instanceId: string, api: ApiInterfaceRx): (accountId: AccountId | Uint8Array | string) => Observable { + return memo(instanceId, (accountId: AccountId | Uint8Array | string): Observable => + api.derive.accounts._identityBase(accountId).pipe( + map(([identityOfOpt, superOfOpt]: [Option | undefined, Option> | undefined]): DeriveHasIdentity => { + const parentId = superOfOpt && superOfOpt.isSome + ? superOfOpt.unwrap().toString() + : null; + const hasParent = !!parentId; + const hasIdentity = hasParent || !!(identityOfOpt && identityOfOpt.isSome); + + return { hasIdentity, hasParent, parentId }; + }) + ) + ); +} + +export function hasIdentityMulti (instanceId: string, api: ApiInterfaceRx): (accountIds: (AccountId | Uint8Array | string)[]) => Observable { + return memo(instanceId, (accountIds: (AccountId | Uint8Array | string)[]): Observable => + combineLatest( + accountIds.map((accountId) => api.derive.accounts.hasIdentity(accountId)) + ) + ); +} diff --git a/packages/api-derive/src/accounts/index.ts b/packages/api-derive/src/accounts/index.ts index b8810ca799..de8f3a9567 100644 --- a/packages/api-derive/src/accounts/index.ts +++ b/packages/api-derive/src/accounts/index.ts @@ -5,6 +5,7 @@ export * from './accountId'; export * from './flags'; export * from './idAndIndex'; export * from './idToIndex'; +export * from './identity'; export * from './indexToId'; export * from './indexes'; export * from './info'; diff --git a/packages/api-derive/src/accounts/info.ts b/packages/api-derive/src/accounts/info.ts index 815b2cf257..2bab0c10a1 100644 --- a/packages/api-derive/src/accounts/info.ts +++ b/packages/api-derive/src/accounts/info.ts @@ -2,27 +2,17 @@ // SPDX-License-Identifier: Apache-2.0 import { ApiInterfaceRx } from '@polkadot/api/types'; -import { AccountId, AccountIndex, Address, Balance, IdentityInfoAdditional, Registration } from '@polkadot/types/interfaces'; +import { AccountId, AccountIndex, Address, Balance } from '@polkadot/types/interfaces'; import { ITuple } from '@polkadot/types/types'; import { DeriveAccountInfo, DeriveAccountRegistration } from '../types'; import { Observable, combineLatest, of } from 'rxjs'; import { map, switchMap } from 'rxjs/operators'; -import { Bytes, Data, Option, u32 } from '@polkadot/types'; +import { Bytes, Option, u32 } from '@polkadot/types'; import { u8aToString } from '@polkadot/util'; import { memo } from '../util'; -const UNDEF_HEX = { toHex: () => undefined }; - -function dataAsString (data: Data): string | undefined { - return data.isRaw - ? u8aToString(data.asRaw.toU8a(true)) - : data.isNone - ? undefined - : data.toHex(); -} - function retrieveNick (api: ApiInterfaceRx, accountId?: AccountId): Observable { return (( accountId && api.query.nicks?.nameOf @@ -37,86 +27,17 @@ function retrieveNick (api: ApiInterfaceRx, accountId?: AccountId): Observable { - return additional.reduce((other: Record, [_key, _value]): Record => { - const key = dataAsString(_key); - const value = dataAsString(_value); - - if (key && value) { - other[key] = value; - } - - return other; - }, {}); -} - -function extractIdentity (identityOfOpt?: Option, superOf?: [AccountId, Data]): DeriveAccountRegistration { - if (!identityOfOpt?.isSome) { - return { judgements: [] }; - } - - const { info, judgements } = identityOfOpt.unwrap(); - const topDisplay = dataAsString(info.display); - - return { - display: (superOf && dataAsString(superOf[1])) || topDisplay, - displayParent: superOf && topDisplay, - email: dataAsString(info.email), - image: dataAsString(info.image), - judgements, - legal: dataAsString(info.legal), - other: extractOther(info.additional), - parent: superOf && superOf[0], - pgp: info.pgpFingerprint.unwrapOr(UNDEF_HEX).toHex(), - riot: dataAsString(info.riot), - twitter: dataAsString(info.twitter), - web: dataAsString(info.web) - }; -} - -function retrieveIdentity (api: ApiInterfaceRx, accountId?: AccountId): Observable { - return (( - accountId && api.query.identity?.identityOf - ? api.queryMulti([ - [api.query.identity.identityOf, accountId], - [api.query.identity.superOf, accountId] - ]) - : of([undefined, undefined]) - ) as Observable<[Option | undefined, Option> | undefined]>).pipe( - switchMap(([identityOfOpt, superOfOpt]): Observable<[Option | undefined, [AccountId, Data] | undefined]> => { - if (identityOfOpt?.isSome) { - // this identity has something set - return of([identityOfOpt, undefined]); - } else if (superOfOpt?.isSome) { - const superOf = superOfOpt.unwrap(); - - // we have a super - return combineLatest([ - api.query.identity.identityOf>(superOf[0]), - of(superOf) - ]); - } - - // nothing of value returned - return of([undefined, undefined]); - }), - map(([identityOfOpt, superOf]): DeriveAccountRegistration => - extractIdentity(identityOfOpt, superOf) - ) - ); -} - /** * @name info * @description Returns aux. info with regards to an account, current that includes the accountId, accountIndex and nickname */ -export function info (instanceId: string, api: ApiInterfaceRx): (address?: AccountIndex | AccountId | Address | string | null) => Observable { - return memo(instanceId, (address?: AccountIndex | AccountId | Address | string | null): Observable => +export function info (instanceId: string, api: ApiInterfaceRx): (address?: AccountIndex | AccountId | Address | Uint8Array | string | null) => Observable { + return memo(instanceId, (address?: AccountIndex | AccountId | Address | Uint8Array | string | null): Observable => api.derive.accounts.idAndIndex(address).pipe( switchMap(([accountId, accountIndex]): Observable<[Partial, DeriveAccountRegistration, string?]> => combineLatest([ of({ accountId, accountIndex }), - retrieveIdentity(api, accountId), + api.derive.accounts.identity(accountId), retrieveNick(api, accountId) ]) ), diff --git a/packages/api-derive/src/accounts/types.ts b/packages/api-derive/src/accounts/types.ts index 3c2d76a647..154e1f44ec 100644 --- a/packages/api-derive/src/accounts/types.ts +++ b/packages/api-derive/src/accounts/types.ts @@ -35,3 +35,9 @@ export interface DeriveAccountInfo { identity: DeriveAccountRegistration; nickname?: string; } + +export interface DeriveHasIdentity { + hasIdentity: boolean; + hasParent: boolean; + parentId: string | null; +}