diff --git a/backends/EmbeddedLND.ts b/backends/EmbeddedLND.ts index 7504b12b9..b267f0dfb 100644 --- a/backends/EmbeddedLND.ts +++ b/backends/EmbeddedLND.ts @@ -43,6 +43,7 @@ const { signMessageNodePubkey, verifyMessageNodePubkey, bumpFee, + bumpForceCloseFee, fundPsbt, signPsbt, finalizePsbt, @@ -273,6 +274,7 @@ export default class EmbeddedLND extends LND { getUTXOs = async (data: any) => await listUnspent(data); bumpFee = async (data: any) => await bumpFee(data); + bumpForceCloseFee = async (data: any) => await bumpForceCloseFee(data); lookupInvoice = async (data: any) => await lookupInvoice(data.r_hash); listAccounts = async () => await listAccounts(); diff --git a/backends/LND.ts b/backends/LND.ts index 4e8828601..db5321cd6 100644 --- a/backends/LND.ts +++ b/backends/LND.ts @@ -584,6 +584,8 @@ export default class LND { this.postRequest('/v1/funding/step', data); getUTXOs = (data: any) => this.postRequest('/v2/wallet/utxos', data); bumpFee = (data: any) => this.postRequest('/v2/wallet/bumpfee', data); + bumpForceCloseFee = (data: any) => + this.postRequest('/v2/wallet/BumpForceCloseFee', data); listAccounts = () => this.getRequest('/v2/wallet/accounts'); listAddresses = () => this.getRequest('/v2/wallet/addresses'); importAccount = (data: any) => diff --git a/backends/LightningNodeConnect.ts b/backends/LightningNodeConnect.ts index f14a91119..034623fd2 100644 --- a/backends/LightningNodeConnect.ts +++ b/backends/LightningNodeConnect.ts @@ -1,6 +1,7 @@ import { NativeModules, NativeEventEmitter } from 'react-native'; -import LNC, { lnrpc, walletrpc } from '../zeus_modules/@lightninglabs/lnc-rn'; +import LNC from '../zeus_modules/@lightninglabs/lnc-rn'; +import { lnrpc, walletrpc } from '../zeus_modules/@lightninglabs/lnc-core'; import { settingsStore, nodeInfoStore } from '../stores/storeInstances'; import CredentialStore from './LNC/credentialStore'; @@ -111,7 +112,7 @@ export default class LightningNodeConnect { .closedChannels({}) .then((data: lnrpc.ClosedChannelsResponse) => snakeize(data)); getChannelInfo = async (chanId: string) => { - const request: lnrpc.ChanInfoRequest = { chanId }; + const request: lnrpc.ChanInfoRequest = { chanId, chanPoint: '' }; return await this.lnc.lnd.lightning .getChanInfo(request) .then((data: lnrpc.ChannelEdge) => snakeize(data)); @@ -456,6 +457,12 @@ export default class LightningNodeConnect { await this.lnc.lnd.walletKit .bumpFee(snakeize(req)) .then((data: walletrpc.BumpFeeResponse) => snakeize(data)); + bumpForceCloseFee = async (req: walletrpc.BumpForceCloseFeeRequest) => + await this.lnc.lnd.walletKit + .bumpForceCloseFee(snakeize(req)) + .then((data: walletrpc.BumpForceCloseFeeResponse) => + snakeize(data) + ); listAccounts = async () => await this.lnc.lnd.walletKit .listAccounts({}) diff --git a/components/Modals/InfoModal.tsx b/components/Modals/InfoModal.tsx index ef9fa9e5a..1e0fb44f6 100644 --- a/components/Modals/InfoModal.tsx +++ b/components/Modals/InfoModal.tsx @@ -77,7 +77,8 @@ export default class InfoModal extends React.Component { style={{ fontFamily: 'PPNeueMontreal-Book', color: themeColor('text'), - fontSize: 20, + fontSize: + infoModalText.length > 3 ? 18 : 20, marginBottom: 40 }} > diff --git a/lndmobile/LndMobileInjection.ts b/lndmobile/LndMobileInjection.ts index 25b28eb1f..87db8b274 100644 --- a/lndmobile/LndMobileInjection.ts +++ b/lndmobile/LndMobileInjection.ts @@ -84,6 +84,7 @@ import { signMessage, signMessageNodePubkey, bumpFee, + bumpForceCloseFee, fundPsbt, signPsbt, finalizePsbt, @@ -393,15 +394,28 @@ export interface ILndMobileInjections { ) => Promise; bumpFee: ({ outpoint, - target_conf, - force, - sat_per_vbyte + immediate, + sat_per_vbyte, + budget }: { outpoint: lnrpc.OutPoint; - target_conf?: number; - force?: boolean; + immediate?: boolean; sat_per_vbyte?: Long; + budget?: Long; }) => Promise; + bumpForceCloseFee: ({ + chan_point, + target_conf, + immediate, + starting_feerate, + budget + }: { + chan_point: lnrpc.OutPoint; + target_conf?: number; + immediate?: boolean; + starting_feerate?: Long; + budget?: Long; + }) => Promise; fundPsbt: ({ account, psbt, @@ -550,6 +564,7 @@ export default { signMessage, signMessageNodePubkey, bumpFee, + bumpForceCloseFee, fundPsbt, signPsbt, finalizePsbt, diff --git a/lndmobile/wallet.ts b/lndmobile/wallet.ts index b14514c3c..853eb540a 100644 --- a/lndmobile/wallet.ts +++ b/lndmobile/wallet.ts @@ -13,19 +13,24 @@ import Base64Utils from '../utils/Base64Utils'; export const bumpFee = async ({ outpoint, target_conf, - force, - sat_per_vbyte + immediate, + sat_per_vbyte, + budget }: { outpoint: lnrpc.OutPoint; target_conf?: number; - force?: boolean; + immediate?: boolean; sat_per_vbyte?: Long; + budget?: Long; }): Promise => { const options: walletrpc.IBumpFeeRequest = { outpoint, target_conf, - force, - sat_per_vbyte: sat_per_vbyte ? Long.fromValue(sat_per_vbyte) : undefined + immediate, + sat_per_vbyte: sat_per_vbyte + ? Long.fromValue(sat_per_vbyte) + : undefined, + budget: budget ? Long.fromValue(budget) : undefined }; const response = await sendCommand< walletrpc.IBumpFeeRequest, @@ -40,6 +45,41 @@ export const bumpFee = async ({ return response; }; +/** + * @throws + */ +export const bumpForceCloseFee = async ({ + chan_point, + immediate, + starting_feerate, + budget +}: { + chan_point: lnrpc.ChannelPoint; + immediate?: boolean; + starting_feerate?: Long; + budget?: Long; +}): Promise => { + const options: walletrpc.IBumpForceCloseFeeRequest = { + chan_point, + immediate, + starting_feerate: starting_feerate + ? Long.fromValue(starting_feerate) + : undefined, + budget: budget ? Long.fromValue(budget) : undefined + }; + const response = await sendCommand< + walletrpc.IBumpForceCloseFeeRequest, + walletrpc.BumpForceCloseFeeRequest, + walletrpc.BumpForceCloseFeeResponse + >({ + request: walletrpc.BumpForceCloseFeeRequest, + response: walletrpc.BumpForceCloseFeeResponse, + method: 'WalletKitBumpForceCloseFee', + options + }); + return response; +}; + /** * @throws */ diff --git a/locales/en.json b/locales/en.json index 6d4f16425..41fe6366e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -64,6 +64,7 @@ "general.true": "True", "general.false": "False", "general.force": "Force", + "general.immediate": "Immediate", "general.proceed": "Proceed", "general.fiatFetchError": "Error fetching exchange rates", "general.iUnderstand": "I understand", @@ -1001,9 +1002,21 @@ "views.Mortals.title": "Mortals", "views.BumpFee.title": "Speed up transaction", "views.BumpFee.titleAlt": "Speed up channel open", + "views.BumpFee.titleClose": "Speed up channel close", + "views.BumpFee.titleForceClose": "Speed up channel force close", "views.BumpFee.targetConfs": "Target confirmations", "views.BumpFee.success": "Successfully bumped fee!", "views.BumpFee.error": "Error bumping fee", + "views.BumpFee.outpoint.explainer1": "An outpoint is a reference to a particular output of a previous transaction. It consists of two parts:", + "views.BumpFee.outpoint.explainer2": "1) Transaction ID (txid): This is a unique identifier for a specific transaction on the Bitcoin blockchain.", + "views.BumpFee.outpoint.explainer3": "2) Output Index (vout): This denotes which specific output of the referenced transaction is being spent.", + "views.BumpFee.outpoint.explainer4": "The two are combined with a : character, for example: a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d:0", + "views.BumpFee.outpoint.explainer5": "This is the first output in this transaction, as the output index starts at 0. The second output has vout 1, etc.", + "views.BumpFee.budget": "Budget (sats)", + "views.BumpFee.budget.explainer1": "The max amount in sats that can be used as the fees. Setting this value greater than the input's value may result in CPFP - one or more wallet utxos will be used to pay the fees specified by the budget.", + "views.BumpFee.budget.explainer2": "If not set, for new inputs, by default 50% of the input's value will be treated as the budget for fee bumping; for existing inputs, their current budgets will be retained.", + "views.BumpFee.immediate.explainer": "Whether this input will be swept immediately. When enabled, the sweeper will sweep this input without waiting for the next batch.", + "views.BumpFee.startingFee": "Starting fee (sat/vbyte)", "views.Sync.title": "Syncing wallet", "views.Sync.currentBlockHeight": "Current block height", "views.Sync.tip": "Tip", diff --git a/proto/lightning.d.ts b/proto/lightning.d.ts index 380602c9e..78c7da118 100644 --- a/proto/lightning.d.ts +++ b/proto/lightning.d.ts @@ -48445,6 +48445,25 @@ export namespace walletrpc { request: walletrpc.IBumpFeeRequest ): Promise; + /** + * Calls BumpForceCloseFee. + * @param request BumpForceCloseFeeRequest message or plain object + * @param callback Node-style callback called with the error, if any, and BumpForceCloseFeeResponse + */ + public bumpForceCloseFee( + request: walletrpc.IBumpForceCloseFeeRequest, + callback: walletrpc.WalletKit.BumpForceCloseFeeCallback + ): void; + + /** + * Calls BumpForceCloseFee. + * @param request BumpForceCloseFeeRequest message or plain object + * @returns Promise + */ + public bumpForceCloseFee( + request: walletrpc.IBumpForceCloseFeeRequest + ): Promise; + /** * Calls ListSweeps. * @param request ListSweepsRequest message or plain object @@ -48781,6 +48800,16 @@ export namespace walletrpc { response?: walletrpc.BumpFeeResponse ) => void; + /** + * Callback as used by {@link walletrpc.WalletKit#bumpForceCloseFee}. + * @param error Error, if any + * @param [response] BumpForceCloseFeeResponse + */ + type BumpForceCloseFeeCallback = ( + error: Error | null, + response?: walletrpc.BumpForceCloseFeeResponse + ) => void; + /** * Callback as used by {@link walletrpc.WalletKit#listSweeps}. * @param error Error, if any @@ -54506,6 +54535,256 @@ export namespace walletrpc { public static getTypeUrl(typeUrlPrefix?: string): string; } + /** Properties of a BumpForceCloseFeeRequest. */ + interface IBumpForceCloseFeeRequest { + /** BumpForceCloseFeeRequest chan_point */ + chan_point?: lnrpc.IChannelPoint | null; + + /** BumpForceCloseFeeRequest deadline_delta */ + deadline_delta?: number | null; + + /** BumpForceCloseFeeRequest starting_feerate */ + starting_feerate?: Long | null; + + /** BumpForceCloseFeeRequest immediate */ + immediate?: boolean | null; + + /** BumpForceCloseFeeRequest budget */ + budget?: Long | null; + } + + /** Represents a BumpForceCloseFeeRequest. */ + class BumpForceCloseFeeRequest implements IBumpForceCloseFeeRequest { + /** + * Constructs a new BumpForceCloseFeeRequest. + * @param [properties] Properties to set + */ + constructor(properties?: walletrpc.IBumpForceCloseFeeRequest); + + /** BumpForceCloseFeeRequest chan_point. */ + public chan_point?: lnrpc.IChannelPoint | null; + + /** BumpForceCloseFeeRequest deadline_delta. */ + public deadline_delta: number; + + /** BumpForceCloseFeeRequest starting_feerate. */ + public starting_feerate: Long; + + /** BumpForceCloseFeeRequest immediate. */ + public immediate: boolean; + + /** BumpForceCloseFeeRequest budget. */ + public budget: Long; + + /** + * Creates a new BumpForceCloseFeeRequest instance using the specified properties. + * @param [properties] Properties to set + * @returns BumpForceCloseFeeRequest instance + */ + public static create( + properties?: walletrpc.IBumpForceCloseFeeRequest + ): walletrpc.BumpForceCloseFeeRequest; + + /** + * Encodes the specified BumpForceCloseFeeRequest message. Does not implicitly {@link walletrpc.BumpForceCloseFeeRequest.verify|verify} messages. + * @param message BumpForceCloseFeeRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode( + message: walletrpc.IBumpForceCloseFeeRequest, + writer?: $protobuf.Writer + ): $protobuf.Writer; + + /** + * Encodes the specified BumpForceCloseFeeRequest message, length delimited. Does not implicitly {@link walletrpc.BumpForceCloseFeeRequest.verify|verify} messages. + * @param message BumpForceCloseFeeRequest message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited( + message: walletrpc.IBumpForceCloseFeeRequest, + writer?: $protobuf.Writer + ): $protobuf.Writer; + + /** + * Decodes a BumpForceCloseFeeRequest message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns BumpForceCloseFeeRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode( + reader: $protobuf.Reader | Uint8Array, + length?: number + ): walletrpc.BumpForceCloseFeeRequest; + + /** + * Decodes a BumpForceCloseFeeRequest message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns BumpForceCloseFeeRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited( + reader: $protobuf.Reader | Uint8Array + ): walletrpc.BumpForceCloseFeeRequest; + + /** + * Verifies a BumpForceCloseFeeRequest message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): string | null; + + /** + * Creates a BumpForceCloseFeeRequest message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns BumpForceCloseFeeRequest + */ + public static fromObject(object: { + [k: string]: any; + }): walletrpc.BumpForceCloseFeeRequest; + + /** + * Creates a plain object from a BumpForceCloseFeeRequest message. Also converts values to other types if specified. + * @param message BumpForceCloseFeeRequest + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject( + message: walletrpc.BumpForceCloseFeeRequest, + options?: $protobuf.IConversionOptions + ): { [k: string]: any }; + + /** + * Converts this BumpForceCloseFeeRequest to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for BumpForceCloseFeeRequest + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + + /** Properties of a BumpForceCloseFeeResponse. */ + interface IBumpForceCloseFeeResponse { + /** BumpForceCloseFeeResponse status */ + status?: string | null; + } + + /** Represents a BumpForceCloseFeeResponse. */ + class BumpForceCloseFeeResponse implements IBumpForceCloseFeeResponse { + /** + * Constructs a new BumpForceCloseFeeResponse. + * @param [properties] Properties to set + */ + constructor(properties?: walletrpc.IBumpForceCloseFeeResponse); + + /** BumpForceCloseFeeResponse status. */ + public status: string; + + /** + * Creates a new BumpForceCloseFeeResponse instance using the specified properties. + * @param [properties] Properties to set + * @returns BumpForceCloseFeeResponse instance + */ + public static create( + properties?: walletrpc.IBumpForceCloseFeeResponse + ): walletrpc.BumpForceCloseFeeResponse; + + /** + * Encodes the specified BumpForceCloseFeeResponse message. Does not implicitly {@link walletrpc.BumpForceCloseFeeResponse.verify|verify} messages. + * @param message BumpForceCloseFeeResponse message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode( + message: walletrpc.IBumpForceCloseFeeResponse, + writer?: $protobuf.Writer + ): $protobuf.Writer; + + /** + * Encodes the specified BumpForceCloseFeeResponse message, length delimited. Does not implicitly {@link walletrpc.BumpForceCloseFeeResponse.verify|verify} messages. + * @param message BumpForceCloseFeeResponse message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited( + message: walletrpc.IBumpForceCloseFeeResponse, + writer?: $protobuf.Writer + ): $protobuf.Writer; + + /** + * Decodes a BumpForceCloseFeeResponse message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns BumpForceCloseFeeResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode( + reader: $protobuf.Reader | Uint8Array, + length?: number + ): walletrpc.BumpForceCloseFeeResponse; + + /** + * Decodes a BumpForceCloseFeeResponse message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns BumpForceCloseFeeResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited( + reader: $protobuf.Reader | Uint8Array + ): walletrpc.BumpForceCloseFeeResponse; + + /** + * Verifies a BumpForceCloseFeeResponse message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): string | null; + + /** + * Creates a BumpForceCloseFeeResponse message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns BumpForceCloseFeeResponse + */ + public static fromObject(object: { + [k: string]: any; + }): walletrpc.BumpForceCloseFeeResponse; + + /** + * Creates a plain object from a BumpForceCloseFeeResponse message. Also converts values to other types if specified. + * @param message BumpForceCloseFeeResponse + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject( + message: walletrpc.BumpForceCloseFeeResponse, + options?: $protobuf.IConversionOptions + ): { [k: string]: any }; + + /** + * Converts this BumpForceCloseFeeResponse to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + + /** + * Gets the default type url for BumpForceCloseFeeResponse + * @param [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns The default type url + */ + public static getTypeUrl(typeUrlPrefix?: string): string; + } + /** Properties of a ListSweepsRequest. */ interface IListSweepsRequest { /** ListSweepsRequest verbose */ diff --git a/proto/lightning.js b/proto/lightning.js index 5dccd2058..fc94114c5 100644 --- a/proto/lightning.js +++ b/proto/lightning.js @@ -108503,6 +108503,39 @@ export const walletrpc = $root.walletrpc = (() => { * @variation 2 */ + /** + * Callback as used by {@link walletrpc.WalletKit#bumpForceCloseFee}. + * @memberof walletrpc.WalletKit + * @typedef BumpForceCloseFeeCallback + * @type {function} + * @param {Error|null} error Error, if any + * @param {walletrpc.BumpForceCloseFeeResponse} [response] BumpForceCloseFeeResponse + */ + + /** + * Calls BumpForceCloseFee. + * @function bumpForceCloseFee + * @memberof walletrpc.WalletKit + * @instance + * @param {walletrpc.IBumpForceCloseFeeRequest} request BumpForceCloseFeeRequest message or plain object + * @param {walletrpc.WalletKit.BumpForceCloseFeeCallback} callback Node-style callback called with the error, if any, and BumpForceCloseFeeResponse + * @returns {undefined} + * @variation 1 + */ + Object.defineProperty(WalletKit.prototype.bumpForceCloseFee = function bumpForceCloseFee(request, callback) { + return this.rpcCall(bumpForceCloseFee, $root.walletrpc.BumpForceCloseFeeRequest, $root.walletrpc.BumpForceCloseFeeResponse, request, callback); + }, "name", { value: "BumpForceCloseFee" }); + + /** + * Calls BumpForceCloseFee. + * @function bumpForceCloseFee + * @memberof walletrpc.WalletKit + * @instance + * @param {walletrpc.IBumpForceCloseFeeRequest} request BumpForceCloseFeeRequest message or plain object + * @returns {Promise} Promise + * @variation 2 + */ + /** * Callback as used by {@link walletrpc.WalletKit#listSweeps}. * @memberof walletrpc.WalletKit @@ -120691,6 +120724,538 @@ export const walletrpc = $root.walletrpc = (() => { return BumpFeeResponse; })(); + walletrpc.BumpForceCloseFeeRequest = (function() { + + /** + * Properties of a BumpForceCloseFeeRequest. + * @memberof walletrpc + * @interface IBumpForceCloseFeeRequest + * @property {lnrpc.IChannelPoint|null} [chan_point] BumpForceCloseFeeRequest chan_point + * @property {number|null} [deadline_delta] BumpForceCloseFeeRequest deadline_delta + * @property {Long|null} [starting_feerate] BumpForceCloseFeeRequest starting_feerate + * @property {boolean|null} [immediate] BumpForceCloseFeeRequest immediate + * @property {Long|null} [budget] BumpForceCloseFeeRequest budget + */ + + /** + * Constructs a new BumpForceCloseFeeRequest. + * @memberof walletrpc + * @classdesc Represents a BumpForceCloseFeeRequest. + * @implements IBumpForceCloseFeeRequest + * @constructor + * @param {walletrpc.IBumpForceCloseFeeRequest=} [properties] Properties to set + */ + function BumpForceCloseFeeRequest(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BumpForceCloseFeeRequest chan_point. + * @member {lnrpc.IChannelPoint|null|undefined} chan_point + * @memberof walletrpc.BumpForceCloseFeeRequest + * @instance + */ + BumpForceCloseFeeRequest.prototype.chan_point = null; + + /** + * BumpForceCloseFeeRequest deadline_delta. + * @member {number} deadline_delta + * @memberof walletrpc.BumpForceCloseFeeRequest + * @instance + */ + BumpForceCloseFeeRequest.prototype.deadline_delta = 0; + + /** + * BumpForceCloseFeeRequest starting_feerate. + * @member {Long} starting_feerate + * @memberof walletrpc.BumpForceCloseFeeRequest + * @instance + */ + BumpForceCloseFeeRequest.prototype.starting_feerate = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * BumpForceCloseFeeRequest immediate. + * @member {boolean} immediate + * @memberof walletrpc.BumpForceCloseFeeRequest + * @instance + */ + BumpForceCloseFeeRequest.prototype.immediate = false; + + /** + * BumpForceCloseFeeRequest budget. + * @member {Long} budget + * @memberof walletrpc.BumpForceCloseFeeRequest + * @instance + */ + BumpForceCloseFeeRequest.prototype.budget = $util.Long ? $util.Long.fromBits(0,0,true) : 0; + + /** + * Creates a new BumpForceCloseFeeRequest instance using the specified properties. + * @function create + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {walletrpc.IBumpForceCloseFeeRequest=} [properties] Properties to set + * @returns {walletrpc.BumpForceCloseFeeRequest} BumpForceCloseFeeRequest instance + */ + BumpForceCloseFeeRequest.create = function create(properties) { + return new BumpForceCloseFeeRequest(properties); + }; + + /** + * Encodes the specified BumpForceCloseFeeRequest message. Does not implicitly {@link walletrpc.BumpForceCloseFeeRequest.verify|verify} messages. + * @function encode + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {walletrpc.IBumpForceCloseFeeRequest} message BumpForceCloseFeeRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BumpForceCloseFeeRequest.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.chan_point != null && Object.hasOwnProperty.call(message, "chan_point")) + $root.lnrpc.ChannelPoint.encode(message.chan_point, writer.uint32(/* id 1, wireType 2 =*/10).fork()).ldelim(); + if (message.deadline_delta != null && Object.hasOwnProperty.call(message, "deadline_delta")) + writer.uint32(/* id 2, wireType 0 =*/16).uint32(message.deadline_delta); + if (message.starting_feerate != null && Object.hasOwnProperty.call(message, "starting_feerate")) + writer.uint32(/* id 3, wireType 0 =*/24).uint64(message.starting_feerate); + if (message.immediate != null && Object.hasOwnProperty.call(message, "immediate")) + writer.uint32(/* id 4, wireType 0 =*/32).bool(message.immediate); + if (message.budget != null && Object.hasOwnProperty.call(message, "budget")) + writer.uint32(/* id 5, wireType 0 =*/40).uint64(message.budget); + return writer; + }; + + /** + * Encodes the specified BumpForceCloseFeeRequest message, length delimited. Does not implicitly {@link walletrpc.BumpForceCloseFeeRequest.verify|verify} messages. + * @function encodeDelimited + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {walletrpc.IBumpForceCloseFeeRequest} message BumpForceCloseFeeRequest message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BumpForceCloseFeeRequest.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BumpForceCloseFeeRequest message from the specified reader or buffer. + * @function decode + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {walletrpc.BumpForceCloseFeeRequest} BumpForceCloseFeeRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BumpForceCloseFeeRequest.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.walletrpc.BumpForceCloseFeeRequest(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.chan_point = $root.lnrpc.ChannelPoint.decode(reader, reader.uint32()); + break; + } + case 2: { + message.deadline_delta = reader.uint32(); + break; + } + case 3: { + message.starting_feerate = reader.uint64(); + break; + } + case 4: { + message.immediate = reader.bool(); + break; + } + case 5: { + message.budget = reader.uint64(); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BumpForceCloseFeeRequest message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {walletrpc.BumpForceCloseFeeRequest} BumpForceCloseFeeRequest + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BumpForceCloseFeeRequest.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BumpForceCloseFeeRequest message. + * @function verify + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BumpForceCloseFeeRequest.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.chan_point != null && message.hasOwnProperty("chan_point")) { + let error = $root.lnrpc.ChannelPoint.verify(message.chan_point); + if (error) + return "chan_point." + error; + } + if (message.deadline_delta != null && message.hasOwnProperty("deadline_delta")) + if (!$util.isInteger(message.deadline_delta)) + return "deadline_delta: integer expected"; + if (message.starting_feerate != null && message.hasOwnProperty("starting_feerate")) + if (!$util.isInteger(message.starting_feerate) && !(message.starting_feerate && $util.isInteger(message.starting_feerate.low) && $util.isInteger(message.starting_feerate.high))) + return "starting_feerate: integer|Long expected"; + if (message.immediate != null && message.hasOwnProperty("immediate")) + if (typeof message.immediate !== "boolean") + return "immediate: boolean expected"; + if (message.budget != null && message.hasOwnProperty("budget")) + if (!$util.isInteger(message.budget) && !(message.budget && $util.isInteger(message.budget.low) && $util.isInteger(message.budget.high))) + return "budget: integer|Long expected"; + return null; + }; + + /** + * Creates a BumpForceCloseFeeRequest message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {Object.} object Plain object + * @returns {walletrpc.BumpForceCloseFeeRequest} BumpForceCloseFeeRequest + */ + BumpForceCloseFeeRequest.fromObject = function fromObject(object) { + if (object instanceof $root.walletrpc.BumpForceCloseFeeRequest) + return object; + let message = new $root.walletrpc.BumpForceCloseFeeRequest(); + if (object.chan_point != null) { + if (typeof object.chan_point !== "object") + throw TypeError(".walletrpc.BumpForceCloseFeeRequest.chan_point: object expected"); + message.chan_point = $root.lnrpc.ChannelPoint.fromObject(object.chan_point); + } + if (object.deadline_delta != null) + message.deadline_delta = object.deadline_delta >>> 0; + if (object.starting_feerate != null) + if ($util.Long) + (message.starting_feerate = $util.Long.fromValue(object.starting_feerate)).unsigned = true; + else if (typeof object.starting_feerate === "string") + message.starting_feerate = parseInt(object.starting_feerate, 10); + else if (typeof object.starting_feerate === "number") + message.starting_feerate = object.starting_feerate; + else if (typeof object.starting_feerate === "object") + message.starting_feerate = new $util.LongBits(object.starting_feerate.low >>> 0, object.starting_feerate.high >>> 0).toNumber(true); + if (object.immediate != null) + message.immediate = Boolean(object.immediate); + if (object.budget != null) + if ($util.Long) + (message.budget = $util.Long.fromValue(object.budget)).unsigned = true; + else if (typeof object.budget === "string") + message.budget = parseInt(object.budget, 10); + else if (typeof object.budget === "number") + message.budget = object.budget; + else if (typeof object.budget === "object") + message.budget = new $util.LongBits(object.budget.low >>> 0, object.budget.high >>> 0).toNumber(true); + return message; + }; + + /** + * Creates a plain object from a BumpForceCloseFeeRequest message. Also converts values to other types if specified. + * @function toObject + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {walletrpc.BumpForceCloseFeeRequest} message BumpForceCloseFeeRequest + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BumpForceCloseFeeRequest.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) { + object.chan_point = null; + object.deadline_delta = 0; + if ($util.Long) { + let long = new $util.Long(0, 0, true); + object.starting_feerate = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.starting_feerate = options.longs === String ? "0" : 0; + object.immediate = false; + if ($util.Long) { + let long = new $util.Long(0, 0, true); + object.budget = options.longs === String ? long.toString() : options.longs === Number ? long.toNumber() : long; + } else + object.budget = options.longs === String ? "0" : 0; + } + if (message.chan_point != null && message.hasOwnProperty("chan_point")) + object.chan_point = $root.lnrpc.ChannelPoint.toObject(message.chan_point, options); + if (message.deadline_delta != null && message.hasOwnProperty("deadline_delta")) + object.deadline_delta = message.deadline_delta; + if (message.starting_feerate != null && message.hasOwnProperty("starting_feerate")) + if (typeof message.starting_feerate === "number") + object.starting_feerate = options.longs === String ? String(message.starting_feerate) : message.starting_feerate; + else + object.starting_feerate = options.longs === String ? $util.Long.prototype.toString.call(message.starting_feerate) : options.longs === Number ? new $util.LongBits(message.starting_feerate.low >>> 0, message.starting_feerate.high >>> 0).toNumber(true) : message.starting_feerate; + if (message.immediate != null && message.hasOwnProperty("immediate")) + object.immediate = message.immediate; + if (message.budget != null && message.hasOwnProperty("budget")) + if (typeof message.budget === "number") + object.budget = options.longs === String ? String(message.budget) : message.budget; + else + object.budget = options.longs === String ? $util.Long.prototype.toString.call(message.budget) : options.longs === Number ? new $util.LongBits(message.budget.low >>> 0, message.budget.high >>> 0).toNumber(true) : message.budget; + return object; + }; + + /** + * Converts this BumpForceCloseFeeRequest to JSON. + * @function toJSON + * @memberof walletrpc.BumpForceCloseFeeRequest + * @instance + * @returns {Object.} JSON object + */ + BumpForceCloseFeeRequest.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for BumpForceCloseFeeRequest + * @function getTypeUrl + * @memberof walletrpc.BumpForceCloseFeeRequest + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + BumpForceCloseFeeRequest.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/walletrpc.BumpForceCloseFeeRequest"; + }; + + return BumpForceCloseFeeRequest; + })(); + + walletrpc.BumpForceCloseFeeResponse = (function() { + + /** + * Properties of a BumpForceCloseFeeResponse. + * @memberof walletrpc + * @interface IBumpForceCloseFeeResponse + * @property {string|null} [status] BumpForceCloseFeeResponse status + */ + + /** + * Constructs a new BumpForceCloseFeeResponse. + * @memberof walletrpc + * @classdesc Represents a BumpForceCloseFeeResponse. + * @implements IBumpForceCloseFeeResponse + * @constructor + * @param {walletrpc.IBumpForceCloseFeeResponse=} [properties] Properties to set + */ + function BumpForceCloseFeeResponse(properties) { + if (properties) + for (let keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * BumpForceCloseFeeResponse status. + * @member {string} status + * @memberof walletrpc.BumpForceCloseFeeResponse + * @instance + */ + BumpForceCloseFeeResponse.prototype.status = ""; + + /** + * Creates a new BumpForceCloseFeeResponse instance using the specified properties. + * @function create + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {walletrpc.IBumpForceCloseFeeResponse=} [properties] Properties to set + * @returns {walletrpc.BumpForceCloseFeeResponse} BumpForceCloseFeeResponse instance + */ + BumpForceCloseFeeResponse.create = function create(properties) { + return new BumpForceCloseFeeResponse(properties); + }; + + /** + * Encodes the specified BumpForceCloseFeeResponse message. Does not implicitly {@link walletrpc.BumpForceCloseFeeResponse.verify|verify} messages. + * @function encode + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {walletrpc.IBumpForceCloseFeeResponse} message BumpForceCloseFeeResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BumpForceCloseFeeResponse.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.status != null && Object.hasOwnProperty.call(message, "status")) + writer.uint32(/* id 1, wireType 2 =*/10).string(message.status); + return writer; + }; + + /** + * Encodes the specified BumpForceCloseFeeResponse message, length delimited. Does not implicitly {@link walletrpc.BumpForceCloseFeeResponse.verify|verify} messages. + * @function encodeDelimited + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {walletrpc.IBumpForceCloseFeeResponse} message BumpForceCloseFeeResponse message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + BumpForceCloseFeeResponse.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a BumpForceCloseFeeResponse message from the specified reader or buffer. + * @function decode + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {walletrpc.BumpForceCloseFeeResponse} BumpForceCloseFeeResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BumpForceCloseFeeResponse.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + let end = length === undefined ? reader.len : reader.pos + length, message = new $root.walletrpc.BumpForceCloseFeeResponse(); + while (reader.pos < end) { + let tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + message.status = reader.string(); + break; + } + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a BumpForceCloseFeeResponse message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {walletrpc.BumpForceCloseFeeResponse} BumpForceCloseFeeResponse + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + BumpForceCloseFeeResponse.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a BumpForceCloseFeeResponse message. + * @function verify + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + BumpForceCloseFeeResponse.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.status != null && message.hasOwnProperty("status")) + if (!$util.isString(message.status)) + return "status: string expected"; + return null; + }; + + /** + * Creates a BumpForceCloseFeeResponse message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {Object.} object Plain object + * @returns {walletrpc.BumpForceCloseFeeResponse} BumpForceCloseFeeResponse + */ + BumpForceCloseFeeResponse.fromObject = function fromObject(object) { + if (object instanceof $root.walletrpc.BumpForceCloseFeeResponse) + return object; + let message = new $root.walletrpc.BumpForceCloseFeeResponse(); + if (object.status != null) + message.status = String(object.status); + return message; + }; + + /** + * Creates a plain object from a BumpForceCloseFeeResponse message. Also converts values to other types if specified. + * @function toObject + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {walletrpc.BumpForceCloseFeeResponse} message BumpForceCloseFeeResponse + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + BumpForceCloseFeeResponse.toObject = function toObject(message, options) { + if (!options) + options = {}; + let object = {}; + if (options.defaults) + object.status = ""; + if (message.status != null && message.hasOwnProperty("status")) + object.status = message.status; + return object; + }; + + /** + * Converts this BumpForceCloseFeeResponse to JSON. + * @function toJSON + * @memberof walletrpc.BumpForceCloseFeeResponse + * @instance + * @returns {Object.} JSON object + */ + BumpForceCloseFeeResponse.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + /** + * Gets the default type url for BumpForceCloseFeeResponse + * @function getTypeUrl + * @memberof walletrpc.BumpForceCloseFeeResponse + * @static + * @param {string} [typeUrlPrefix] your custom typeUrlPrefix(default "type.googleapis.com") + * @returns {string} The default type url + */ + BumpForceCloseFeeResponse.getTypeUrl = function getTypeUrl(typeUrlPrefix) { + if (typeUrlPrefix === undefined) { + typeUrlPrefix = "type.googleapis.com"; + } + return typeUrlPrefix + "/walletrpc.BumpForceCloseFeeResponse"; + }; + + return BumpForceCloseFeeResponse; + })(); + walletrpc.ListSweepsRequest = (function() { /** diff --git a/proto/walletrpc/walletkit.proto b/proto/walletrpc/walletkit.proto index 513e4f8ee..b865fb2d2 100644 --- a/proto/walletrpc/walletkit.proto +++ b/proto/walletrpc/walletkit.proto @@ -273,6 +273,13 @@ service WalletKit { */ rpc BumpFee (BumpFeeRequest) returns (BumpFeeResponse); + /* lncli: `wallet bumpforceclosefee` + BumpForceCloseFee is an endpoint that allows users to bump the fee of a + channel force close. This only works for channels with option_anchors. + */ + rpc BumpForceCloseFee (BumpForceCloseFeeRequest) + returns (BumpForceCloseFeeResponse); + /* lncli: `wallet listsweeps` ListSweeps returns a list of the sweep transactions our node has produced. Note that these sweeps may not be confirmed yet, as we record sweeps on @@ -1241,6 +1248,46 @@ message BumpFeeResponse { string status = 1; } +message BumpForceCloseFeeRequest { + // The channel point which force close transaction we are attempting to + // bump the fee rate for. + lnrpc.ChannelPoint chan_point = 1; + + // Optional. The deadline delta in number of blocks that the anchor output + // should be spent within to bump the closing transaction. + uint32 deadline_delta = 2; + + /* + Optional. The starting fee rate, expressed in sat/vbyte. This value will be + used by the sweeper's fee function as its starting fee rate. When not set, + the sweeper will use the estimated fee rate using the target_conf as the + starting fee rate. + */ + uint64 starting_feerate = 3; + + /* + Optional. Whether this cpfp transaction will be triggered immediately. When + set to true, the sweeper will consider all currently registered sweeps and + trigger new batch transactions including the sweeping of the anchor output + related to the selected force close transaction. + */ + bool immediate = 4; + + /* + Optional. The max amount in sats that can be used as the fees. For already + registered anchor outputs if not set explicitly the old value will be used. + For channel force closes which have no HTLCs in their commitment transaction + this value has to be set to an appropriate amount to pay for the cpfp + transaction of the force closed channel otherwise the fee bumping will fail. + */ + uint64 budget = 5; +} + +message BumpForceCloseFeeResponse { + // The status of the force close fee bump operation. + string status = 1; +} + message ListSweepsRequest { /* Retrieve the full sweep transaction details. If false, only the sweep txids diff --git a/stores/FeeStore.ts b/stores/FeeStore.ts index 13843862d..ce6aceb83 100644 --- a/stores/FeeStore.ts +++ b/stores/FeeStore.ts @@ -82,7 +82,12 @@ export default class FeeStore { this.fees = {}; this.loadingFees = false; this.bumpFeeSuccess = false; + this.resetErrors(); + }; + + resetErrors = () => { this.bumpFeeError = false; + this.bumpFeeErrorMsg = ''; }; @action @@ -244,6 +249,30 @@ export default class FeeStore { }); }; + @action + public bumpForceCloseFee = (params?: any) => { + this.loading = true; + this.bumpFeeSuccess = false; + this.bumpFeeError = false; + const [funding_txid_str, output_index] = params.chan_point.split(':'); + BackendUtils.bumpForceCloseFee({ + ...params, + chan_point: { + funding_txid_str, + output_index: Number(output_index) || 0 + } + }) + .then(() => { + this.bumpFeeSuccess = true; + this.loading = false; + }) + .catch((err: Error) => { + this.bumpFeeError = true; + this.bumpFeeErrorMsg = errorToUserFriendly(err); + this.loading = false; + }); + }; + @action public bumpFeeOpeningChannel = (params?: any) => { this.loading = true; @@ -265,8 +294,11 @@ export default class FeeStore { // if output isn't correct (it'll be index 0 or 1), try alternate input // NOTE: this will only work for single-party funded channels if ( - err.toString() === - 'Error: the passed output does not belong to the wallet' + err + .toString() + .includes( + 'the passed output does not belong to the wallet' + ) ) { const newOutputIndex = output_index === '0' ? 1 : 0; this.bumpFeeErrorMsg = `${err}. Retrying with input ${newOutputIndex}`; diff --git a/utils/BackendUtils.ts b/utils/BackendUtils.ts index ad5dc05f9..cc27df9bf 100644 --- a/utils/BackendUtils.ts +++ b/utils/BackendUtils.ts @@ -120,6 +120,8 @@ class BackendUtils { this.call('publishTransaction', args); fundingStateStep = (...args: any[]) => this.call('fundingStateStep', args); bumpFee = (...args: any[]) => this.call('bumpFee', args); + bumpForceCloseFee = (...args: any[]) => + this.call('bumpForceCloseFee', args); lookupInvoice = (...args: any[]) => this.call('lookupInvoice', args); subscribeInvoice = (...args: any[]) => this.call('subscribeInvoice', args); subscribeInvoices = (...args: any[]) => diff --git a/views/BumpFee.tsx b/views/BumpFee.tsx index ca7b8805b..71da35cb3 100644 --- a/views/BumpFee.tsx +++ b/views/BumpFee.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'; +import { StyleSheet, View } from 'react-native'; import { ButtonGroup } from 'react-native-elements'; import { inject, observer } from 'mobx-react'; import { Route } from '@react-navigation/native'; @@ -7,6 +7,7 @@ import { StackNavigationProp } from '@react-navigation/stack'; import Button from '../components/Button'; import Header from '../components/Header'; +import OnchainFeeInput from '../components/OnchainFeeInput'; import LoadingIndicator from '../components/LoadingIndicator'; import Screen from '../components/Screen'; import { @@ -14,10 +15,10 @@ import { ErrorMessage } from '../components/SuccessErrorMessage'; import Switch from '../components/Switch'; +import Text from '../components/Text'; import TextInput from '../components/TextInput'; import FeeStore from '../stores/FeeStore'; -import SettingsStore from '../stores/SettingsStore'; import { themeColor } from '../utils/ThemeUtils'; import { localeString } from '../utils/LocaleUtils'; @@ -25,25 +26,29 @@ import { localeString } from '../utils/LocaleUtils'; interface BumpFeeProps { navigation: StackNavigationProp; FeeStore: FeeStore; - SettingsStore: SettingsStore; route: Route< 'BumpFee', { outpoint: string; - channel: boolean; + chan_point: string; + pendingOpen: boolean; + pendingClose: boolean; + forceClose: boolean; } >; } interface BumpFeeState { outpoint: string; + chan_point: string; target_conf: string; sat_per_vbyte: string; - force: boolean; + immediate: boolean; target_type: number; + budget: string; } -@inject('FeeStore', 'SettingsStore') +@inject('FeeStore') @observer export default class BumpFee extends React.PureComponent< BumpFeeProps, @@ -52,42 +57,46 @@ export default class BumpFee extends React.PureComponent< constructor(props: any) { super(props); const outpoint = this.props.route.params?.outpoint ?? ''; + const chan_point = this.props.route.params?.chan_point ?? ''; this.state = { outpoint, + chan_point, target_conf: '1', sat_per_vbyte: '1', - force: false, - target_type: 0 + immediate: false, + target_type: 0, + budget: '' }; } - UNSAFE_componentWillMount() { - this.props.FeeStore.resetFees(); + UNSAFE_componentWillMount(): void { + this.props.FeeStore.resetErrors(); } - handleOnNavigateBack = (sat_per_vbyte: string) => { - this.setState({ - sat_per_vbyte - }); - }; - render() { - const { FeeStore, SettingsStore, navigation, route } = this.props; - const { outpoint, target_conf, sat_per_vbyte, force, target_type } = - this.state; + const { FeeStore, navigation, route } = this.props; + const { + outpoint, + chan_point, + target_conf, + sat_per_vbyte, + immediate, + target_type, + budget + } = this.state; const { bumpFeeOpeningChannel, + bumpForceCloseFee, bumpFeeSuccess, bumpFeeError, bumpFeeErrorMsg, loading } = FeeStore; - const { settings } = SettingsStore; - const { privacy } = settings; - const enableMempoolRates = privacy && privacy.enableMempoolRates; - const isChannel = route.params?.channel; + const pendingOpen = route.params?.pendingOpen; + const pendingClose = route.params?.pendingClose; + const forceClose = route.params?.forceClose; const feeRateButton = () => ( )} - - {localeString('general.outpoint')} - - - this.setState({ - outpoint: text - }) - } - autoCapitalize="none" - autoCorrect={false} - /> + {!forceClose && ( + <> + + {localeString('general.outpoint')} + + + this.setState({ + outpoint: text + }) + } + autoCapitalize="none" + autoCorrect={false} + /> + + )} - { - this.setState({ target_type }); - }} - selectedIndex={target_type} - buttons={buttons} - selectedButtonStyle={{ - backgroundColor: themeColor('highlight'), - borderRadius: 12 - }} - containerStyle={{ - backgroundColor: themeColor('secondary'), - borderRadius: 12, - borderColor: themeColor('secondary'), - marginBottom: 15 - }} - innerBorderStyle={{ - color: themeColor('secondary') - }} - /> + {forceClose && ( + <> + + {localeString('views.Channel.channelPoint')} + + + this.setState({ + chan_point: text + }) + } + autoCapitalize="none" + autoCorrect={false} + locked + /> + + )} + + {!forceClose && ( + { + this.setState({ target_type }); + }} + selectedIndex={target_type} + buttons={buttons} + selectedButtonStyle={{ + backgroundColor: themeColor('highlight'), + borderRadius: 12 + }} + containerStyle={{ + backgroundColor: themeColor('secondary'), + borderRadius: 12, + borderColor: themeColor('secondary'), + marginBottom: 15 + }} + innerBorderStyle={{ + color: themeColor('secondary') + }} + /> + )} {target_type === 0 && ( <> @@ -208,50 +270,21 @@ export default class BumpFee extends React.PureComponent< color: themeColor('secondaryText') }} > - {localeString('views.OpenChannel.satsPerVbyte')} + {forceClose + ? localeString('views.BumpFee.startingFee') + : localeString( + 'views.OpenChannel.satsPerVbyte' + )} - {enableMempoolRates ? ( - - navigation.navigate('EditFee', { - onNavigateBack: - this.handleOnNavigateBack - }) - } - > - - - {sat_per_vbyte} - - - - ) : ( - - - this.setState({ - sat_per_vbyte: text - }) - } - /> - - )} + { + this.setState({ + sat_per_vbyte: text + }); + }} + navigation={navigation} + /> )} @@ -282,54 +315,105 @@ export default class BumpFee extends React.PureComponent< )} - <> + + {`${localeString( + 'views.BumpFee.budget' + )} (${localeString('general.optional')})`} + + { + if (!isNaN(Number(text))) { + this.setState({ + budget: text + }); + } + }} + autoCapitalize="none" + autoCorrect={false} + keyboardType="numeric" + /> + + - {localeString('general.force')} + {localeString('general.immediate')} { this.setState({ - force: !force + immediate: !immediate }); }} /> - +