UNPKG

@ledgerhq/coin-tezos

Version:
128 lines (111 loc) 3.69 kB
// SPDX-FileCopyrightText: © 2026 LEDGER SAS // SPDX-License-Identifier: Apache-2.0 import type { TezosCoinConfig } from '../config' import type { Baker } from '../types' import { makeLRUCache } from '@ledgerhq/live-network/cache' import network from '@ledgerhq/live-network/network' import { log } from '@ledgerhq/logs' import { ledgerValidatorAddress } from './bakers.whitelist-default' export type TezosApiBaker = { address: string name: string status: string balance: number features: TezosBakerFeature[] delegation: { enabled: boolean minBalance: number fee: number capacity: number freeSpace: number estimatedApy: number features: TezosBakerFeature[] } staking: { enabled: boolean minBalance: number fee: number capacity: number freeSpace: number estimatedApy: number features: TezosBakerFeature[] } } export type TezosBakerFeature = { title: string content: string | unknown } export const cache = makeLRUCache( async (config: TezosCoinConfig): Promise<Baker[]> => { const bakers: Baker[] = [] const TEZOS_API_BASE_URL = config.baker.url const { data } = await network<TezosApiBaker[]>({ url: `${TEZOS_API_BASE_URL}/v3/bakers`, }) if (data && Array.isArray(data)) { log('tezos/bakers', 'found ' + data.length + ' bakers') data .filter((raw) => raw.status === 'active' && raw.delegation.enabled) .forEach((raw) => { const baker = asBaker(raw) if (baker) { bakers.push(baker) } }) bakers.sort((a, b) => (a.name > b.name ? 1 : -1)) } const ledgerBakerIndex = bakers.findIndex((baker) => baker.address === ledgerValidatorAddress) const [ledgerBaker] = bakers.splice(ledgerBakerIndex, 1) log('tezos/bakers', 'loaded ' + bakers.length + ' bakers') return [ledgerBaker, ...bakers] }, (config: TezosCoinConfig) => config.baker.url ) let _lastBakers: Baker[] = [] export const fetchAllBakers = async (config: TezosCoinConfig): Promise<Baker[]> => { const allBakers = await cache.force(config) _lastBakers = allBakers return allBakers } function whitelist(all: Baker[], addresses: string[]) { return all.filter((baker) => addresses.includes(baker.address)) } export const listBakersWithDefault = (whitelistAddresses: string[]): Baker[] => { return whitelist(_lastBakers, whitelistAddresses) } export const listBakers = async ( config: TezosCoinConfig, whitelistAddresses: string[] ): Promise<Baker[]> => { _lastBakers = await cache(config) return whitelist(_lastBakers, whitelistAddresses) } export function getBakerSync(addr: string): Baker | undefined { return _lastBakers.find((baker) => baker.address === addr) } export async function loadBaker(config: TezosCoinConfig, addr: string): Promise<Baker | undefined> { const cacheBaker = getBakerSync(addr) if (cacheBaker) return cacheBaker const bakers = await cache(config) const baker = bakers.find((baker) => baker.address === addr) return baker } export const asBaker = (data: TezosApiBaker): Baker | undefined => { const { address, name, delegation } = data if ( typeof name === 'string' && typeof address === 'string' && typeof delegation.freeSpace === 'number' && typeof delegation.estimatedApy === 'number' && delegation.estimatedApy >= 0 && delegation.estimatedApy <= 1 ) { return { name, address, logoURL: `https://services.tzkt.io/v1/avatars/${address}`, nominalYield: `${Math.floor(10000 * delegation.estimatedApy) / 100} %`, capacityStatus: delegation.freeSpace <= 0 ? 'full' : 'normal', } } }