UNPKG

@ledgerhq/coin-tezos

Version:
109 lines 4.53 kB
// SPDX-FileCopyrightText: © 2026 LEDGER SAS // SPDX-License-Identifier: Apache-2.0 import { log } from '@ledgerhq/logs'; import { hasManagerKey } from '../network/types'; import { createTzktApi } from '../network/tzkt'; import { partitionNativeBalance } from '../utils'; import { STAKING_UID_PREFIX } from './positionUid'; export { isDelegationPosition, isFinalizablePosition, isStakePosition, isUnstakingPosition, } from './positionUid'; export function fetchUnstakeRequests(config, address, account) { if (!hasManagerKey(account)) return Promise.resolve([]); return (account.unstakedBalance ?? 0) > 0 ? createTzktApi(config).getUnstakeRequests(address) : Promise.resolve([]); } function unstakeRequestToStake(address, req) { // TzKT's status filter is unreliable; never build a position for a finalized request. if (req.status !== 'pending' && req.status !== 'finalizable') { log('coin:tezos', 'unstakeRequestToStake: dropping non-active unstake request', { requestId: req.id, status: req.status, }); return null; } if (req.actualAmount <= 0) { log('coin:tezos', 'unstakeRequestToStake: dropping non-positive unstake request', { requestId: req.id, actualAmount: req.actualAmount, }); return null; } const createdAt = new Date(req.firstTime); if (!Number.isFinite(createdAt.getTime())) { log('coin:tezos', 'unstakeRequestToStake: dropping unstake request with invalid firstTime', { requestId: req.id, firstTime: req.firstTime, }); return null; } const isFinalizable = req.status === 'finalizable'; const prefix = isFinalizable ? STAKING_UID_PREFIX.finalizable : STAKING_UID_PREFIX.unstaking; return { uid: `${prefix}${req.id}`, address, ...(req.baker?.address && { delegate: req.baker.address }), state: isFinalizable ? 'inactive' : 'deactivating', createdAt, asset: { type: 'native' }, amount: BigInt(req.actualAmount), actions: [], }; } export function buildStakesForAccount(address, account, unstakeRequests) { const balance = BigInt(account.balance ?? 0); const stakedBalance = BigInt(account.stakedBalance ?? 0); // Account-level unstaked total (the source validateIntent's gate uses), not the summed per-request // positions below — so spendable stays gate-consistent even if a request is dropped or its fetch fails. const unstakedBalance = BigInt(account.unstakedBalance ?? 0); const delegateAddress = account.delegate?.address; const unstakeStakes = []; for (const req of unstakeRequests) { const stake = unstakeRequestToStake(address, req); if (stake) unstakeStakes.push(stake); } const stakes = []; if (delegateAddress) { const { spendable: delegated, locked } = partitionNativeBalance(balance, stakedBalance, unstakedBalance); if (locked < stakedBalance + unstakedBalance) { log('coin:tezos', 'buildStakesForAccount: balance < staked + unstaked, clamping to 0', { address, balance: balance.toString(), stakedBalance: stakedBalance.toString(), unstakedBalance: unstakedBalance.toString(), }); } stakes.push({ uid: `${STAKING_UID_PREFIX.delegation}${address}`, address, delegate: delegateAddress, state: 'active', asset: { type: 'native' }, amount: delegated, actions: [], }); } if (stakedBalance > 0n) { stakes.push({ uid: `${STAKING_UID_PREFIX.stake}${address}`, address, ...(delegateAddress && { delegate: delegateAddress }), state: 'active', asset: { type: 'native' }, amount: stakedBalance, actions: [], }); } stakes.push(...unstakeStakes); return stakes; } export async function getStakes(context, address, _cursor) { const config = await context.config(); const accountInfo = await createTzktApi(config).getAccountByAddress(address); if (!hasManagerKey(accountInfo)) return { items: [] }; const unstakeRequests = await fetchUnstakeRequests(config, address, accountInfo); return { items: buildStakesForAccount(address, accountInfo, unstakeRequests) }; } //# sourceMappingURL=getStakes.js.map