Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Balance Sheet #424

Merged
merged 8 commits into from
May 29, 2024
Merged

feat: Balance Sheet #424

merged 8 commits into from
May 29, 2024

Conversation

Kodylow
Copy link
Member

@Kodylow Kodylow commented May 17, 2024

Adds a balance sheet view of all modules.

image

Summary by CodeRabbit

  • New Features

    • Introduced a unit selection feature in the BalanceCard component to switch between msats, sats, and btc.
    • Added a new BalanceTable component to display balance information based on the selected unit.
  • Enhancements

    • Updated the layout of the FederationAdmin and FederationInfoCard components for improved visual presentation.
    • Changed the label "Balance" to "Balance Sheet" for clearer terminology.
  • Bug Fixes

    • Adjusted the equity display in the BalanceTable to ensure accurate representation of total equity.
  • Documentation

    • Added a new formatValue function to handle unit conversions.
  • Refactor

    • Reorganized component imports and state management in the BalanceCard component for better performance and readability.

@Kodylow Kodylow requested a review from a team as a code owner May 17, 2024 23:22
Copy link
Contributor

coderabbitai bot commented May 17, 2024

Walkthrough

Walkthrough

The recent updates to the guardian-ui application primarily focus on enhancing the layout and functionality of several components, particularly within the FederationAdmin and BalanceCard components. Significant changes include the repositioning and resizing of components, the introduction of a new BalanceTable component, and improvements in state management and user interface elements. Additionally, there are updates to the internationalization files and type definitions to support these changes.

Changes

File Path Change Summary
apps/guardian-ui/src/admin/FederationAdmin.tsx Adjusted layout: Repositioned BalanceCard, modified width of Flex container, moved FederationInfoCard.
apps/guardian-ui/src/components/BalanceCard.tsx Updated imports, removed walletBalance logic, added state for unit, updated layout, added unit selection buttons, replaced KeyValues with BalanceTable.
apps/guardian-ui/src/components/BalanceTable.tsx Introduced BalanceTable component, added unit property, formatted values based on unit, updated table layout.
apps/guardian-ui/src/components/FederationInfoCard.tsx Updated layout using SimpleGrid for KeyValues component.
apps/guardian-ui/src/languages/en.json Changed label from "Balance" to "Balance Sheet".
packages/types/src/modules.ts Added Unknown to ModuleKind enum, updated FedimintModule interface.
packages/utils/src/format.tsx Added formatValue function to format values based on unit.

Sequence Diagram(s) (Beta)

sequenceDiagram
    participant User
    participant FederationAdmin
    participant BalanceCard
    participant BalanceTable

    User->>FederationAdmin: Access Federation Admin page
    FederationAdmin->>BalanceCard: Render BalanceCard
    BalanceCard->>BalanceTable: Pass balance data and selected unit
    BalanceTable-->>BalanceCard: Display formatted balance information
    BalanceCard-->>FederationAdmin: Display updated BalanceCard
    FederationAdmin-->>User: Show updated Federation Admin page
Loading
sequenceDiagram
    participant Developer
    participant TypeModule
    participant FormatUtil

    Developer->>TypeModule: Add `Unknown` to `ModuleKind` enum
    TypeModule-->>Developer: Updated enum and interface
    Developer->>FormatUtil: Add `formatValue` function
    FormatUtil-->>Developer: Updated utility functions
Loading

Recent Review Details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits Files that changed from the base of the PR and between ed051b9 and e49342e.
Files selected for processing (7)
  • apps/guardian-ui/src/admin/FederationAdmin.tsx (1 hunks)
  • apps/guardian-ui/src/components/BalanceCard.tsx (2 hunks)
  • apps/guardian-ui/src/components/BalanceTable.tsx (1 hunks)
  • apps/guardian-ui/src/components/FederationInfoCard.tsx (2 hunks)
  • apps/guardian-ui/src/languages/en.json (1 hunks)
  • packages/types/src/modules.ts (2 hunks)
  • packages/utils/src/format.tsx (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • apps/guardian-ui/src/languages/en.json
Additional Context Used
Biome (13)
apps/guardian-ui/src/admin/FederationAdmin.tsx (4)

1-1: The default import is only used as a type.


2-7: All these imports are only used as types.


31-31: Use Number.parseInt instead of the equivalent global.


34-34: Use Number.parseInt instead of the equivalent global.

apps/guardian-ui/src/components/BalanceCard.tsx (2)

1-1: The default import is only used as a type.


10-11: All these imports are only used as types.

apps/guardian-ui/src/components/BalanceTable.tsx (2)

1-1: All these imports are only used as types.


17-18: Some named imports are only used as types.

apps/guardian-ui/src/components/FederationInfoCard.tsx (2)

1-1: The default import is only used as a type.


2-3: All these imports are only used as types.

packages/types/src/modules.ts (2)

1-1: All these imports are only used as types.


1-2: All these imports are only used as types.

packages/utils/src/format.tsx (1)

1-1: All these imports are only used as types.

Additional comments not posted (2)
packages/types/src/modules.ts (1)

8-8: Ensure integration of new ModuleKind values.

The new ModuleKind.Unknown and configurations for Meta and Unknown are added. Verify their usage across the application to ensure proper integration.

Also applies to: 48-49

Verification successful

The new ModuleKind.Unknown and ModuleKind.Meta values are integrated into the application logic.

  • ModuleKind.Unknown is used in apps/guardian-ui/src/components/BalanceTable.tsx.
  • ModuleKind.Meta is used in apps/guardian-ui/src/components/FederationConfigCard.tsx.
Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the integration of new module kinds `Meta` and `Unknown`.

# Test: Search for the usage of `ModuleKind.Meta` and `ModuleKind.Unknown`. Expect: Proper handling in the application logic.
rg --type typescript $'ModuleKind.Meta|ModuleKind.Unknown'

Length of output: 94



Script:

#!/bin/bash
# Description: Verify the integration of new module kinds `Meta` and `Unknown`.

# Test: Search for the usage of `ModuleKind.Meta` and `ModuleKind.Unknown`. Expect: Proper handling in the application logic.
rg --type ts $'ModuleKind.Meta|ModuleKind.Unknown'

Length of output: 459

apps/guardian-ui/src/components/BalanceTable.tsx (1)

30-32: Filter out the Unknown module to avoid confusion.

The logic to filter out the Unknown module is correctly implemented, ensuring it does not appear in the balance calculations.


Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to full the review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Kodylow Kodylow requested a review from a team as a code owner May 17, 2024 23:27
@Kodylow
Copy link
Member Author

Kodylow commented May 17, 2024

Added a filter to now put the "unknown" module in the balance sheet, that's just there to test if it'll break clients and it doesn't have any assets/liabilities

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Comment on lines 48 to 57
[ModuleKind.Meta]: {
meta_consensus: {
meta_consensus_version: number;
};
};
[ModuleKind.Unknown]: {
unknown_consensus: {
unknown_consensus_version: number;
};
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

Ensure proper integration of ModuleKind.Meta throughout the application.

  • ModuleKind.Meta is defined in packages/types/src/modules.ts but not used elsewhere in the codebase.
  • Unlike ModuleKind.Unknown, which is handled in BalanceTable.tsx, ModuleKind.Meta lacks integration.
Analysis chain

The addition of configurations for Meta and Unknown module kinds is approved. Ensure these new module kinds are integrated properly throughout the application.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the integration of new module kinds `Meta` and `Unknown`.

# Test: Search for the usage of `ModuleKind.Meta` and `ModuleKind.Unknown`. Expect: Proper handling in the application logic.
rg --type typescript $'ModuleKind.Meta|ModuleKind.Unknown'

Length of output: 94



Script:

#!/bin/bash
# Description: Verify the integration of new module kinds `Meta` and `Unknown`.

# Test: Search for the usage of `ModuleKind.Meta` and `ModuleKind.Unknown`. Expect: Proper handling in the application logic.
rg --type ts $'ModuleKind.Meta|ModuleKind.Unknown'

Length of output: 264



Script:

#!/bin/bash
# Description: Extract relevant lines from `BalanceTable.tsx` to verify the handling of `ModuleKind.Meta` and `ModuleKind.Unknown`.

# Extract lines containing `ModuleKind.Meta` and `ModuleKind.Unknown` and their surrounding context.
rg --type ts --context 5 $'ModuleKind.Meta|ModuleKind.Unknown' apps/guardian-ui/src/components/BalanceTable.tsx

Length of output: 512



Script:

#!/bin/bash
# Description: Verify the integration of `ModuleKind.Meta` across the entire codebase.

# Search for the usage of `ModuleKind.Meta` in the entire codebase.
rg --type ts $'ModuleKind.Meta'

Length of output: 83

@elsirion
Copy link
Contributor

elsirion commented May 21, 2024

That unknown module would be confusing to users imo, but it's only present in test environments afaik, so this should be fine.

Missed your last comment.

Comment on lines +27 to +32
const moduleSummaries = Object.entries(auditSummary.module_summaries).filter(
([, module]) => module.kind !== ModuleKind.Unknown
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we filter the unknown module? It's confusing, but only there in devimint afaik.

const totalLiabilities = moduleSummaries.reduce((sum, [, module]) => {
return sum + (module.net_assets < 0 ? module.net_assets : 0);
}, 0) as MSats;
const isBalanced = totalAssets + totalLiabilities === 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be false for any federation charging fees. I think as long as assets > liabilities we should say it's balanced.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably have a way of specifically separating out the fees either into an "equity" column or a way to get the specific amounts collected in fees from the different modules so that we can still display that the outstanding liabilities match the outstanding reserves exactly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modules don't collect fees themselves and we don't record fees anywhere. All that happens is that the federation slowly becomes overfunded. We discussed different approaches, but this didn't seem like a hug problem so far. If you want you can just display any overfunding as a fees entry on the liabilities side.

@Kodylow
Copy link
Member Author

Kodylow commented May 29, 2024

Okay added an Equity (Guardian Fees) column and adjusted the check to "Is Full Reserve" checking that assets > liabilities and equity is > 0

image

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Outside diff range and nitpick comments (1)
apps/guardian-ui/src/admin/FederationAdmin.tsx (1)

Line range hint 31-31: Replace global parseInt with Number.parseInt for better clarity and consistency in the codebase.

-      const peerIds = Object.keys(status.federation.status_by_peer).map((id) =>
-        parseInt(id, 10)
-      );
-      const configPeerIds = Object.keys(config.api_endpoints).map((id) =>
-        parseInt(id, 10)
-      );
+      const peerIds = Object.keys(status.federation.status_by_peer).map((id) => Number.parseInt(id, 10));
+      const configPeerIds = Object.keys(config.api_endpoints).map((id) => Number.parseInt(id, 10));

Also applies to: 34-34

Comment on lines 32 to 65
<Card w='60%'>
<CardHeader>
<Text size='lg' fontWeight='600'>
{t('federation-dashboard.balance.label')}
</Text>
<Flex justifyContent='space-between' alignItems='center'>
<Text size='lg' fontWeight='600'>
{t('federation-dashboard.balance.label')}
</Text>
<ButtonGroup size='xs' borderRadius='md'>
<Button
onClick={() => setUnit('msats')}
variant={unit === 'msats' ? 'solid' : 'ghost'}
>
msats
</Button>
<Button
onClick={() => setUnit('sats')}
variant={unit === 'sats' ? 'solid' : 'ghost'}
>
sats
</Button>
<Button
onClick={() => setUnit('btc')}
variant={unit === 'btc' ? 'solid' : 'ghost'}
>
BTC
</Button>
</ButtonGroup>
</Flex>
</CardHeader>
<CardBody>
<KeyValues direction='row' keyValues={keyValues} />
{auditSummary ? (
<BalanceTable auditSummary={auditSummary} unit={unit} />
) : (
<Text>Loading...</Text>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The layout and unit switching functionality are well-implemented. However, consider adding a loading indicator or skeleton screen for a better user experience during data fetch.

-          <Text>Loading...</Text>
+          <Skeleton height='20px' width='100%' />

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
<Card w='60%'>
<CardHeader>
<Text size='lg' fontWeight='600'>
{t('federation-dashboard.balance.label')}
</Text>
<Flex justifyContent='space-between' alignItems='center'>
<Text size='lg' fontWeight='600'>
{t('federation-dashboard.balance.label')}
</Text>
<ButtonGroup size='xs' borderRadius='md'>
<Button
onClick={() => setUnit('msats')}
variant={unit === 'msats' ? 'solid' : 'ghost'}
>
msats
</Button>
<Button
onClick={() => setUnit('sats')}
variant={unit === 'sats' ? 'solid' : 'ghost'}
>
sats
</Button>
<Button
onClick={() => setUnit('btc')}
variant={unit === 'btc' ? 'solid' : 'ghost'}
>
BTC
</Button>
</ButtonGroup>
</Flex>
</CardHeader>
<CardBody>
<KeyValues direction='row' keyValues={keyValues} />
{auditSummary ? (
<BalanceTable auditSummary={auditSummary} unit={unit} />
) : (
<Text>Loading...</Text>
)}
<Card w='60%'>
<CardHeader>
<Flex justifyContent='space-between' alignItems='center'>
<Text size='lg' fontWeight='600'>
{t('federation-dashboard.balance.label')}
</Text>
<ButtonGroup size='xs' borderRadius='md'>
<Button
onClick={() => setUnit('msats')}
variant={unit === 'msats' ? 'solid' : 'ghost'}
>
msats
</Button>
<Button
onClick={() => setUnit('sats')}
variant={unit === 'sats' ? 'solid' : 'ghost'}
>
sats
</Button>
<Button
onClick={() => setUnit('btc')}
variant={unit === 'btc' ? 'solid' : 'ghost'}
>
BTC
</Button>
</ButtonGroup>
</Flex>
</CardHeader>
<CardBody>
{auditSummary ? (
<BalanceTable auditSummary={auditSummary} unit={unit} />
) : (
<Skeleton height='20px' width='100%' />
)}

apps/guardian-ui/src/components/BalanceTable.tsx Outdated Show resolved Hide resolved
@@ -0,0 +1,90 @@
import React from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using type-only imports for React to clarify that it's only used for type annotations.

- import React from 'react';
+ import type { FC } from 'react';
+ import React from 'react';

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
import React from 'react';
import type { FC } from 'react';
import React from 'react';

Comment on lines 79 to 83
{isBalanced ? (
<Icon as={CheckIcon} color='green.500' />
) : (
<Icon as={CloseIcon} color='red.500' />
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tip

Codebase Verification

The logic for determining the balance status (isBalanced) in BalanceTable.tsx is not currently covered by any tests. To ensure the reliability and correctness of this logic, it is recommended to add thorough tests for the balance status determination.

  • Location of logic: apps/guardian-ui/src/components/BalanceTable.tsx
  • Logic definition:
    const isBalanced =
      totalAssets - totalLiabilities + equity === 0 && equity >= 0;
Analysis chain

The visual indicators for balance status are a good UX addition. Ensure that the logic for determining balance status is thoroughly tested.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify the logic for determining balance status.

# Test: Search for balance status logic. Expect: Thorough testing.
rg --type typescript $'isBalanced'

Length of output: 68



Script:

#!/bin/bash
# Description: Verify the logic for determining balance status.

# Test: Search for balance status logic. Expect: Thorough testing.
rg --type ts 'isBalanced'

Length of output: 174



Script:

#!/bin/bash
# Description: Extract the definition of `isBalanced` and search for related tests.

# Extract the definition of `isBalanced`
rg --type ts --context 5 'const isBalanced =' apps/guardian-ui/src/components/BalanceTable.tsx

# Search for related tests
rg --type ts 'isBalanced' tests/

Length of output: 578



Script:

#!/bin/bash
# Description: Search for test files related to `BalanceTable.tsx` and check for tests involving `isBalanced`.

# Find test files related to `BalanceTable.tsx`
rg --files | grep -i 'balance.*test'

# Check for tests involving `isBalanced`
rg --type ts 'isBalanced' $(rg --files | grep -i 'balance.*test')

Length of output: 254

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Comment on lines 1 to 139
import React from 'react';
import { Table, Tbody, Td, Th, Thead, Tr, Icon, Flex } from '@chakra-ui/react';
import { ReactComponent as CheckIcon } from '../assets/svgs/check-circle.svg';
import { ReactComponent as CloseIcon } from '../assets/svgs/x-circle.svg';
import { AuditSummary, ModuleKind, MSats } from '@fedimint/types';
import { formatMsatsToBtc } from '@fedimint/utils';

interface BalanceTableProps {
auditSummary: AuditSummary;
unit: 'msats' | 'sats' | 'btc';
}

export const BalanceTable: React.FC<BalanceTableProps> = ({
auditSummary,
unit,
}) => {
const moduleSummaries = Object.entries(auditSummary.module_summaries).filter(
([, module]) => module.kind !== ModuleKind.Unknown
);

const totalAssets = moduleSummaries.reduce((sum, [, module]) => {
return sum + (module.net_assets > 0 ? module.net_assets : 0);
}, 0) as MSats;
const totalLiabilities = moduleSummaries.reduce((sum, [, module]) => {
return sum + Math.abs(module.net_assets < 0 ? module.net_assets : 0);
}, 0) as MSats;
const equity = totalAssets - totalLiabilities;
const isBalanced =
totalAssets - totalLiabilities + equity === 0 && equity >= 0;

const formatValue = (value: MSats) => {
switch (unit) {
case 'msats':
return value.toString();
case 'sats':
return (value / 1000).toFixed(0);
case 'btc':
return formatMsatsToBtc(value);
default:
return value.toString();
}
};

return (
<Table variant='simple'>
<Thead>
<Tr>
<Th>Module</Th>
<Th isNumeric>Assets</Th>
<Th isNumeric>Liabilities</Th>
<Th isNumeric>Equity (Guardian Fees)</Th>
<Th>Is Full Reserve</Th> {/* Updated column header */}
</Tr>
</Thead>
<Tbody>
{moduleSummaries.map(([key, module]) => (
<Tr key={key}>
<Td>{module.kind}</Td>
<Td isNumeric>
{module.net_assets > 0 ? formatValue(module.net_assets) : '0'}
</Td>
<Td isNumeric>
{module.net_assets < 0
? formatValue(Math.abs(module.net_assets) as MSats)
: '0'}
</Td>
<Td isNumeric></Td> {/* Set equity to 0 for individual rows */}
<Td /> {/* Empty cell for alignment */}
</Tr>
))}
<Tr fontWeight='bold'>
<Td>Total</Td>
<Td isNumeric>{formatValue(totalAssets)}</Td>
<Td isNumeric>{formatValue(Math.abs(totalLiabilities) as MSats)}</Td>
<Td isNumeric>{formatValue(equity as MSats)}</Td>{' '}
{/* Display total equity */}
<Td>
<Flex justifyContent='center' alignItems='center'>
{isBalanced ? (
<Icon as={CheckIcon} color='green.500' />
) : (
<Icon as={CloseIcon} color='red.500' />
)}
</Flex>
</Td>
</Tr>
</Tbody>
</Table>
);
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The BalanceTable component is well-implemented with clear handling of assets, liabilities, and equity calculations. It correctly filters out the Unknown module and supports multiple units for value display.

Minor suggestion: Use self-closing tags for empty JSX elements to improve readability.

- <Td></Td> {/* Empty cell for alignment */}
+ <Td /> {/* Empty cell for alignment */}

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
import React from 'react';
import { Table, Tbody, Td, Th, Thead, Tr, Icon, Flex } from '@chakra-ui/react';
import { ReactComponent as CheckIcon } from '../assets/svgs/check-circle.svg';
import { ReactComponent as CloseIcon } from '../assets/svgs/x-circle.svg';
import { AuditSummary, ModuleKind, MSats } from '@fedimint/types';
import { formatMsatsToBtc } from '@fedimint/utils';
interface BalanceTableProps {
auditSummary: AuditSummary;
unit: 'msats' | 'sats' | 'btc';
}
export const BalanceTable: React.FC<BalanceTableProps> = ({
auditSummary,
unit,
}) => {
const moduleSummaries = Object.entries(auditSummary.module_summaries).filter(
([, module]) => module.kind !== ModuleKind.Unknown
);
const totalAssets = moduleSummaries.reduce((sum, [, module]) => {
return sum + (module.net_assets > 0 ? module.net_assets : 0);
}, 0) as MSats;
const totalLiabilities = moduleSummaries.reduce((sum, [, module]) => {
return sum + Math.abs(module.net_assets < 0 ? module.net_assets : 0);
}, 0) as MSats;
const equity = totalAssets - totalLiabilities;
const isBalanced =
totalAssets - totalLiabilities + equity === 0 && equity >= 0;
const formatValue = (value: MSats) => {
switch (unit) {
case 'msats':
return value.toString();
case 'sats':
return (value / 1000).toFixed(0);
case 'btc':
return formatMsatsToBtc(value);
default:
return value.toString();
}
};
return (
<Table variant='simple'>
<Thead>
<Tr>
<Th>Module</Th>
<Th isNumeric>Assets</Th>
<Th isNumeric>Liabilities</Th>
<Th isNumeric>Equity (Guardian Fees)</Th>
<Th>Is Full Reserve</Th> {/* Updated column header */}
</Tr>
</Thead>
<Tbody>
{moduleSummaries.map(([key, module]) => (
<Tr key={key}>
<Td>{module.kind}</Td>
<Td isNumeric>
{module.net_assets > 0 ? formatValue(module.net_assets) : '0'}
</Td>
<Td isNumeric>
{module.net_assets < 0
? formatValue(Math.abs(module.net_assets) as MSats)
: '0'}
</Td>
<Td isNumeric></Td> {/* Set equity to 0 for individual rows */}
<Td /> {/* Empty cell for alignment */}
</Tr>
))}
<Tr fontWeight='bold'>
<Td>Total</Td>
<Td isNumeric>{formatValue(totalAssets)}</Td>
<Td isNumeric>{formatValue(Math.abs(totalLiabilities) as MSats)}</Td>
<Td isNumeric>{formatValue(equity as MSats)}</Td>{' '}
{/* Display total equity */}
<Td>
<Flex justifyContent='center' alignItems='center'>
{isBalanced ? (
<Icon as={CheckIcon} color='green.500' />
) : (
<Icon as={CloseIcon} color='red.500' />
)}
</Flex>
</Td>
</Tr>
</Tbody>
</Table>
);
};
{moduleSummaries.map(([key, module]) => (
<Tr key={key}>
<Td>{module.kind}</Td>
<Td isNumeric>
{module.net_assets > 0 ? formatValue(module.net_assets) : '0'}
</Td>
<Td isNumeric>
{module.net_assets < 0
? formatValue(Math.abs(module.net_assets) as MSats)
: '0'}
</Td>
<Td isNumeric></Td> {/* Set equity to 0 for individual rows */}
<Td /> {/* Empty cell for alignment */}
</Tr>
))}

@Kodylow
Copy link
Member Author

Kodylow commented May 29, 2024

Actually that took up a lot of real estate, moved into separate cards for Full reserve and fees calculations, if there's no fees it just doesn't show that card:

image

@Kodylow Kodylow requested a review from elsirion May 29, 2024 00:42
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Outside diff range and nitpick comments (1)
apps/guardian-ui/src/admin/FederationAdmin.tsx (1)

Line range hint 31-31: Use Number.parseInt for parsing integers.

For better clarity and consistency, use Number.parseInt instead of the global parseInt.

- const peerIds = Object.keys(status.federation.status_by_peer).map((id) => parseInt(id, 10));
+ const peerIds = Object.keys(status.federation.status_by_peer).map((id) => Number.parseInt(id, 10));
- const configPeerIds = Object.keys(config.api_endpoints).map((id) => parseInt(id, 10));
+ const configPeerIds = Object.keys(config.api_endpoints).map((id) => Number.parseInt(id, 10));

Also applies to: 34-34

Copy link
Contributor

@elsirion elsirion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a thorough review

@@ -5,6 +5,7 @@ export enum ModuleKind {
Mint = 'mint',
Wallet = 'wallet',
Meta = 'meta',
Unknown = 'unknown',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I worry a bit about this enum, does this mean we will encounter errors when encountering other modules than the ones listed here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to land this and open as a separate issue for unknown module handling

@Kodylow Kodylow merged commit 56a8141 into fedimint:master May 29, 2024
1 check passed
@Kodylow Kodylow deleted the assets-liabilities branch May 29, 2024 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants