From 98621a4297955d4be75a9be30906c776ea614dd6 Mon Sep 17 00:00:00 2001 From: Thorianite <100335276+Thorian1te@users.noreply.github.com> Date: Fri, 31 May 2024 15:17:18 +0930 Subject: [PATCH] Disabled users from unbonding when node is active (#267) * Disabled users from unbonding when node is active * case for sign_membership & in standyby mode: --- CHANGELOG.md | 3 +- package.json | 2 +- .../components/Bonds/Bonds.stories.tsx | 3 +- .../Bonds/table/BondsTable.stories.tsx | 3 +- .../components/Bonds/table/BondsTable.tsx | 14 ++-- .../txs/interact/Interact.helpers.test.ts | 71 ++++++++++++++++++- .../wallet/txs/interact/Interact.helpers.ts | 10 ++- .../txs/interact/InteractForm.stories.tsx | 21 +++++- .../wallet/txs/interact/InteractFormThor.tsx | 29 ++++++-- src/renderer/i18n/de/bonds.ts | 3 +- src/renderer/i18n/en/bonds.ts | 3 +- src/renderer/i18n/es/bonds.ts | 3 +- src/renderer/i18n/fr/bonds.ts | 3 +- src/renderer/i18n/hi/bonds.ts | 3 +- src/renderer/i18n/ru/bonds.ts | 3 +- src/renderer/i18n/types.ts | 1 + src/renderer/services/mayachain/mayanode.ts | 5 +- src/renderer/services/mayachain/types.ts | 1 + src/renderer/services/thorchain/thornode.ts | 5 +- src/renderer/services/thorchain/types.ts | 1 + .../wallet/Interact/InteractViewTHOR.tsx | 27 ++++++- 21 files changed, 183 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b06656f1a..89f4cfde7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ - Fix UTXO send price fetch [256](https://github.com/asgardex/asgardex-desktop/pull/257) - Fix Maya lp Bugs & BSC.USDT [#255](https://github.com/asgardex/asgardex-desktop/pull/258) -- Update Thornode Leedger to use ClientLedger from xchainjs [#259](https://github.com/asgardex/asgardex-desktop/pull/260) +- Update Thornode Ledger to use ClientLedger from xchainjs [#259](https://github.com/asgardex/asgardex-desktop/pull/260) +- Disable Unbonding if node is active [#233] (https://github.com/asgardex/asgardex-desktop/pull/263) # 1.21.7 (2024-05-23) diff --git a/package.json b/package.json index a5e5066b5..97510462f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "asgardex", "productName": "ASGARDEX", - "version": "1.21.7", + "version": "1.21.8", "description": "WALLET AND EXCHANGE CLIENT FOR THORCHAIN", "main": "index.js", "scripts": { diff --git a/src/renderer/components/Bonds/Bonds.stories.tsx b/src/renderer/components/Bonds/Bonds.stories.tsx index 1af92dcad..acd01cff5 100644 --- a/src/renderer/components/Bonds/Bonds.stories.tsx +++ b/src/renderer/components/Bonds/Bonds.stories.tsx @@ -16,7 +16,8 @@ const mockNodeInfo = (address: Address) => ({ bondProviders: { nodeOperatorFee: baseAmount(100000000 * 400000), providers: [] - } + }, + signMembership: [] }) const addressValidation: AddressValidation = (_) => true diff --git a/src/renderer/components/Bonds/table/BondsTable.stories.tsx b/src/renderer/components/Bonds/table/BondsTable.stories.tsx index ce5be549e..90daf2176 100644 --- a/src/renderer/components/Bonds/table/BondsTable.stories.tsx +++ b/src/renderer/components/Bonds/table/BondsTable.stories.tsx @@ -15,7 +15,8 @@ const mockNodeInfo = (address: Address) => ({ bondProviders: { nodeOperatorFee: baseAmount(100000000 * 400000), providers: [] - } + }, + signMembership: [] }) export const Default: Story = () => { diff --git a/src/renderer/components/Bonds/table/BondsTable.tsx b/src/renderer/components/Bonds/table/BondsTable.tsx index 0febc9cb7..6aa8d9931 100644 --- a/src/renderer/components/Bonds/table/BondsTable.tsx +++ b/src/renderer/components/Bonds/table/BondsTable.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useMemo, useState } from 'react' import { Network } from '@xchainjs/xchain-client' +import type * as TN from '@xchainjs/xchain-thornode' import { Address, baseAmount, BaseAmount } from '@xchainjs/xchain-util' import { ColumnType } from 'antd/lib/table' import * as FP from 'fp-ts/function' @@ -30,6 +31,7 @@ interface CustomExpandIconProps { onExpand: (record: string, event: React.MouseEvent) => void // Adjusted for // Adjust YourRecordType accordingly record: string } +type ProvidersWithStatus = Providers & { status: TN.NodeStatusEnum; signMembership: string[]; nodeAddress: Address } export const BondsTable: React.FC = ({ nodes, @@ -100,7 +102,7 @@ export const BondsTable: React.FC = ({ [intl, network, goToNode] ) - const expandedTableColumns: ColumnType[] = [ + const expandedTableColumns: ColumnType[] = [ { title: intl.formatMessage({ id: 'bonds.bondProvider' }), dataIndex: 'bondAddress', @@ -111,11 +113,12 @@ export const BondsTable: React.FC = ({ title: intl.formatMessage({ id: 'common.action' }), dataIndex: 'action', key: 'action', - render: (_, { bondAddress, bond }) => { + render: (_, record) => { + const { bondAddress, bond, status, signMembership, nodeAddress } = record const isWalletAddress = walletAddresses.THOR.some((walletInfo) => walletInfo.address === bondAddress) || walletAddresses.MAYA.some((walletInfo) => walletInfo.address === bondAddress) - + const unbondDisabled = status === 'Active' || (signMembership.includes(nodeAddress) && status === 'Standby') return (
= ({
goToAction('unbond', matchedNodeAddress, bond)}> {intl.formatMessage({ id: 'deposit.interact.actions.unbond' })} @@ -244,6 +247,9 @@ export const BondsTable: React.FC = ({ dataSource={record.bondProviders.providers.map((provider: Providers, index: string) => ({ bondAddress: provider.bondAddress, bond: provider.bond, + status: record.status, + member: record.signMembership, + nodeAddres: record.nodeAddress, key: `${record.address}-${index}` }))} pagination={false} diff --git a/src/renderer/components/wallet/txs/interact/Interact.helpers.test.ts b/src/renderer/components/wallet/txs/interact/Interact.helpers.test.ts index bbc26494b..684efd9e2 100644 --- a/src/renderer/components/wallet/txs/interact/Interact.helpers.test.ts +++ b/src/renderer/components/wallet/txs/interact/Interact.helpers.test.ts @@ -1,11 +1,14 @@ -import { bn } from '@xchainjs/xchain-util' +import type * as TN from '@xchainjs/xchain-thornode' +import { baseAmount, bn } from '@xchainjs/xchain-util' import * as O from 'fp-ts/lib/Option' +import { NodeInfo } from '../../../../services/thorchain/types' import { getInteractTypeFromNullableString, isInteractType, validateCustomAmountInput, - validateUnboundAmountInput + validateUnboundAmountInput, + findNodeIndex } from './Interact.helpers' describe('wallet/interact/helpers', () => { @@ -130,4 +133,68 @@ describe('wallet/interact/helpers', () => { expect(result).toBeNone() }) }) + + describe('findNodeIndex', () => { + const nodes: NodeInfo[] = [ + { + address: 'thor10czf2s89h79fsjmqqck85cdqeq536hw5ngz4lt', + bond: baseAmount(100000000 * 40000000), + award: baseAmount(100000000 * 400000), + status: 'Active' as TN.NodeStatusEnum, + bondProviders: { providers: [], nodeOperatorFee: baseAmount(100000000 * 400000) }, // Mock bondProviders + signMembership: [] + }, + { + address: 'thor16ery22gma35h2fduxr0swdfvz4s6yvy6yhskf6', + bond: baseAmount(100000000 * 40000000), // Mock bond value + award: baseAmount(100000000 * 400000), // Mock award value + status: 'Standby' as TN.NodeStatusEnum, + bondProviders: { providers: [], nodeOperatorFee: baseAmount(100000000 * 400000) }, // Mock bondProviders + signMembership: ['thor16ery22gma35h2fduxr0swdfvz4s6yvy6yhskf6'] + }, + { + address: 'thor13uy6szawgsj9xjs0gq2xddzmcup3zl63khp6gq', + bond: baseAmount(100000000 * 40000000), // Mock bond value + award: baseAmount(100000000 * 400000), // Mock award value + status: 'Standby' as TN.NodeStatusEnum, + bondProviders: { providers: [], nodeOperatorFee: baseAmount(100000000 * 400000) }, // Mock bondProviders + signMembership: [] + } + ] + + it('should find an active node', () => { + const result = findNodeIndex(nodes, 'thor10czf2s89h79fsjmqqck85cdqeq536hw5ngz4lt') + expect(result).toEqual(0) + }) + + it('should find a standby node with the address in signMembership', () => { + const result = findNodeIndex(nodes, 'thor16ery22gma35h2fduxr0swdfvz4s6yvy6yhskf6') + expect(result).toEqual(1) + }) + + it('should not find a node if the address does not match any active or standby nodes', () => { + const result = findNodeIndex(nodes, 'thor1invalidaddress1234567890') + expect(result).toEqual(-1) + }) + + it('should not find a node if the address matches a standby node but is not in signMembership', () => { + const result = findNodeIndex(nodes, 'thor18df3c5lhdskfsjmqqck85cdqeq536hw5ngz4lt') + expect(result).toEqual(-1) + }) + + it('should not find a node if the status does not match active or standby', () => { + const modifiedNodes = [ + { + address: 'thor1nprw0w6ex8xh4tfl3vtkhqnjvds68kwshq9ax9', + bond: baseAmount(100000000 * 40000000), // Mock bond value + award: baseAmount(100000000 * 400000), // Mock award value + status: 'Disabled' as TN.NodeStatusEnum, + bondProviders: { providers: [], nodeOperatorFee: baseAmount(100000000 * 400000) }, // Mock bondProviders + signMembership: [] + } + ] + const result = findNodeIndex(modifiedNodes, 'thor1nprw0w6ex8xh4tfl3vtkhqnjvds68kwshq9ax9') + expect(result).toEqual(-1) + }) + }) }) diff --git a/src/renderer/components/wallet/txs/interact/Interact.helpers.ts b/src/renderer/components/wallet/txs/interact/Interact.helpers.ts index 73de8d67e..b5bceac8d 100644 --- a/src/renderer/components/wallet/txs/interact/Interact.helpers.ts +++ b/src/renderer/components/wallet/txs/interact/Interact.helpers.ts @@ -9,7 +9,7 @@ import { IntlShape } from 'react-intl' import { optionFromNullableString } from '../../../../../shared/utils/fp' import { greaterThan, greaterThanEqualTo, validateBN } from '../../../../helpers/form/validation' import { emptyString } from '../../../../helpers/stringHelper' -import { InteractState } from '../../../../services/thorchain/types' +import { InteractState, NodeInfos } from '../../../../services/thorchain/types' import { InteractType } from './Interact.types' export const getInteractiveDescription = ({ state, intl }: { state: InteractState; intl: IntlShape }): string => { @@ -89,3 +89,11 @@ export const isInteractType = (u: unknown): u is InteractType => export const getInteractTypeFromNullableString = (s?: string): O.Option => FP.pipe(s, optionFromNullableString, O.chain(O.fromPredicate(isInteractType))) + +export const findNodeIndex = (nodes: NodeInfos, inputaddress: string) => { + return nodes.findIndex( + ({ address, status, signMembership }) => + (address.toLowerCase() === inputaddress && status === 'Active') || + (signMembership.includes(inputaddress) && status === 'Standby') + ) +} diff --git a/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx b/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx index 48cc2709d..30b7eea36 100644 --- a/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx +++ b/src/renderer/components/wallet/txs/interact/InteractForm.stories.tsx @@ -1,6 +1,10 @@ +import { useState } from 'react' + +import * as RD from '@devexperts/remote-data-ts' import { Meta } from '@storybook/react' import { Network, TxHash } from '@xchainjs/xchain-client' -import { assetAmount, assetToBase, BaseAmount, baseAmount } from '@xchainjs/xchain-util' +import { NodeStatusEnum } from '@xchainjs/xchain-thornode' +import { Address, assetAmount, assetToBase, BaseAmount, baseAmount } from '@xchainjs/xchain-util' import * as FP from 'fp-ts/lib/function' import * as O from 'fp-ts/lib/Option' import * as Rx from 'rxjs' @@ -25,6 +29,18 @@ type Args = { walletType: WalletType } +const mockNodeInfo = (address: Address) => ({ + bond: baseAmount(100000000 * 40000000), + award: baseAmount(100000000 * 400000), + status: NodeStatusEnum.Active, + address, + bondProviders: { + nodeOperatorFee: baseAmount(100000000 * 400000), + providers: [] + }, + signMembership: [] +}) + const Template = ({ interactType, txRDStatus, feeRDStatus, balance, validAddress, walletType }: Args) => { const interact$: InteractStateHandler = (_) => { const getCurrentStep = () => { @@ -55,7 +71,7 @@ const Template = ({ interactType, txRDStatus, feeRDStatus, balance, validAddress }) } const { thorchainQuery } = useThorchainQueryContext() - + const [nodesList] = useState([]) const feeRD: FeeRD = FP.pipe( feeRDStatus, getMockRDValueFactory( @@ -90,6 +106,7 @@ const Template = ({ interactType, txRDStatus, feeRDStatus, balance, validAddress poolDetails={[]} nodeAddress="" bondAmount="" + nodes={RD.success(nodesList.map((address) => mockNodeInfo(address)))} /> ) } diff --git a/src/renderer/components/wallet/txs/interact/InteractFormThor.tsx b/src/renderer/components/wallet/txs/interact/InteractFormThor.tsx index 947e0efa1..2627bcc59 100644 --- a/src/renderer/components/wallet/txs/interact/InteractFormThor.tsx +++ b/src/renderer/components/wallet/txs/interact/InteractFormThor.tsx @@ -39,7 +39,7 @@ import { useSubscriptionState } from '../../../../hooks/useSubscriptionState' import { FeeRD } from '../../../../services/chain/types' import { AddressValidation, GetExplorerTxUrl, OpenExplorerTxUrl } from '../../../../services/clients' import { INITIAL_INTERACT_STATE } from '../../../../services/thorchain/const' -import { InteractState, InteractStateHandler } from '../../../../services/thorchain/types' +import { InteractState, InteractStateHandler, NodeInfos, NodeInfosRD } from '../../../../services/thorchain/types' import { ValidatePasswordHandler, WalletBalance } from '../../../../services/wallet/types' import { LedgerConfirmationModal, WalletPasswordConfirmationModal } from '../../../modal/confirmation' import { TxModal } from '../../../modal/tx' @@ -88,6 +88,7 @@ type Props = { poolDetails: PoolDetails nodeAddress: string | null bondAmount: string | null + nodes: NodeInfosRD } export const InteractFormThor: React.FC = (props) => { const { @@ -107,7 +108,8 @@ export const InteractFormThor: React.FC = (props) => { thorchainQuery, network, nodeAddress, - bondAmount + bondAmount, + nodes: nodesRD } = props const intl = useIntl() @@ -118,6 +120,14 @@ export const InteractFormThor: React.FC = (props) => { const [_amountToSend, setAmountToSend] = useState(ZERO_BASE_AMOUNT) + const nodes: NodeInfos = useMemo( + () => + FP.pipe( + nodesRD, + RD.getOrElse(() => [] as NodeInfos) + ), + [nodesRD] + ) const [memo, setMemo] = useState('') const amountToSend = useMemo(() => { switch (interactType) { @@ -374,8 +384,13 @@ export const InteractFormThor: React.FC = (props) => { ) const addressValidator = useCallback( - async (_: unknown, value: string) => - FP.pipe( + async (_: unknown, value: string) => { + const inputAddres = value.toLowerCase() + const nodeIndex = H.findNodeIndex(nodes, inputAddres) + if (interactType === 'unbond' && nodeIndex > -1) { + return Promise.reject(intl.formatMessage({ id: 'bonds.validations.bondStatusActive' })) + } + return FP.pipe( value, validateAddress( addressValidation, @@ -386,10 +401,10 @@ export const InteractFormThor: React.FC = (props) => { (e) => Promise.reject(e), () => Promise.resolve() ) - ), - [addressValidation, intl] + ) + }, + [addressValidation, interactType, intl, nodes] ) - // Send tx start time const [sendTxStartTime, setSendTxStartTime] = useState(0) diff --git a/src/renderer/i18n/de/bonds.ts b/src/renderer/i18n/de/bonds.ts index 21bdb4d7c..46b48f51a 100644 --- a/src/renderer/i18n/de/bonds.ts +++ b/src/renderer/i18n/de/bonds.ts @@ -15,7 +15,8 @@ const bonds: BondsMessages = { 'bonds.node.add': 'Node hinzufügen', 'bonds.node.enterMessage': 'Node-Adresse eingeben', 'bonds.validations.nodeAlreadyAdded': 'Node wurde bereits hinzugefügt', - 'bonds.node.removeMessage': 'Bist du sicher, dass du die Node {node} entfernen möchtest?' + 'bonds.node.removeMessage': 'Bist Du sicher, dass Du die Node {node} entfernen möchtest?', + 'bonds.validations.bondStatusActive': 'Das Abtrennen von einem aktiven Knoten ist nicht erlaubt' } export default bonds diff --git a/src/renderer/i18n/en/bonds.ts b/src/renderer/i18n/en/bonds.ts index c36ae4ac7..d2ce381bd 100644 --- a/src/renderer/i18n/en/bonds.ts +++ b/src/renderer/i18n/en/bonds.ts @@ -15,7 +15,8 @@ const bonds: BondsMessages = { 'bonds.node.add': 'Add node', 'bonds.node.enterMessage': 'Enter node to monitor', 'bonds.validations.nodeAlreadyAdded': 'Node is already added', - 'bonds.node.removeMessage': 'Are you sure you want to remove node with address {node} ?' + 'bonds.node.removeMessage': 'Are you sure you want to remove node with address {node} ?', + 'bonds.validations.bondStatusActive': 'Unbonding from an active node is not allowed' } export default bonds diff --git a/src/renderer/i18n/es/bonds.ts b/src/renderer/i18n/es/bonds.ts index 559654d33..22d1a5f4c 100644 --- a/src/renderer/i18n/es/bonds.ts +++ b/src/renderer/i18n/es/bonds.ts @@ -15,7 +15,8 @@ const bonds: BondsMessages = { 'bonds.node.add': 'Añadir nodo', 'bonds.node.enterMessage': 'Introduzca el nodo a supervisar', 'bonds.validations.nodeAlreadyAdded': 'Nodo ya añadido', - 'bonds.node.removeMessage': '¿Estás seguro de que quieres eliminar el nodo con dirección {node} ?' + 'bonds.node.removeMessage': '¿Estás seguro de que quieres eliminar el nodo con dirección {node} ?', + 'bonds.validations.bondStatusActive': 'Desvincularse de un nodo activo no está permitido' } export default bonds diff --git a/src/renderer/i18n/fr/bonds.ts b/src/renderer/i18n/fr/bonds.ts index 00437ae39..e1718fb8a 100644 --- a/src/renderer/i18n/fr/bonds.ts +++ b/src/renderer/i18n/fr/bonds.ts @@ -15,7 +15,8 @@ const bonds: BondsMessages = { 'bonds.node.add': 'Ajouter un nœud', 'bonds.node.enterMessage': 'Entrez le nœud à surveiller', 'bonds.validations.nodeAlreadyAdded': 'Le nœud est déjà ajouté', - 'bonds.node.removeMessage': 'Êtes-vous sûr de vouloir supprimer le nœud {node} ?' + 'bonds.node.removeMessage': 'Êtes-vous sûr de vouloir supprimer le nœud {node} ?', + 'bonds.validations.bondStatusActive': "La déliaison d'un nœud actif n'est pas autorisée" } export default bonds diff --git a/src/renderer/i18n/hi/bonds.ts b/src/renderer/i18n/hi/bonds.ts index 6e84d63e4..e2e25a680 100644 --- a/src/renderer/i18n/hi/bonds.ts +++ b/src/renderer/i18n/hi/bonds.ts @@ -15,7 +15,8 @@ const bonds: BondsMessages = { 'bonds.node.add': 'नोड जोड़ें', 'bonds.node.enterMessage': 'ट्रैकिंग के लिए नोड दर्ज करें', 'bonds.validations.nodeAlreadyAdded': 'नोड पहले से जोड़ा जा चुका है', - 'bonds.node.removeMessage': 'क्या आप वाकई में नोड {node} को हटाना चाहते हैं?' + 'bonds.node.removeMessage': 'क्या आप वाकई में नोड {node} को हटाना चाहते हैं?', + 'bonds.validations.bondStatusActive': 'सक्रिय नोड से अनबॉन्डिंग की अनुमति नहीं है' } export default bonds diff --git a/src/renderer/i18n/ru/bonds.ts b/src/renderer/i18n/ru/bonds.ts index 45ce2fa28..ef0edb218 100644 --- a/src/renderer/i18n/ru/bonds.ts +++ b/src/renderer/i18n/ru/bonds.ts @@ -15,7 +15,8 @@ const bonds: BondsMessages = { 'bonds.node.add': 'Добавить узел', 'bonds.node.enterMessage': 'Введите узел для отслеживания', 'bonds.validations.nodeAlreadyAdded': 'Узел уже добавлен', - 'bonds.node.removeMessage': 'Вы уверены, что хотите удалить узел {node} ?' + 'bonds.node.removeMessage': 'Вы уверены, что хотите удалить узел {node} ?', + 'bonds.validations.bondStatusActive': 'Развязывание с активным узлом не допускается' } export default bonds diff --git a/src/renderer/i18n/types.ts b/src/renderer/i18n/types.ts index c20882d59..a26f6f2ae 100644 --- a/src/renderer/i18n/types.ts +++ b/src/renderer/i18n/types.ts @@ -329,6 +329,7 @@ type BondsMessageKey = | 'bonds.node.removeMessage' | 'bonds.bondProvider' | 'bonds.validations.nodeAlreadyAdded' + | 'bonds.validations.bondStatusActive' export type BondsMessages = { [key in BondsMessageKey]: string } diff --git a/src/renderer/services/mayachain/mayanode.ts b/src/renderer/services/mayachain/mayanode.ts index f3872c674..8b48ac1e6 100644 --- a/src/renderer/services/mayachain/mayanode.ts +++ b/src/renderer/services/mayachain/mayanode.ts @@ -208,7 +208,7 @@ export const createMayanodeService$ = (network$: Network$, clientUrl$: ClientUrl liveData.map((nodes) => FP.pipe( nodes, - A.map(({ bond, reward, status, node_address, bond_providers }) => { + A.map(({ bond, reward, status, node_address, bond_providers, signer_membership }) => { return { address: node_address, bond: baseAmount(bond, CACAO_DECIMAL), @@ -222,7 +222,8 @@ export const createMayanodeService$ = (network$: Network$, clientUrl$: ClientUrl bond: baseAmount(provider.bond, CACAO_DECIMAL) })) : [] - } + }, + signMembership: signer_membership } }) ) diff --git a/src/renderer/services/mayachain/types.ts b/src/renderer/services/mayachain/types.ts index e00d94701..0508219a1 100644 --- a/src/renderer/services/mayachain/types.ts +++ b/src/renderer/services/mayachain/types.ts @@ -126,6 +126,7 @@ export type NodeInfo = { award: BaseAmount status: TN.NodeStatusEnum bondProviders: BondProviders + signMembership: string[] } export type NodeInfoLD = LiveData diff --git a/src/renderer/services/thorchain/thornode.ts b/src/renderer/services/thorchain/thornode.ts index c373f6172..2126bfb8c 100644 --- a/src/renderer/services/thorchain/thornode.ts +++ b/src/renderer/services/thorchain/thornode.ts @@ -281,7 +281,7 @@ export const createThornodeService$ = (network$: Network$, clientUrl$: ClientUrl liveData.map((nodes) => FP.pipe( nodes, - A.map(({ total_bond, current_award, status, node_address, bond_providers }) => ({ + A.map(({ total_bond, current_award, status, node_address, bond_providers, signer_membership }) => ({ address: node_address, bond: baseAmount(total_bond, THORCHAIN_DECIMAL), award: baseAmount(current_award, THORCHAIN_DECIMAL), @@ -294,7 +294,8 @@ export const createThornodeService$ = (network$: Network$, clientUrl$: ClientUrl bond: baseAmount(provider.bond, THORCHAIN_DECIMAL) })) : [] - } + }, + signMembership: signer_membership })) ) ), diff --git a/src/renderer/services/thorchain/types.ts b/src/renderer/services/thorchain/types.ts index 9c33dc4ec..5362c8eea 100644 --- a/src/renderer/services/thorchain/types.ts +++ b/src/renderer/services/thorchain/types.ts @@ -127,6 +127,7 @@ export type NodeInfo = { award: BaseAmount status: TN.NodeStatusEnum bondProviders: BondProviders + signMembership: string[] } export type NodeInfoLD = LiveData diff --git a/src/renderer/views/wallet/Interact/InteractViewTHOR.tsx b/src/renderer/views/wallet/Interact/InteractViewTHOR.tsx index 7db3eefeb..3239018e3 100644 --- a/src/renderer/views/wallet/Interact/InteractViewTHOR.tsx +++ b/src/renderer/views/wallet/Interact/InteractViewTHOR.tsx @@ -2,10 +2,12 @@ import React, { useCallback, useMemo } from 'react' import * as RD from '@devexperts/remote-data-ts' import { Col, Row } from 'antd' +import * as A from 'fp-ts/Array' import * as FP from 'fp-ts/function' import * as O from 'fp-ts/Option' import { useObservableState } from 'observable-hooks' import { useLocation, useNavigate, useParams } from 'react-router-dom' +import * as Rx from 'rxjs' import * as RxOp from 'rxjs/operators' import { ErrorView } from '../../../components/shared/error' @@ -28,6 +30,8 @@ import { useOpenExplorerTxUrl } from '../../../hooks/useOpenExplorerTxUrl' import { useValidateAddress } from '../../../hooks/useValidateAddress' import * as walletRoutes from '../../../routes/wallet' import { FeeRD } from '../../../services/chain/types' +import { userNodes$ } from '../../../services/storage/userNodes' +import { NodeInfosRD } from '../../../services/thorchain/types' import { reloadBalancesByChain } from '../../../services/wallet' import { DEFAULT_BALANCES_FILTER, INITIAL_BALANCES_STATE } from '../../../services/wallet/const' import { SelectedWalletAssetRD } from '../../../services/wallet/types' @@ -114,7 +118,7 @@ export const InteractViewTHOR: React.FC = () => { ) }, [oBalances, selectedAssetRD]) - const { fees$, reloadFees, interact$ } = useThorchainContext() + const { fees$, reloadFees, interact$, getNodeInfos$ } = useThorchainContext() const [feeRD] = useObservableState( () => @@ -125,6 +129,26 @@ export const InteractViewTHOR: React.FC = () => { RD.initial ) + const [nodeInfos] = useObservableState( + () => + FP.pipe( + Rx.combineLatest([userNodes$, getNodeInfos$]), + RxOp.switchMap(([userNodes, nodeInfos]) => + Rx.of( + FP.pipe( + nodeInfos, + RD.map((data) => + FP.pipe( + data, + A.filter(({ address }) => userNodes.includes(address)) + ) + ) + ) + ) + ) + ), + RD.initial + ) const interactTypeChanged = useCallback( (type: InteractType) => { navigate( @@ -192,6 +216,7 @@ export const InteractViewTHOR: React.FC = () => { poolDetails={poolDetails} nodeAddress={nodeAddress} bondAmount={bondAmount} + nodes={nodeInfos} /> )