Show unbonded delegations in wallet delegation list
- In `delegate.rs`, add `delegation_node_identity` and `delegation_mixnode_is_unbonding` so missing node details emit `unbonded:{mix_id}` with `mixnode_is_unbonding: true` instead of an empty `node_identity`.
- Add `delegationListVisibility.ts` (`shouldHideDelegationFromList`, `filterVisibleDelegations`, `searchDelegations`) and wire `DelegationList.tsx` to the shared helpers.
- Update `useSortDelegations.tsx` to pin fully unbonded delegations to the top via `isFullyUnbondedDelegation`.
- In `UndelegateModal.tsx`, display `Node unbonded (mix N)` instead of raw `unbonded:{mix_id}` on the confirm screen.
- Add jest tests
- Add Rust unit test
This commit is contained in:
@@ -418,7 +418,7 @@ pub async fn get_all_mix_delegations(
|
||||
pending_events.len()
|
||||
);
|
||||
|
||||
let mixnode_is_unbonding = node_details.as_ref().map(|m| m.is_unbonding);
|
||||
let mixnode_is_unbonding = delegation_mixnode_is_unbonding(&node_details);
|
||||
log::trace!(
|
||||
" >>> node with mix_id: {} is unbonding: {:?}",
|
||||
d.mix_id,
|
||||
@@ -428,7 +428,7 @@ pub async fn get_all_mix_delegations(
|
||||
with_everything.push(DelegationWithEverything {
|
||||
owner: d.owner,
|
||||
mix_id: d.mix_id,
|
||||
node_identity: node_details.map(|m| m.node_identity).unwrap_or_default(),
|
||||
node_identity: delegation_node_identity(&node_details, d.mix_id),
|
||||
amount: d.amount,
|
||||
block_height: d.height,
|
||||
uses_vesting_contract_tokens,
|
||||
@@ -536,3 +536,41 @@ pub async fn get_delegation_summary(
|
||||
total_rewards,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn delegation_node_identity(
|
||||
node_details: &Option<NodeInformation>,
|
||||
mix_id: NodeId,
|
||||
) -> String {
|
||||
node_details
|
||||
.as_ref()
|
||||
.map(|m| m.node_identity.clone())
|
||||
.unwrap_or_else(|| format!("unbonded:{}", mix_id))
|
||||
}
|
||||
|
||||
pub(crate) fn delegation_mixnode_is_unbonding(
|
||||
node_details: &Option<NodeInformation>,
|
||||
) -> Option<bool> {
|
||||
node_details.as_ref().map(|m| m.is_unbonding).or_else(|| {
|
||||
if node_details.is_none() {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unbonded_registry_miss_uses_synthetic_identity() {
|
||||
const EXAMPLE_MIX_ID: NodeId = 1234;
|
||||
|
||||
assert_eq!(
|
||||
delegation_node_identity(&None, EXAMPLE_MIX_ID),
|
||||
"unbonded:1234"
|
||||
);
|
||||
assert_eq!(delegation_mixnode_is_unbonding(&None), Some(true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
AlertTitle,
|
||||
Box,
|
||||
Button,
|
||||
Chip,
|
||||
Collapse,
|
||||
FormControl,
|
||||
IconButton,
|
||||
@@ -30,7 +31,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { TauriLink as Link } from 'src/components/TauriLinkWrapper';
|
||||
import { format } from 'date-fns';
|
||||
import { Undelegate } from 'src/svg-icons';
|
||||
import { toPercentIntegerString } from 'src/utils';
|
||||
import { toPercentIntegerString, isFullyUnbondedDelegation, formatUnbondedNodeLabel, shouldHideDelegationFromList, searchDelegations } from 'src/utils';
|
||||
import { InfoTooltip } from '../InfoToolTip';
|
||||
import { DelegationListItemActions, DelegationsActionsMenu } from './DelegationActions';
|
||||
import { PendingDelegationCard } from './PendingDelegationCard';
|
||||
@@ -41,25 +42,7 @@ export type Order = 'asc' | 'desc';
|
||||
type AdditionalTypes = { profit_margin_percent: number; operating_cost: number };
|
||||
export type SortingKeys = keyof AdditionalTypes | keyof DelegationWithEverything;
|
||||
|
||||
const shouldBeFiltered = (item: any): boolean => {
|
||||
if (isDelegation(item)) {
|
||||
if (!item.node_identity || item.node_identity === '-' || item.node_identity === '...') {
|
||||
return true;
|
||||
}
|
||||
if (typeof item.avg_uptime_percent === 'string' && item.avg_uptime_percent === '-') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPendingDelegation(item)) {
|
||||
if ((!item.node_identity || item.node_identity === '') && item.event && item.event.kind === 'Undelegate') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
const shouldBeFiltered = (item: any): boolean => shouldHideDelegationFromList(item);
|
||||
|
||||
const SORT_FIELD_OPTIONS: { id: SortingKeys; label: string }[] = [
|
||||
{ id: 'delegated_on_iso_datetime', label: 'Delegated on' },
|
||||
@@ -132,9 +115,8 @@ export const DelegationList: FCWithChildren<{
|
||||
const searchNeedle = identityFilter.trim().toLowerCase();
|
||||
|
||||
const displayedDelegations = React.useMemo(() => {
|
||||
if (!searchNeedle) return activeDelegations;
|
||||
return activeDelegations.filter((d) => d.node_identity.toLowerCase().includes(searchNeedle));
|
||||
}, [activeDelegations, searchNeedle]);
|
||||
return searchDelegations(activeDelegations, identityFilter);
|
||||
}, [activeDelegations, identityFilter]);
|
||||
|
||||
const activeCount = activeDelegations.length;
|
||||
|
||||
@@ -407,7 +389,7 @@ export const DelegationList: FCWithChildren<{
|
||||
displayedDelegations.map((item) => {
|
||||
const rowKey = `${item.mix_id}-${item.node_identity}`;
|
||||
const isOpen = expandedKey === rowKey;
|
||||
const nodeIsUnbonded = Boolean(!item.node_identity);
|
||||
const nodeIsFullyUnbonded = isFullyUnbondedDelegation(item);
|
||||
const satNum = saturationNumeric(item);
|
||||
let satColor: 'text.secondary' | 'error.main' | 'success.main' = 'text.secondary';
|
||||
if (satNum !== undefined) {
|
||||
@@ -458,9 +440,20 @@ export const DelegationList: FCWithChildren<{
|
||||
<LockOutlined sx={{ color: 'text.secondary', fontSize: 18 }} />
|
||||
</Tooltip>
|
||||
)}
|
||||
{nodeIsUnbonded ? (
|
||||
{nodeIsFullyUnbonded ? (
|
||||
<Tooltip title={unbondedTooltip} arrow>
|
||||
<Typography color="text.secondary">-</Typography>
|
||||
<Stack direction="row" alignItems="center" spacing={0.75} sx={{ minWidth: 0 }}>
|
||||
<Chip
|
||||
label="Node unbonded"
|
||||
size="small"
|
||||
color="warning"
|
||||
variant="outlined"
|
||||
icon={<WarningAmberOutlined />}
|
||||
/>
|
||||
<Typography variant="body2" color="text.secondary" noWrap>
|
||||
{formatUnbondedNodeLabel(item.mix_id)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Link
|
||||
@@ -487,7 +480,7 @@ export const DelegationList: FCWithChildren<{
|
||||
{getRewardValue(item)}
|
||||
</TableCell>
|
||||
<TableCell align="right" sx={{ py: 1.25, whiteSpace: 'nowrap', verticalAlign: 'middle' }}>
|
||||
{!item.pending_events.length && !nodeIsUnbonded && (
|
||||
{!item.pending_events.length && !nodeIsFullyUnbonded && (
|
||||
<DelegationsActionsMenu
|
||||
onActionClick={(action) =>
|
||||
onItemActionClick ? onItemActionClick(item, action) : undefined
|
||||
@@ -496,7 +489,7 @@ export const DelegationList: FCWithChildren<{
|
||||
disableDelegateMore={item.mixnode_is_unbonding}
|
||||
/>
|
||||
)}
|
||||
{!item.pending_events.length && nodeIsUnbonded && (
|
||||
{!item.pending_events.length && nodeIsFullyUnbonded && (
|
||||
<IconButton sx={{ color: (t) => t.palette.nym.nymWallet.text.main }} size="small">
|
||||
<Undelegate
|
||||
onClick={() => (onItemActionClick ? onItemActionClick(item, 'undelegate') : undefined)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Box, SxProps } from '@mui/material';
|
||||
import { FeeDetails } from '@nymproject/types';
|
||||
import { useGetFee } from 'src/hooks/useGetFee';
|
||||
import { simulateUndelegateFromNode, simulateVestingUndelegateFromMixnode } from 'src/requests';
|
||||
import { formatDelegationNodeIdentityForDisplay } from 'src/utils';
|
||||
import { AppContext } from 'src/context';
|
||||
import { ModalFee } from '../Modals/ModalFee';
|
||||
import { ModalListItem } from '../Modals/ModalListItem';
|
||||
@@ -49,7 +50,11 @@ export const UndelegateModal: FCWithChildren<{
|
||||
backdropProps={backdropProps}
|
||||
>
|
||||
<Box sx={{ mt: 3 }}>
|
||||
<ModalListItem label="Node identity" value={identityKey || '-'} divider />
|
||||
<ModalListItem
|
||||
label="Node identity"
|
||||
value={formatDelegationNodeIdentityForDisplay(identityKey, mixId) || '-'}
|
||||
divider
|
||||
/>
|
||||
<ModalListItem label="Delegation amount" value={`${amount} ${currency.toUpperCase()}`} divider />
|
||||
<ModalFee fee={fee} isLoading={isFeeLoading} error={feeError} divider />
|
||||
<ModalListItem label=" Tokens will be transferred to account you are logged in with now" value="" divider />
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
jest.mock('src/utils', () => ({
|
||||
decCoinToDisplay: jest.fn((coin: { amount: string; denom: string }) => coin),
|
||||
}));
|
||||
|
||||
jest.mock('src/requests', () => ({
|
||||
getDelegationSummary: jest.fn(),
|
||||
getAllPendingDelegations: jest.fn(),
|
||||
}));
|
||||
|
||||
import { fetchDelegationSummaryQuery } from './delegationQuery';
|
||||
import { getAllPendingDelegations, getDelegationSummary } from 'src/requests';
|
||||
|
||||
const mockedGetDelegationSummary = getDelegationSummary as jest.MockedFunction<typeof getDelegationSummary>;
|
||||
const mockedGetAllPendingDelegations = getAllPendingDelegations as jest.MockedFunction<
|
||||
typeof getAllPendingDelegations
|
||||
>;
|
||||
|
||||
describe('fetchDelegationSummaryQuery', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('includes unbonded delegations in totals and keeps pending events on new nodes separate', async () => {
|
||||
mockedGetDelegationSummary.mockResolvedValue({
|
||||
delegations: [
|
||||
{
|
||||
mix_id: 42,
|
||||
node_identity: 'unbonded:42',
|
||||
amount: { amount: '1000000', denom: 'unym' },
|
||||
unclaimed_rewards: { amount: '50000', denom: 'unym' },
|
||||
mixnode_is_unbonding: true,
|
||||
},
|
||||
],
|
||||
total_delegations: { amount: '1000000', denom: 'unym' },
|
||||
total_rewards: { amount: '50000', denom: 'unym' },
|
||||
} as unknown as Awaited<ReturnType<typeof getDelegationSummary>>);
|
||||
|
||||
mockedGetAllPendingDelegations.mockResolvedValue([
|
||||
{
|
||||
node_identity: 'new-node-identity',
|
||||
event: { mix_id: 99, kind: 'Delegate', address: 'n1test' },
|
||||
},
|
||||
] as Awaited<ReturnType<typeof getAllPendingDelegations>>);
|
||||
|
||||
const result = await fetchDelegationSummaryQuery();
|
||||
|
||||
expect(result.delegations).toHaveLength(2);
|
||||
expect(result.delegations[0]).toMatchObject({ mix_id: 42, node_identity: 'unbonded:42' });
|
||||
expect(result.pendingDelegations).toHaveLength(1);
|
||||
expect(result.totalDelegations.amount).toBe('1000000');
|
||||
expect(result.totalRewards.amount).toBe('50000');
|
||||
expect(result.totalDelegationsAndRewards.amount).toBe('1050000.000000');
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,17 @@
|
||||
import { orderBy as _orderBy } from 'lodash';
|
||||
import { Order, SortingKeys } from 'src/components/Delegation/DelegationList';
|
||||
import { TDelegations, isDelegation } from 'src/context/delegations';
|
||||
import { isFullyUnbondedDelegation } from 'src/utils';
|
||||
|
||||
type MappedTypes = 'delegationValue' | 'operatorReward' | 'profitMarginValue' | 'operatorCostValue';
|
||||
|
||||
export const useSortDelegations = (delegationItems: TDelegations, order: Order, orderBy: SortingKeys) => {
|
||||
const unbondedDelegations = delegationItems.filter((delegation) => !delegation.node_identity);
|
||||
const delegations = delegationItems.filter((delegation) => delegation.node_identity);
|
||||
const unbondedDelegations = delegationItems.filter(
|
||||
(delegation) => isDelegation(delegation) && isFullyUnbondedDelegation(delegation),
|
||||
);
|
||||
const delegations = delegationItems.filter(
|
||||
(delegation) => !(isDelegation(delegation) && isFullyUnbondedDelegation(delegation)),
|
||||
);
|
||||
|
||||
// example of a mapped type in typescript
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
UNBONDED_NODE_IDENTITY_PREFIX,
|
||||
formatUnbondedNodeLabel,
|
||||
formatDelegationNodeIdentityForDisplay,
|
||||
isFullyUnbondedDelegation,
|
||||
isUnbondedNodeIdentity,
|
||||
} from './delegationIdentity';
|
||||
|
||||
describe('delegationIdentity', () => {
|
||||
it('treats empty identity as unbonded', () => {
|
||||
expect(isUnbondedNodeIdentity('')).toBe(true);
|
||||
expect(isUnbondedNodeIdentity(undefined)).toBe(true);
|
||||
expect(isFullyUnbondedDelegation({ node_identity: '', mixnode_is_unbonding: false })).toBe(true);
|
||||
});
|
||||
|
||||
it('treats unbonded prefix as fully unbonded', () => {
|
||||
const identity = `${UNBONDED_NODE_IDENTITY_PREFIX}42`;
|
||||
expect(isUnbondedNodeIdentity(identity)).toBe(true);
|
||||
expect(isFullyUnbondedDelegation({ node_identity: identity, mixnode_is_unbonding: false })).toBe(true);
|
||||
});
|
||||
|
||||
it('does not treat bonded identity as unbonded', () => {
|
||||
const identity = '2Abcdefghijklmnopqrstuvwxyz1234567890';
|
||||
expect(isUnbondedNodeIdentity(identity)).toBe(false);
|
||||
expect(isFullyUnbondedDelegation({ node_identity: identity, mixnode_is_unbonding: true })).toBe(false);
|
||||
});
|
||||
|
||||
it('formats unbonded node label with mix id', () => {
|
||||
expect(formatUnbondedNodeLabel(123)).toBe('Node unbonded (mix 123)');
|
||||
});
|
||||
|
||||
it('formats unbonded identity for display using mix id', () => {
|
||||
expect(formatDelegationNodeIdentityForDisplay('unbonded:42', 42)).toBe('Node unbonded (mix 42)');
|
||||
expect(formatDelegationNodeIdentityForDisplay('2Abcdefghijklmnopqrstuvwxyz1234567890', 42)).toBe(
|
||||
'2Abcdefghijklmnopqrstuvwxyz1234567890',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import type { DelegationWithEverything } from '@nymproject/types';
|
||||
|
||||
export const UNBONDED_NODE_IDENTITY_PREFIX = 'unbonded:';
|
||||
|
||||
export function isUnbondedNodeIdentity(nodeIdentity: string | undefined | null): boolean {
|
||||
if (!nodeIdentity) {
|
||||
return true;
|
||||
}
|
||||
return nodeIdentity.startsWith(UNBONDED_NODE_IDENTITY_PREFIX);
|
||||
}
|
||||
|
||||
export function isFullyUnbondedDelegation(
|
||||
item: Pick<DelegationWithEverything, 'node_identity' | 'mixnode_is_unbonding'>,
|
||||
): boolean {
|
||||
return isUnbondedNodeIdentity(item.node_identity);
|
||||
}
|
||||
|
||||
export function formatUnbondedNodeLabel(mixId: number): string {
|
||||
return `Node unbonded (mix ${mixId})`;
|
||||
}
|
||||
|
||||
export function formatDelegationNodeIdentityForDisplay(nodeIdentity: string, mixId: number): string {
|
||||
if (isUnbondedNodeIdentity(nodeIdentity)) {
|
||||
return formatUnbondedNodeLabel(mixId);
|
||||
}
|
||||
return nodeIdentity;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import type { DelegationWithEverything, WrappedDelegationEvent } from '@nymproject/types';
|
||||
import { isFullyUnbondedDelegation } from './delegationIdentity';
|
||||
|
||||
export type DelegationListItem = DelegationWithEverything | WrappedDelegationEvent;
|
||||
|
||||
const isPendingDelegationItem = (delegation: DelegationListItem): delegation is WrappedDelegationEvent =>
|
||||
'event' in delegation;
|
||||
|
||||
const isDelegationItem = (delegation: DelegationListItem): delegation is DelegationWithEverything =>
|
||||
'owner' in delegation;
|
||||
|
||||
export function shouldHideDelegationFromList(item: DelegationListItem): boolean {
|
||||
if (isDelegationItem(item)) {
|
||||
if (!item.node_identity || item.node_identity === '-' || item.node_identity === '...') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPendingDelegationItem(item)) {
|
||||
if ((!item.node_identity || item.node_identity === '') && item.event && item.event.kind === 'Undelegate') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function filterVisibleDelegations(items: DelegationListItem[]): DelegationListItem[] {
|
||||
return items.filter((item) => !shouldHideDelegationFromList(item));
|
||||
}
|
||||
|
||||
export function searchDelegations(
|
||||
delegations: DelegationWithEverything[],
|
||||
searchNeedle: string,
|
||||
): DelegationWithEverything[] {
|
||||
const needle = searchNeedle.trim().toLowerCase();
|
||||
if (!needle) {
|
||||
return delegations;
|
||||
}
|
||||
return delegations.filter(
|
||||
(d) => d.node_identity.toLowerCase().includes(needle) || String(d.mix_id).includes(needle),
|
||||
);
|
||||
}
|
||||
|
||||
export function isUndelegateOnlyDelegation(item: DelegationWithEverything): boolean {
|
||||
return isFullyUnbondedDelegation(item);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
export * from './common';
|
||||
export * from './delegationIdentity';
|
||||
export * from './delegationListVisibility';
|
||||
export * from './fireRequests';
|
||||
export * from './console';
|
||||
export { default as fireRequests } from './fireRequests';
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import type { DelegationWithEverything } from '@nymproject/types';
|
||||
import { formatDelegationNodeIdentityForDisplay } from './delegationIdentity';
|
||||
import {
|
||||
filterVisibleDelegations,
|
||||
isUndelegateOnlyDelegation,
|
||||
searchDelegations,
|
||||
shouldHideDelegationFromList,
|
||||
} from './delegationListVisibility';
|
||||
import {
|
||||
EXAMPLE_HISTORICAL_NODE_IDENTITY,
|
||||
EXAMPLE_UNBONDED_MIX_ID,
|
||||
buildFixedUnbondedWalletDelegation,
|
||||
buildLegacyHiddenUnbondedWalletDelegation,
|
||||
} from './unbondedDelegation.fixture';
|
||||
|
||||
describe('unbonded delegation wallet visibility acceptance', () => {
|
||||
it('hides the pre-fix wallet row with empty node_identity', () => {
|
||||
const legacyRow = buildLegacyHiddenUnbondedWalletDelegation();
|
||||
|
||||
expect(legacyRow.mix_id).toBe(EXAMPLE_UNBONDED_MIX_ID);
|
||||
expect(legacyRow.node_identity).toBe('');
|
||||
expect(shouldHideDelegationFromList(legacyRow)).toBe(true);
|
||||
expect(filterVisibleDelegations([legacyRow])).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows the post-fix wallet row for the same on-chain delegation', () => {
|
||||
const fixedRow = buildFixedUnbondedWalletDelegation();
|
||||
|
||||
expect(fixedRow.node_identity).toBe(`unbonded:${EXAMPLE_UNBONDED_MIX_ID}`);
|
||||
expect(fixedRow.mixnode_is_unbonding).toBe(true);
|
||||
expect(shouldHideDelegationFromList(fixedRow)).toBe(false);
|
||||
expect(filterVisibleDelegations([fixedRow])).toHaveLength(1);
|
||||
expect(isUndelegateOnlyDelegation(fixedRow)).toBe(true);
|
||||
});
|
||||
|
||||
it('does not hide the row when uptime is missing', () => {
|
||||
const fixedRow = buildFixedUnbondedWalletDelegation();
|
||||
|
||||
expect(fixedRow.avg_uptime_percent).toBeNull();
|
||||
expect(shouldHideDelegationFromList(fixedRow)).toBe(false);
|
||||
});
|
||||
|
||||
it('finds the row by mix_id search when historical identity is unavailable', () => {
|
||||
const fixedRow = buildFixedUnbondedWalletDelegation();
|
||||
const visible = filterVisibleDelegations([fixedRow]) as DelegationWithEverything[];
|
||||
|
||||
expect(searchDelegations(visible, String(EXAMPLE_UNBONDED_MIX_ID))).toHaveLength(1);
|
||||
expect(searchDelegations(visible, EXAMPLE_HISTORICAL_NODE_IDENTITY)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('formats undelegate confirmation copy without exposing the synthetic prefix', () => {
|
||||
const fixedRow = buildFixedUnbondedWalletDelegation();
|
||||
|
||||
expect(formatDelegationNodeIdentityForDisplay(fixedRow.node_identity, fixedRow.mix_id)).toBe(
|
||||
`Node unbonded (mix ${EXAMPLE_UNBONDED_MIX_ID})`,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { DelegationWithEverything } from '@nymproject/types';
|
||||
import { UNBONDED_NODE_IDENTITY_PREFIX } from './delegationIdentity';
|
||||
|
||||
/** Synthetic mix_id used in wallet unbonded-delegation tests. */
|
||||
export const EXAMPLE_UNBONDED_MIX_ID = 1234;
|
||||
|
||||
/** Example bonded-node identity (registry miss => wallet uses unbonded prefix instead). */
|
||||
export const EXAMPLE_HISTORICAL_NODE_IDENTITY = '2ExampleHistoricalNodeIdentityKey00000000000001';
|
||||
|
||||
export const EXAMPLE_DELEGATOR_ADDRESS = 'n1exampledelegator00000000000000000000000';
|
||||
|
||||
type UnbondedWalletDelegationOptions = {
|
||||
mixId?: number;
|
||||
amount?: string;
|
||||
blockHeight?: bigint;
|
||||
owner?: string;
|
||||
};
|
||||
|
||||
export function buildFixedUnbondedWalletDelegation(
|
||||
options: UnbondedWalletDelegationOptions = {},
|
||||
): DelegationWithEverything {
|
||||
const mixId = options.mixId ?? EXAMPLE_UNBONDED_MIX_ID;
|
||||
|
||||
return {
|
||||
mix_id: mixId,
|
||||
node_identity: `${UNBONDED_NODE_IDENTITY_PREFIX}${mixId}`,
|
||||
amount: { amount: options.amount ?? '1000000', denom: 'nym' },
|
||||
owner: options.owner ?? EXAMPLE_DELEGATOR_ADDRESS,
|
||||
block_height: options.blockHeight ?? BigInt(1_000_000),
|
||||
delegated_on_iso_datetime: null,
|
||||
unclaimed_rewards: null,
|
||||
stake_saturation: null,
|
||||
avg_uptime_percent: null,
|
||||
accumulated_by_delegates: null,
|
||||
accumulated_by_operator: null,
|
||||
cost_params: null,
|
||||
uses_vesting_contract_tokens: false,
|
||||
pending_events: [],
|
||||
mixnode_is_unbonding: true,
|
||||
errors: null,
|
||||
};
|
||||
}
|
||||
|
||||
/** Pre-fix wallet backend shape when node registry lookup returned none. */
|
||||
export function buildLegacyHiddenUnbondedWalletDelegation(
|
||||
options: UnbondedWalletDelegationOptions = {},
|
||||
): DelegationWithEverything {
|
||||
return {
|
||||
...buildFixedUnbondedWalletDelegation(options),
|
||||
node_identity: '',
|
||||
mixnode_is_unbonding: null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user