Merge pull request #4195 from kaloudis/deduplicate-receive
refactor: Receive: deduplicate componentDidMount/render locals
This commit is contained in:
@@ -80,6 +80,8 @@ import {
|
||||
verifyMessageWithAddr as verifyMsgWithAddr
|
||||
} from '../lndmobile/wallet';
|
||||
|
||||
import { toLnrpcAddressTypeNum } from '../utils/LndUtils';
|
||||
|
||||
export default class EmbeddedLND extends LND {
|
||||
openChannelListener: any;
|
||||
|
||||
@@ -123,7 +125,10 @@ export default class EmbeddedLND extends LND {
|
||||
reversed?: boolean;
|
||||
}) => await listPayments(params);
|
||||
getNewAddress = async (data: any) =>
|
||||
await newAddress(data.type, data.account);
|
||||
await newAddress(
|
||||
toLnrpcAddressTypeNum(data?.type) as any,
|
||||
data?.account
|
||||
);
|
||||
getNewChangeAddress = async (data: any) =>
|
||||
await newChangeAddress(data.type, data.account);
|
||||
openChannelSync = async (data: OpenChannelRequest) =>
|
||||
|
||||
+8
-1
@@ -9,6 +9,7 @@ import OpenChannelRequest from './../models/OpenChannelRequest';
|
||||
import Base64Utils from './../utils/Base64Utils';
|
||||
import VersionUtils from './../utils/VersionUtils';
|
||||
import { localeString } from './../utils/LocaleUtils';
|
||||
import { toLnrpcAddressType } from './../utils/LndUtils';
|
||||
import { Hash as sha256Hash } from 'fast-sha256';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
@@ -371,7 +372,13 @@ export default class LND {
|
||||
}`
|
||||
);
|
||||
|
||||
getNewAddress = (data: any) => this.getRequest('/v1/newaddress', data);
|
||||
getNewAddress = (data: any) => {
|
||||
const params: any = { ...data };
|
||||
const type = toLnrpcAddressType(params.type);
|
||||
if (type !== undefined) params.type = type;
|
||||
else delete params.type;
|
||||
return this.getRequest('/v1/newaddress', params);
|
||||
};
|
||||
getNewChangeAddress = (data: any) =>
|
||||
this.postRequest('/v2/wallet/address/next', data);
|
||||
openChannelSync = (data: OpenChannelRequest) => {
|
||||
|
||||
@@ -10,20 +10,12 @@ import OpenChannelRequest from '../models/OpenChannelRequest';
|
||||
|
||||
import Base64Utils from '../utils/Base64Utils';
|
||||
import { snakeize } from '../utils/DataFormatUtils';
|
||||
import { toLnrpcAddressType } from '../utils/LndUtils';
|
||||
import VersionUtils from '../utils/VersionUtils';
|
||||
|
||||
import { Hash as sha256Hash } from 'fast-sha256';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
const ADDRESS_TYPES = [
|
||||
'WITNESS_PUBKEY_HASH',
|
||||
'NESTED_PUBKEY_HASH',
|
||||
'UNUSED_WITNESS_PUBKEY_HASH',
|
||||
'UNUSED_NESTED_PUBKEY_HASH',
|
||||
'TAPROOT_PUBKEY',
|
||||
'UNUSED_TAPROOT_PUBKEY'
|
||||
];
|
||||
|
||||
const NEXT_ADDR_MAP: any = {
|
||||
WITNESS_PUBKEY_HASH: 0,
|
||||
NESTED_PUBKEY_HASH: 1,
|
||||
@@ -220,7 +212,7 @@ export default class LightningNodeConnect {
|
||||
getNewAddress = async (data: any) =>
|
||||
await this.lnc.lnd.lightning
|
||||
.newAddress({
|
||||
type: ADDRESS_TYPES[data.type] || data.type,
|
||||
type: toLnrpcAddressType(data.type),
|
||||
account: data.account || 'default'
|
||||
})
|
||||
.then((data: walletrpc.AddrRequest) => snakeize(data));
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import { toLnrpcAddressType, toLnrpcAddressTypeNum } from './LndUtils';
|
||||
|
||||
describe('LndUtils', () => {
|
||||
describe('toLnrpcAddressType', () => {
|
||||
it('returns the lnrpc enum name for numeric-string input from the picker / settings', () => {
|
||||
expect(toLnrpcAddressType('0')).toEqual('WITNESS_PUBKEY_HASH');
|
||||
expect(toLnrpcAddressType('1')).toEqual('NESTED_PUBKEY_HASH');
|
||||
expect(toLnrpcAddressType('2')).toEqual(
|
||||
'UNUSED_WITNESS_PUBKEY_HASH'
|
||||
);
|
||||
expect(toLnrpcAddressType('3')).toEqual(
|
||||
'UNUSED_NESTED_PUBKEY_HASH'
|
||||
);
|
||||
expect(toLnrpcAddressType('4')).toEqual('TAPROOT_PUBKEY');
|
||||
expect(toLnrpcAddressType('5')).toEqual('UNUSED_TAPROOT_PUBKEY');
|
||||
});
|
||||
|
||||
it('accepts numeric input as well as numeric strings', () => {
|
||||
expect(toLnrpcAddressType(0)).toEqual('WITNESS_PUBKEY_HASH');
|
||||
expect(toLnrpcAddressType(1)).toEqual('NESTED_PUBKEY_HASH');
|
||||
expect(toLnrpcAddressType(4)).toEqual('TAPROOT_PUBKEY');
|
||||
});
|
||||
|
||||
it('maps walletrpc enum names returned by ListAccounts to the lnrpc equivalent', () => {
|
||||
// lnrpc has no hybrid variant; nested-segwit p2sh-p2wkh is the
|
||||
// closest equivalent the receiver sees.
|
||||
expect(toLnrpcAddressType('NESTED_WITNESS_PUBKEY_HASH')).toEqual(
|
||||
'NESTED_PUBKEY_HASH'
|
||||
);
|
||||
expect(
|
||||
toLnrpcAddressType('HYBRID_NESTED_WITNESS_PUBKEY_HASH')
|
||||
).toEqual('NESTED_PUBKEY_HASH');
|
||||
});
|
||||
|
||||
it('passes lnrpc enum names through unchanged', () => {
|
||||
expect(toLnrpcAddressType('WITNESS_PUBKEY_HASH')).toEqual(
|
||||
'WITNESS_PUBKEY_HASH'
|
||||
);
|
||||
expect(toLnrpcAddressType('NESTED_PUBKEY_HASH')).toEqual(
|
||||
'NESTED_PUBKEY_HASH'
|
||||
);
|
||||
expect(toLnrpcAddressType('TAPROOT_PUBKEY')).toEqual(
|
||||
'TAPROOT_PUBKEY'
|
||||
);
|
||||
});
|
||||
|
||||
it('returns undefined for null / undefined input so the backend default kicks in', () => {
|
||||
expect(toLnrpcAddressType(undefined)).toBeUndefined();
|
||||
expect(toLnrpcAddressType(null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('passes unrecognised values through verbatim rather than swallowing them', () => {
|
||||
// Lets callers decide how to handle (LND surfaces a clear
|
||||
// "not a valid value" error rather than silently picking '0').
|
||||
expect(toLnrpcAddressType('SOMETHING_NEW')).toEqual(
|
||||
'SOMETHING_NEW'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toLnrpcAddressTypeNum', () => {
|
||||
it('returns the lnrpc numeric AddressType for numeric-string input', () => {
|
||||
expect(toLnrpcAddressTypeNum('0')).toEqual(0);
|
||||
expect(toLnrpcAddressTypeNum('1')).toEqual(1);
|
||||
expect(toLnrpcAddressTypeNum('2')).toEqual(2);
|
||||
expect(toLnrpcAddressTypeNum('3')).toEqual(3);
|
||||
expect(toLnrpcAddressTypeNum('4')).toEqual(4);
|
||||
expect(toLnrpcAddressTypeNum('5')).toEqual(5);
|
||||
});
|
||||
|
||||
it('returns the lnrpc numeric AddressType for lnrpc enum names', () => {
|
||||
expect(toLnrpcAddressTypeNum('WITNESS_PUBKEY_HASH')).toEqual(0);
|
||||
expect(toLnrpcAddressTypeNum('NESTED_PUBKEY_HASH')).toEqual(1);
|
||||
expect(toLnrpcAddressTypeNum('TAPROOT_PUBKEY')).toEqual(4);
|
||||
});
|
||||
|
||||
it('returns the lnrpc numeric AddressType for walletrpc enum names', () => {
|
||||
expect(toLnrpcAddressTypeNum('NESTED_WITNESS_PUBKEY_HASH')).toEqual(
|
||||
1
|
||||
);
|
||||
expect(
|
||||
toLnrpcAddressTypeNum('HYBRID_NESTED_WITNESS_PUBKEY_HASH')
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
it('returns undefined for null / undefined input', () => {
|
||||
expect(toLnrpcAddressTypeNum(undefined)).toBeUndefined();
|
||||
expect(toLnrpcAddressTypeNum(null)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined for unrecognised non-numeric strings', () => {
|
||||
// protobufjs would silently encode garbage as 0 — better to
|
||||
// fall through to the backend default.
|
||||
expect(toLnrpcAddressTypeNum('SOMETHING_NEW')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('accepts numeric input directly', () => {
|
||||
expect(toLnrpcAddressTypeNum(0)).toEqual(0);
|
||||
expect(toLnrpcAddressTypeNum(1)).toEqual(1);
|
||||
expect(toLnrpcAddressTypeNum(4)).toEqual(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
// LND's lnrpc.NewAddress endpoint expects the AddressType enum by name.
|
||||
// Address types reach the backend in three forms:
|
||||
//
|
||||
// 1. The numeric strings used by the address-type picker / settings ('0',
|
||||
// '1', '4') — LND REST's grpc-gateway silently treats these as the
|
||||
// default (WITNESS_PUBKEY_HASH = native segwit) unless converted.
|
||||
// 2. The lnrpc enum names (already correct).
|
||||
// 3. The walletrpc enum names returned by ListAccounts and forwarded by
|
||||
// OnChainAddresses → Receive: NESTED_WITNESS_PUBKEY_HASH and
|
||||
// HYBRID_NESTED_WITNESS_PUBKEY_HASH. lnrpc.NewAddress has no hybrid
|
||||
// variant; the resulting address is still a nested-segwit p2sh-p2wkh,
|
||||
// which is the visible part the receiver cares about.
|
||||
const LNRPC_NEW_ADDRESS_TYPE_NAMES: { [key: string]: string } = {
|
||||
'0': 'WITNESS_PUBKEY_HASH',
|
||||
'1': 'NESTED_PUBKEY_HASH',
|
||||
'2': 'UNUSED_WITNESS_PUBKEY_HASH',
|
||||
'3': 'UNUSED_NESTED_PUBKEY_HASH',
|
||||
'4': 'TAPROOT_PUBKEY',
|
||||
'5': 'UNUSED_TAPROOT_PUBKEY',
|
||||
NESTED_WITNESS_PUBKEY_HASH: 'NESTED_PUBKEY_HASH',
|
||||
HYBRID_NESTED_WITNESS_PUBKEY_HASH: 'NESTED_PUBKEY_HASH'
|
||||
};
|
||||
|
||||
export const toLnrpcAddressType = (
|
||||
value: string | number | undefined | null
|
||||
): string | undefined => {
|
||||
if (value == null) return undefined;
|
||||
const key = String(value);
|
||||
return LNRPC_NEW_ADDRESS_TYPE_NAMES[key] ?? key;
|
||||
};
|
||||
|
||||
// Numeric form of the lnrpc enum, for the embedded LND backend.
|
||||
// protobufjs's NewAddressRequest encoder writes the `type` field via
|
||||
// `writer.int32(...)`, which does numeric coercion — enum-name strings
|
||||
// like 'NESTED_PUBKEY_HASH' become NaN and get serialized as 0
|
||||
// (= WITNESS_PUBKEY_HASH). Always send a number.
|
||||
const LNRPC_ADDRESS_TYPE_NUMS: { [key: string]: number } = {
|
||||
WITNESS_PUBKEY_HASH: 0,
|
||||
NESTED_PUBKEY_HASH: 1,
|
||||
UNUSED_WITNESS_PUBKEY_HASH: 2,
|
||||
UNUSED_NESTED_PUBKEY_HASH: 3,
|
||||
TAPROOT_PUBKEY: 4,
|
||||
UNUSED_TAPROOT_PUBKEY: 5
|
||||
};
|
||||
|
||||
export const toLnrpcAddressTypeNum = (
|
||||
value: string | number | undefined | null
|
||||
): number | undefined => {
|
||||
const name = toLnrpcAddressType(value);
|
||||
if (name == null) return undefined;
|
||||
if (LNRPC_ADDRESS_TYPE_NUMS[name] !== undefined)
|
||||
return LNRPC_ADDRESS_TYPE_NUMS[name];
|
||||
const num = Number(name);
|
||||
return Number.isInteger(num) ? num : undefined;
|
||||
};
|
||||
+42
-37
@@ -254,6 +254,38 @@ export default class Receive extends React.Component<
|
||||
return defaultInvoiceType === DefaultInvoiceType.Lightning ? 1 : 0;
|
||||
};
|
||||
|
||||
private getReceiveModeFlags = (): {
|
||||
lnOnly: boolean;
|
||||
onChainOnly: boolean;
|
||||
} => {
|
||||
const { route, SettingsStore } = this.props;
|
||||
const { posStatus, settings } = SettingsStore;
|
||||
const lnOnly =
|
||||
(settings &&
|
||||
posStatus === 'active' &&
|
||||
settings.pos?.confirmationPreference === 'lnOnly') ||
|
||||
!!route.params?.forceLn ||
|
||||
!BackendUtils.supportsOnchainReceiving();
|
||||
const onChainOnly = !!route.params?.forceOnChain;
|
||||
return { lnOnly, onChainOnly };
|
||||
};
|
||||
|
||||
private getSkipOnchain = (): boolean => {
|
||||
const { settings } = this.props.SettingsStore;
|
||||
return (
|
||||
settings?.invoices?.defaultInvoiceType !==
|
||||
DefaultInvoiceType.Unified
|
||||
);
|
||||
};
|
||||
|
||||
private getAddressType = (): string => {
|
||||
const { route, SettingsStore } = this.props;
|
||||
const { settings } = SettingsStore;
|
||||
return (
|
||||
route.params?.addressType || settings?.invoices?.addressType || '0'
|
||||
);
|
||||
};
|
||||
|
||||
async componentDidMount() {
|
||||
const {
|
||||
InvoicesStore,
|
||||
@@ -263,7 +295,7 @@ export default class Receive extends React.Component<
|
||||
route
|
||||
} = this.props;
|
||||
const { reset } = InvoicesStore;
|
||||
const { getSettings, posStatus } = SettingsStore;
|
||||
const { getSettings } = SettingsStore;
|
||||
const { status, lightningAddressHandle } = LightningAddressStore;
|
||||
|
||||
const settings = await getSettings();
|
||||
@@ -307,7 +339,7 @@ export default class Receive extends React.Component<
|
||||
false;
|
||||
|
||||
this.setState({
|
||||
addressType: settings?.invoices?.addressType || '0',
|
||||
addressType: this.getAddressType(),
|
||||
expirationIndex,
|
||||
memo: settings?.invoices?.memo || '',
|
||||
receiverName: settings?.invoices?.receiverName || '',
|
||||
@@ -322,16 +354,7 @@ export default class Receive extends React.Component<
|
||||
flowLspNotConfigured
|
||||
});
|
||||
|
||||
const lnOnly =
|
||||
(settings &&
|
||||
posStatus &&
|
||||
posStatus === 'active' &&
|
||||
settings.pos &&
|
||||
settings.pos.confirmationPreference &&
|
||||
settings.pos.confirmationPreference === 'lnOnly') ||
|
||||
route.params?.forceLn ||
|
||||
!BackendUtils.supportsOnchainReceiving();
|
||||
const onChainOnly = route.params?.forceOnChain;
|
||||
const { lnOnly, onChainOnly } = this.getReceiveModeFlags();
|
||||
|
||||
reset();
|
||||
|
||||
@@ -360,8 +383,7 @@ export default class Receive extends React.Component<
|
||||
this.setState({ selectedIndex: this.getDefaultIndex() });
|
||||
}
|
||||
|
||||
const addressType =
|
||||
route.params?.addressType || settings?.invoices?.addressType || '0';
|
||||
const addressType = this.getAddressType();
|
||||
|
||||
// POS
|
||||
const memo = route.params?.memo ?? this.state.memo;
|
||||
@@ -550,15 +572,12 @@ export default class Receive extends React.Component<
|
||||
addressType?: string,
|
||||
lspIsActive?: boolean
|
||||
) => {
|
||||
const { InvoicesStore, PosStore, SettingsStore } = this.props;
|
||||
const { InvoicesStore, PosStore } = this.props;
|
||||
const { receiverName, orderId, orderTip, exchangeRate } = this.state;
|
||||
// Use passed lspIsActive parameter, fall back to state if not provided
|
||||
const effectiveLspIsActive = lspIsActive ?? this.state.lspIsActive;
|
||||
const { createUnifiedInvoice } = InvoicesStore;
|
||||
const { settings } = SettingsStore;
|
||||
const skipOnchain =
|
||||
settings?.invoices?.defaultInvoiceType !==
|
||||
DefaultInvoiceType.Unified;
|
||||
const skipOnchain = this.getSkipOnchain();
|
||||
|
||||
// POS invoice reuse logic
|
||||
const checkExistingInvoice = async () => {
|
||||
@@ -679,14 +698,11 @@ export default class Receive extends React.Component<
|
||||
};
|
||||
|
||||
validateAddress = (text: string) => {
|
||||
const { navigation, InvoicesStore, SettingsStore, route } = this.props;
|
||||
const { navigation, InvoicesStore, route } = this.props;
|
||||
const { lspIsActive, receiverName } = this.state;
|
||||
const { createUnifiedInvoice } = InvoicesStore;
|
||||
const { settings } = SettingsStore;
|
||||
const satAmount = getSatAmount(route.params?.amount);
|
||||
const skipOnchain =
|
||||
settings?.invoices?.defaultInvoiceType !==
|
||||
DefaultInvoiceType.Unified;
|
||||
const skipOnchain = this.getSkipOnchain();
|
||||
|
||||
handleAnything(text, satAmount.toString())
|
||||
.then((response) => {
|
||||
@@ -1289,20 +1305,9 @@ export default class Receive extends React.Component<
|
||||
const showCustomPreimageField =
|
||||
settings?.invoices?.showCustomPreimageField;
|
||||
|
||||
const skipOnchain =
|
||||
settings?.invoices?.defaultInvoiceType !==
|
||||
DefaultInvoiceType.Unified;
|
||||
const skipOnchain = this.getSkipOnchain();
|
||||
|
||||
const lnOnly =
|
||||
(settings &&
|
||||
posStatus &&
|
||||
posStatus === 'active' &&
|
||||
settings.pos &&
|
||||
settings.pos.confirmationPreference &&
|
||||
settings.pos.confirmationPreference === 'lnOnly') ||
|
||||
route.params?.forceLn ||
|
||||
!BackendUtils.supportsOnchainReceiving();
|
||||
const onChainOnly = route.params?.forceOnChain;
|
||||
const { lnOnly, onChainOnly } = this.getReceiveModeFlags();
|
||||
|
||||
const lnurl = route.params?.lnurlParams;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user