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

frontend: update 'My portfolio' with lightning account #2703

Open
wants to merge 1 commit into
base: staging-ln
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ func TestSetLightningConfig(t *testing.T) {
Code: "v0-test-ln-0",
Number: 0,
RootFingerprint: []byte("fingerprint"),
KeyStoreName: "test",
})
require.NoError(t, cfg.SetLightningConfig(lightningCfg))

Expand Down
2 changes: 2 additions & 0 deletions backend/config/lightning.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type LightningAccountConfig struct {
Mnemonic string `json:"mnemonic"`
// RootFingerprint is fingerprint of the keystore that generated the entropy.
RootFingerprint jsonp.HexBytes `json:"rootFingerprint"`
// KeyStoreName is name of the keystore.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I realized that storing the keystore name could lead to issues when the device name is changed by the user.
I didn't look deeply into this, but I think that the proper way of doing it could be to get the persisted keystore info and retrieve the name there (see backend.Config().AccountsConfig()), but we may need to actively persist the current keystore when enabling the LN account).

cc @benma wdyt?

KeyStoreName string `json:"keystoreName"`
// Code is the code of the lightning account.
Code types.Code `json:"code"`
// Number is the lightning account incremental number.
Expand Down
9 changes: 9 additions & 0 deletions backend/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,15 @@ func (handlers *Handlers) getCoinsTotalBalance(_ *http.Request) (interface{}, er
var sortedCoins []coin.Code
totalCoinsBalances := make(map[coin.Code]*big.Int)

if handlers.backend.Config().LightningConfig().LightningEnabled() {
lightningBalance, err := handlers.backend.Lightning().Balance()
if err != nil {
return nil, err
}
totalCoinsBalances[coin.CodeBTC] = lightningBalance.Available().BigInt()
sortedCoins = append(sortedCoins, coin.CodeBTC)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This potentially breaks the coin sorting, which should be derived by the accounts sorting. The LN balance handling should be done together with the BTC accounts balance

}

for _, account := range handlers.backend.Accounts() {
if account.Config().Config.Inactive || account.Config().Config.HiddenBecauseUnused {
continue
Expand Down
6 changes: 6 additions & 0 deletions backend/lightning/lightning.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ func (lightning *Lightning) Activate() error {
return err
}

keystoreName, err := keystore.Name()
if err != nil {
return err
}

entropyMnemonic, err := bip39.NewMnemonic(entropy)
if err != nil {
lightning.log.WithError(err).Warn("Error generating mnemonic")
Expand All @@ -111,6 +116,7 @@ func (lightning *Lightning) Activate() error {
lightningAccount := config.LightningAccountConfig{
Mnemonic: entropyMnemonic,
RootFingerprint: fingerprint,
KeyStoreName: keystoreName,
Code: types.Code(strings.Join([]string{"v0-", hex.EncodeToString(fingerprint), "-ln-0"}, "")),
Number: 0,
}
Expand Down
1 change: 1 addition & 0 deletions frontends/web/src/api/lightning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface ILightningResponse<T> {
export type TLightningAccountConfig = {
mnemonic: string;
rootFingerprint: string;
keystoreName: string;
code: AccountCode;
num: number;
};
Expand Down
23 changes: 21 additions & 2 deletions frontends/web/src/routes/account/summary/accountssummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Status } from '@/components/status/status';
import { GuideWrapper, GuidedContent, Header, Main } from '@/components/layout';
import { View } from '@/components/view/view';
import { Chart } from './chart';
import { SummaryBalance } from './summarybalance';
import { LightningBalance, SummaryBalance } from './summarybalance';
import { CoinBalance } from './coinbalance';
import { AddBuyReceiveOnEmptyBalances } from '@/routes/account/info/buyReceiveCTA';
import { Entry } from '@/components/guide/entry';
Expand All @@ -36,6 +36,7 @@ import { HideAmountsButton } from '@/components/hideamountsbutton/hideamountsbut
import { AppContext } from '@/contexts/AppContext';
import { getAccountsByKeystore, isAmbiguiousName } from '@/routes/account/utils';
import { RatesContext } from '@/contexts/RatesContext';
import { useLightning } from '@/hooks/lightning';

type TProps = {
accounts: accountApi.IAccount[];
Expand Down Expand Up @@ -63,9 +64,24 @@ export const AccountsSummary = ({
const [accountsTotalBalance, setAccountsTotalBalance] = useState<accountApi.TAccountsTotalBalance>();
const [coinsTotalBalance, setCoinsTotalBalance] = useState<accountApi.TCoinsTotalBalance>();
const [balances, setBalances] = useState<Balances>();
const { lightningConfig } = useLightning();

const hasCard = useSDCard(devices);

// lightning account exists but is not from any connected or remembered keystores
const hasLightningFromOtherKeystore = (
lightningConfig.accounts.length !== 0
&& (
!accountsByKeystore.some(({ keystore }) => {
return keystore.rootFingerprint === lightningConfig.accounts[0].rootFingerprint;
})
)
);
let keystores = accountsByKeystore.length;
if (hasLightningFromOtherKeystore) {
keystores += 1;
}

const getAccountSummary = useCallback(async () => {
// replace previous timer if present
if (summaryReqTimerID.current) {
Expand Down Expand Up @@ -216,12 +232,15 @@ export const AccountsSummary = ({
<AddBuyReceiveOnEmptyBalances accounts={accounts} balances={balances} />
) : undefined
} />
{accountsByKeystore.length > 1 && (
{keystores > 1 && (
<CoinBalance
summaryData={summaryData}
coinsBalances={coinsTotalBalance}
/>
)}
{hasLightningFromOtherKeystore && (
<LightningBalance/>
)}
thisconnect marked this conversation as resolved.
Show resolved Hide resolved
{accountsByKeystore &&
(accountsByKeystore.map(({ keystore, accounts }) =>
<SummaryBalance
Expand Down
48 changes: 48 additions & 0 deletions frontends/web/src/routes/account/summary/summarybalance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,51 @@ export const SummaryBalance = ({
</div>
);
};


export function LightningBalance() {
const { t } = useTranslation();
const { lightningConfig } = useLightning();
const [lightningBalance, setLightningBalance] = useState<accountApi.IBalance>();

const fetchLightningBalance = useCallback(async () => {
setLightningBalance(await getLightningBalance());
}, []);

useEffect(() => {
fetchLightningBalance();
const subscriptions = [subscribeNodeState(fetchLightningBalance)];
return () => unsubscribe(subscriptions);
}, [fetchLightningBalance, lightningConfig]);

return (
<div>
<div className={style.accountName}>
<p>
{lightningConfig.accounts[0].keystoreName} ({lightningConfig.accounts[0].rootFingerprint})
thisconnect marked this conversation as resolved.
Show resolved Hide resolved
</p>
</div>
<div className={style.balanceTable}>
<table className={style.table}>
<colgroup>
<col width="33%" />
<col width="33%" />
<col width="*" />
</colgroup>
<thead>
<tr>
<th>{t('accountSummary.name')}</th>
<th>{t('accountSummary.balance')}</th>
<th>{t('accountSummary.fiatBalance')}</th>
</tr>
</thead>
<tbody>
{lightningBalance && (
<BalanceRow key="lightning" code="lightning" name="Lightning" coinCode="lightning" balance={lightningBalance} />
)}
</tbody>
</table>
</div>
</div>
);
}
Loading