@@ -81,7 +49,7 @@ const TransactionRow = ({ transaction, flat }: Props) => {
- {description ? description : transaction.address}
+ {transaction.description ? transaction.description : transaction.address}
{' '}
diff --git a/apps/frontend/src/pages/Vault/RecentTransactions/TxDetailsSlideover.tsx b/apps/frontend/src/pages/Vault/RecentTransactions/TxDetailsSlideover.tsx
index 7181653a..56691538 100644
--- a/apps/frontend/src/pages/Vault/RecentTransactions/TxDetailsSlideover.tsx
+++ b/apps/frontend/src/pages/Vault/RecentTransactions/TxDetailsSlideover.tsx
@@ -1,4 +1,4 @@
-import React, { useContext } from 'react';
+import React, { useState } from 'react';
import { XIcon, ExternalLinkIcon } from '@heroicons/react/outline';
import { Unit, Price } from 'src/components';
@@ -12,11 +12,9 @@ import { Transaction } from '@lily/types';
interface Props {
transaction: Transaction;
setOpen: React.Dispatch>;
- description: string;
- setDescription: React.Dispatch>;
}
-const TxDetailsSlideover = ({ transaction, setOpen, description, setDescription }: Props) => {
+const TxDetailsSlideover = ({ transaction, setOpen }: Props) => {
return (
<>
@@ -58,11 +56,7 @@ const TxDetailsSlideover = ({ transaction, setOpen, description, setDescription
-
+
{
if (action.type === ACCOUNTMAP_UPDATE) {
@@ -20,5 +21,26 @@ export const accountMapReducer = (state: AccountMap, action: AccountMapAction) =
};
}
+ if (action.type === ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION) {
+ const accountTransactions = [
+ ...(state[action.payload.accountId] as LilyOnchainAccount).transactions
+ ];
+
+ const index = accountTransactions.findIndex((tx) => tx.txid === action.payload.txid);
+
+ accountTransactions[index] = {
+ ...accountTransactions[index],
+ description: action.payload.description
+ };
+
+ return {
+ ...state,
+ [action.payload.accountId]: {
+ ...state[action.payload.accountId],
+ transactions: accountTransactions
+ }
+ };
+ }
+
return state;
};
diff --git a/apps/frontend/src/types/index.d.ts b/apps/frontend/src/types/index.d.ts
index 2afaacdd..d58e62d1 100644
--- a/apps/frontend/src/types/index.d.ts
+++ b/apps/frontend/src/types/index.d.ts
@@ -75,4 +75,8 @@ export type SetStatePsbt = React.Dispatch>;
export type AccountMapAction =
| { type: 'ACCOUNTMAP_UPDATE'; payload: { account: LilyAccount } }
- | { type: 'ACCOUNTMAP_SET'; payload: AccountMap };
+ | { type: 'ACCOUNTMAP_SET'; payload: AccountMap }
+ | {
+ type: 'ACCOUNT_TRANSACTION_UPDATE_DESCRIPTION';
+ payload: { accountId: string; txid: string; description: string };
+ };
diff --git a/apps/frontend/src/utils/accountMap.ts b/apps/frontend/src/utils/accountMap.ts
index cf80e6d6..86c8fc29 100644
--- a/apps/frontend/src/utils/accountMap.ts
+++ b/apps/frontend/src/utils/accountMap.ts
@@ -228,7 +228,8 @@ const getAddressFromPubKey = (
redeem,
input,
witness,
- bip32derivation: [childPubKey.bip32derivation]
+ bip32derivation: [childPubKey.bip32derivation],
+ tags: []
};
} else {
// p2wpkh
@@ -252,7 +253,8 @@ const getAddressFromPubKey = (
redeem,
input,
witness,
- bip32derivation: [childPubKey.bip32derivation]
+ bip32derivation: [childPubKey.bip32derivation],
+ tags: []
};
}
};
diff --git a/packages/shared-server/src/OnchainProviders/Electrum.ts b/packages/shared-server/src/OnchainProviders/Electrum.ts
index d3c2566c..331980a9 100644
--- a/packages/shared-server/src/OnchainProviders/Electrum.ts
+++ b/packages/shared-server/src/OnchainProviders/Electrum.ts
@@ -6,6 +6,10 @@ import ElectrumClient, {
} from '@lily-technologies/electrum-client';
import { address as bitcoinjsAddress, crypto as bitcoinjsCrypto } from 'bitcoinjs-lib';
import { bitcoinsToSatoshis } from 'unchained-bitcoin';
+import { Database } from 'sqlite';
+import sqlite3 from 'sqlite3';
+
+import { getAllLabelsForAddress } from '../sqlite';
import { OnchainBaseProvider } from '.';
@@ -98,7 +102,10 @@ export class ElectrumProvider extends OnchainBaseProvider {
}
}
- async getAccountData(account: OnChainConfig): Promise {
+ async getAccountData(
+ account: OnChainConfig,
+ db: Database
+ ): Promise {
const {
receiveAddresses,
changeAddresses,
@@ -106,15 +113,16 @@ export class ElectrumProvider extends OnchainBaseProvider {
unusedChangeAddresses,
transactions,
utxos
- } = await this.scanForAddressesAndTransactions(account, 10);
+ } = await this.scanForAddressesAndTransactions(account, 10, db);
const currentBalance = utxos.reduce((acum, cur) => acum + cur.value, 0);
console.log(`(${account.id}): Serializing transactions...`);
- const organizedTransactions = serializeTransactions(
+ const organizedTransactions = await serializeTransactions(
transactions,
receiveAddresses,
- changeAddresses
+ changeAddresses,
+ db
);
return {
@@ -176,7 +184,11 @@ export class ElectrumProvider extends OnchainBaseProvider {
return response;
}
- async scanForAddressesAndTransactions(account: OnChainConfig, limitGap: number) {
+ async scanForAddressesAndTransactions(
+ account: OnChainConfig,
+ limitGap: number,
+ db: Database
+ ) {
console.log(`(${account.id}): Deriving addresses and checking for transactions...`);
const receiveAddresses: Address[] = [];
const changeAddresses: Address[] = [];
@@ -200,11 +212,13 @@ export class ElectrumProvider extends OnchainBaseProvider {
// derive a batch of receive/change addresses
for (let i = pos; i < pos + BATCH_SIZE; i++) {
const receiveAddress = getAddressFromAccount(account, `m/0/${i}`, this.network);
+ receiveAddress.tags = await getAllLabelsForAddress(db, receiveAddress.address);
receiveAddress.isChange = false;
receiveAddress.isMine = true;
currentReceiveAddressBatch.push(receiveAddress);
const changeAddress = getAddressFromAccount(account, `m/1/${i}`, this.network);
+ changeAddress.tags = await getAllLabelsForAddress(db, changeAddress.address);
changeAddress.isChange = true;
changeAddress.isMine = true;
currentChangeAddressBatch.push(changeAddress);
diff --git a/packages/shared-server/src/OnchainProviders/Esplora.ts b/packages/shared-server/src/OnchainProviders/Esplora.ts
index b3bdfbff..98e00d53 100644
--- a/packages/shared-server/src/OnchainProviders/Esplora.ts
+++ b/packages/shared-server/src/OnchainProviders/Esplora.ts
@@ -2,6 +2,8 @@ import axios from 'axios';
import { Network } from 'bitcoinjs-lib';
import { blockExplorerAPIURL, MAINNET, TESTNET } from 'unchained-bitcoin';
import BigNumber from 'bignumber.js';
+import { Database } from 'sqlite';
+import sqlite3 from 'sqlite3';
import { OnchainBaseProvider } from '.';
@@ -38,7 +40,10 @@ export class EsploraProvider extends OnchainBaseProvider {
}
}
- async getAccountData(account: OnChainConfig): Promise {
+ async getAccountData(
+ account: OnChainConfig,
+ db: Database
+ ): Promise {
const {
receiveAddresses,
changeAddresses,
@@ -48,10 +53,11 @@ export class EsploraProvider extends OnchainBaseProvider {
} = await this.scanForAddressesAndTransactions(account, 10);
console.log(`(${account.id}): Serializing transactions...`);
- const organizedTransactions = serializeTransactions(
+ const organizedTransactions = await serializeTransactions(
transactions as Transaction[],
receiveAddresses,
- changeAddresses
+ changeAddresses,
+ db
);
console.log(`(${account.id}): Getting UTXO data...`);
diff --git a/packages/shared-server/src/OnchainProviders/OnchainBaseProvider.ts b/packages/shared-server/src/OnchainProviders/OnchainBaseProvider.ts
index 16c25b6b..d116775c 100644
--- a/packages/shared-server/src/OnchainProviders/OnchainBaseProvider.ts
+++ b/packages/shared-server/src/OnchainProviders/OnchainBaseProvider.ts
@@ -7,10 +7,16 @@ import {
} from '@lily/types';
import { Network, networks } from 'bitcoinjs-lib';
+import { Database } from 'sqlite';
+import sqlite3 from 'sqlite3';
+
type Providers = 'Esplora' | 'Electrum' | 'Bitcoin Core';
export interface OnchainProviderInterface {
- getAccountData: (account: OnChainConfig) => Promise;
+ getAccountData: (
+ account: OnChainConfig,
+ db: Database
+ ) => Promise;
broadcastTransaction: (txHex: string) => Promise;
estimateFee: () => Promise;
}
@@ -79,7 +85,10 @@ export abstract class OnchainBaseProvider implements OnchainProviderInterface {
abstract initialize(): void;
- abstract getAccountData(account: OnChainConfig): Promise;
+ abstract getAccountData(
+ account: OnChainConfig,
+ db: Database
+ ): Promise;
// returns txId
abstract broadcastTransaction(txHex: string): Promise;
diff --git a/packages/shared-server/src/sqlite/index.ts b/packages/shared-server/src/sqlite/index.ts
index 1ae10811..3356979e 100644
--- a/packages/shared-server/src/sqlite/index.ts
+++ b/packages/shared-server/src/sqlite/index.ts
@@ -1,5 +1,5 @@
import sqlite3 from 'sqlite3';
-import { open, Database } from 'sqlite';
+import { open } from 'sqlite';
function createDbConnection(filename: string) {
return open({
diff --git a/packages/shared-server/src/utils/accountMap.ts b/packages/shared-server/src/utils/accountMap.ts
index df7727b0..4c2771b9 100644
--- a/packages/shared-server/src/utils/accountMap.ts
+++ b/packages/shared-server/src/utils/accountMap.ts
@@ -1,6 +1,10 @@
import { payments, networks, Network } from 'bitcoinjs-lib';
import { deriveChildPublicKey, generateMultisigFromPublicKeys } from 'unchained-bitcoin';
import { Buffer } from 'buffer';
+import { Database } from 'sqlite';
+import sqlite3 from 'sqlite3';
+
+import { getTransactionDescription } from '../sqlite';
import {
OnChainConfig,
@@ -87,11 +91,12 @@ const decorateTx = (
return tx;
};
-export const serializeTransactions = (
+export const serializeTransactions = async (
transactions: EsploraTransactionResponse[],
addresses: Address[],
- changeAddresses: Address[]
-): Transaction[] => {
+ changeAddresses: Address[],
+ db: Database
+): Promise => {
transactions.sort((a, b) => a.status.block_time - b.status.block_time);
const addressesMap = createMap(addresses, 'address');
@@ -103,52 +108,57 @@ export const serializeTransactions = (
);
let balance = 0;
- const serializedTxs = decoratedTxs.map((tx) => {
- let amountIn: number, amountOut: number, amountOutChange: number;
- amountIn = sum(tx.vin, true);
- amountOut = sum(tx.vout, true);
- amountOutChange = sum(tx.vout, true, true);
+ const serializedTxs = await Promise.all(
+ decoratedTxs.map(async (tx) => {
+ let amountIn: number, amountOut: number, amountOutChange: number;
+ amountIn = sum(tx.vin, true);
+ amountOut = sum(tx.vout, true);
+ amountOutChange = sum(tx.vout, true, true);
- let type: TransactionType;
- let address: string;
- let totalValue: number;
- let value: number;
- // TODO: this is broken when Electrum is Provider
- if (amountIn === amountOut + (amountIn > 0 ? tx.fee : 0)) {
- type = 'moved';
- address = '';
- balance -= tx.fee;
- totalValue = balance;
- address = tx.vout.filter((vout) => vout.isChange)[0].scriptpubkey_address;
- value = tx.vout.reduce((accum, item) => accum + item.value, 0);
- } else {
- const feeContribution = amountIn > 0 ? tx.fee : 0;
- const netAmount = amountIn - amountOut - feeContribution;
- type = netAmount > 0 ? 'sent' : 'received';
- if (type === 'sent') {
- balance -= amountIn - amountOutChange + feeContribution;
+ const description = await (await getTransactionDescription(db, tx.txid))?.description;
+
+ let type: TransactionType;
+ let address: string;
+ let totalValue: number;
+ let value: number;
+ // TODO: this is broken when Electrum is Provider
+ if (amountIn === amountOut + (amountIn > 0 ? tx.fee : 0)) {
+ type = 'moved';
+ address = '';
+ balance -= tx.fee;
totalValue = balance;
- address = tx.vout.filter((vout) => !vout.isMine)[0].scriptpubkey_address;
- value = tx.vout
- .filter((vout) => !vout.isMine)
- .reduce((accum, item) => accum + item.value, 0);
+ address = tx.vout.filter((vout) => vout.isChange)[0].scriptpubkey_address;
+ value = tx.vout.reduce((accum, item) => accum + item.value, 0);
} else {
- balance += amountOut;
- totalValue = balance;
- address = tx.vout.filter((vout) => vout.isMine)[0].scriptpubkey_address;
- value = tx.vout
- .filter((vout) => vout.isMine)
- .reduce((accum, item) => accum + item.value, 0);
+ const feeContribution = amountIn > 0 ? tx.fee : 0;
+ const netAmount = amountIn - amountOut - feeContribution;
+ type = netAmount > 0 ? 'sent' : 'received';
+ if (type === 'sent') {
+ balance -= amountIn - amountOutChange + feeContribution;
+ totalValue = balance;
+ address = tx.vout.filter((vout) => !vout.isMine)[0].scriptpubkey_address;
+ value = tx.vout
+ .filter((vout) => !vout.isMine)
+ .reduce((accum, item) => accum + item.value, 0);
+ } else {
+ balance += amountOut;
+ totalValue = balance;
+ address = tx.vout.filter((vout) => vout.isMine)[0].scriptpubkey_address;
+ value = tx.vout
+ .filter((vout) => vout.isMine)
+ .reduce((accum, item) => accum + item.value, 0);
+ }
}
- }
- return {
- ...tx,
- type,
- address,
- totalValue,
- value
- } as Transaction;
- });
+ return {
+ ...tx,
+ type,
+ address,
+ totalValue,
+ description,
+ value
+ };
+ })
+ );
return serializedTxs.sort((a, b) => b.status.block_time - a.status.block_time);
};
@@ -230,7 +240,8 @@ const getAddressFromPubKey = (
redeem,
input,
witness,
- bip32derivation: [childPubKey.bip32derivation]
+ bip32derivation: [childPubKey.bip32derivation],
+ tags: []
};
} else {
// p2wpkh
@@ -254,7 +265,8 @@ const getAddressFromPubKey = (
redeem,
input,
witness,
- bip32derivation: [childPubKey.bip32derivation]
+ bip32derivation: [childPubKey.bip32derivation],
+ tags: []
};
}
};
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 4ef5806e..25990c6b 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -161,6 +161,7 @@ export interface Transaction extends EsploraTransactionResponse {
totalValue: number;
address: string;
value: number;
+ description: string;
}
export interface DecoratedLightningChannel extends Channel {
@@ -216,6 +217,7 @@ export interface TxStatus {
export interface Address {
network: Network;
address: string;
+ tags: AddressLabel[];
hash?: Buffer;
output: any;
redeem?: any;