diff --git a/lndmobile/LndMobile.d.ts b/lndmobile/LndMobile.d.ts index 7f4ab91ff3..5b24aace8f 100644 --- a/lndmobile/LndMobile.d.ts +++ b/lndmobile/LndMobile.d.ts @@ -94,6 +94,7 @@ export interface ILndMobile { privateKey: string, servicePubKey: string, feeRate: number, + minerFee: number, timeoutBlockHeight: number, destinationAddress: string, lockupAddress: string, diff --git a/lndmobile/LndMobileInjection.ts b/lndmobile/LndMobileInjection.ts index 14cfc0b54a..6d6e668c7e 100644 --- a/lndmobile/LndMobileInjection.ts +++ b/lndmobile/LndMobileInjection.ts @@ -583,6 +583,7 @@ export interface ILndMobileInjections { privateKey, servicePubKey, feeRate, + minerFee, timeoutBlockHeight, destinationAddress, lockupAddress, @@ -597,6 +598,7 @@ export interface ILndMobileInjections { privateKey: string; servicePubKey: string; feeRate: number; + minerFee: number; timeoutBlockHeight: number; destinationAddress: string; lockupAddress: string; diff --git a/lndmobile/swaps.ts b/lndmobile/swaps.ts index d1ed2426d5..012471dff3 100644 --- a/lndmobile/swaps.ts +++ b/lndmobile/swaps.ts @@ -128,6 +128,7 @@ export const createRefundTransaction = async ({ privateKey, servicePubKey, feeRate, + minerFee, timeoutBlockHeight, destinationAddress, lockupAddress, @@ -142,6 +143,7 @@ export const createRefundTransaction = async ({ privateKey: string; servicePubKey: string; feeRate: number; + minerFee: number; timeoutBlockHeight: number; destinationAddress: string; lockupAddress: string; @@ -158,6 +160,7 @@ export const createRefundTransaction = async ({ privateKey, servicePubKey, feeRate, + minerFee, timeoutBlockHeight, destinationAddress, lockupAddress, diff --git a/locales/en.json b/locales/en.json index 698176114c..96b19a215f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -2356,6 +2356,8 @@ "views.Swaps.uncooperative": "Uncooperative refund", "views.Swaps.infoText": "It is recommended you only attempt an uncooperative refund if the service provider is not operational.", "views.Swaps.initiateRefund": "Initiate Refund", + "views.Swaps.feeRate": "Fee Rate", + "views.Swaps.minerFee": "Miner Fee", "views.Swaps.networkFee": "Network fee", "views.Swaps.serviceFee": "Service fee", "views.Swaps.missingAmount": "Please enter an amount", diff --git a/views/Swaps/Refund.tsx b/views/Swaps/Refund.tsx index cec1c11a3f..90470e6b06 100644 --- a/views/Swaps/Refund.tsx +++ b/views/Swaps/Refund.tsx @@ -6,6 +6,8 @@ import { Route } from '@react-navigation/native'; import lndMobile from '../../lndmobile/LndMobileInjection'; const { createRefundTransaction } = lndMobile.swaps; +import { ButtonGroup } from '@rneui/themed'; + import Button from '../../components/Button'; import Header from '../../components/Header'; @@ -15,6 +17,7 @@ import OnchainFeeInput from '../../components/OnchainFeeInput'; import Screen from '../../components/Screen'; import Switch from '../../components/Switch'; import Text from '../../components/Text'; +import TextInput from '../../components/TextInput'; import AddressInput from '../../components/AddressInput'; import { ErrorMessage, @@ -25,6 +28,10 @@ import { Row } from '../../components/layout/Row'; import BackendUtils from '../../utils/BackendUtils'; import { localeString, pascalToHumanReadable } from '../../utils/LocaleUtils'; import { themeColor } from '../../utils/ThemeUtils'; +import { + buttonTextStyle, + getButtonGroupStyles +} from '../../utils/buttonGroupStyles'; import SwapStore from '../../stores/SwapStore'; import NodeInfoStore from '../../stores/NodeInfoStore'; @@ -51,7 +58,9 @@ interface RefundSwapProps { interface RefundSwapState { destinationAddress: string; - fee: string; + feeRate: string; + minerFee: string; + selectedFeeIndex: number; error: string; swapData: Swap; loading: boolean; @@ -69,7 +78,9 @@ export default class RefundSwap extends React.Component< > { state = { destinationAddress: '', - fee: '', + feeRate: '', + minerFee: '', + selectedFeeIndex: 0, error: '', loading: false, swapData: this.props.route.params.swapData, @@ -81,11 +92,13 @@ export default class RefundSwap extends React.Component< createRefundTransaction = async ( swapData: Swap, - fee: any, + feeRate: string, + minerFee: string, destinationAddress: string ): Promise => { const { SwapStore } = this.props; - const { uncooperative } = this.state; + const { uncooperative, selectedFeeIndex } = this.state; + const isMinerFeeMode = selectedFeeIndex === 1; try { const txid = await createRefundTransaction({ @@ -96,7 +109,8 @@ export default class RefundSwap extends React.Component< transactionHex: swapData.lockupTransaction?.hex, privateKey: swapData.refundPrivateKey!, servicePubKey: swapData.servicePubKey!, - feeRate: Number(fee), + feeRate: !isMinerFeeMode ? Number(feeRate) : 0, + minerFee: isMinerFeeMode ? Number(minerFee) : 0, timeoutBlockHeight: Number(swapData.timeoutBlockHeight), destinationAddress, lockupAddress: swapData.effectiveLockupAddress!, @@ -140,7 +154,9 @@ export default class RefundSwap extends React.Component< render() { const { navigation, SwapStore, InvoicesStore } = this.props; const { - fee, + feeRate, + minerFee, + selectedFeeIndex, destinationAddress, swapData, error, @@ -151,6 +167,9 @@ export default class RefundSwap extends React.Component< rawToggle } = this.state; + const isValidMinerFee = + Number(minerFee) > 0 && Number.isInteger(Number(minerFee)); + const rawDetails = { endpoint: swapData.endpoint.replace('/v2', ''), swapId: swapData.id, @@ -159,7 +178,8 @@ export default class RefundSwap extends React.Component< transactionHex: swapData.lockupTransaction?.hex, privateKey: swapData.refundPrivateKey, servicePubKey: swapData.servicePubKey, - feeRate: Number(fee), + feeRate: selectedFeeIndex === 0 ? Number(feeRate) : 0, + minerFee: selectedFeeIndex === 1 ? Number(minerFee) : 0, timeoutBlockHeight: Number(swapData.timeoutBlockHeight), destinationAddress, lockupAddress: swapData.effectiveLockupAddress, @@ -282,21 +302,96 @@ export default class RefundSwap extends React.Component< /> )} - + this.setState({ + selectedFeeIndex: index, + error: '' + }) + } + selectedIndex={selectedFeeIndex} + buttons={[ + { + element: () => ( + + {localeString('views.Swaps.feeRate')} + + ) + }, + { + element: () => ( + + {localeString('views.Swaps.minerFee')} + + ) + } + ]} + selectedButtonStyle={ + getButtonGroupStyles().selectedButtonStyle + } + containerStyle={{ + ...getButtonGroupStyles().containerStyle, + marginTop: 14 }} - > - {localeString('views.Send.feeSatsVbyte')} - - - this.setState({ fee: text, error: '' }) + innerBorderStyle={ + getButtonGroupStyles().innerBorderStyle } - navigation={navigation} /> + {selectedFeeIndex === 0 ? ( + <> + + {localeString('views.Send.feeSatsVbyte')} + + + this.setState({ feeRate: text, error: '' }) + } + navigation={navigation} + /> + + ) : ( + <> + + {`${localeString( + 'views.Swaps.minerFee' + )} (${localeString('general.sats')})`} + + + this.setState({ minerFee: text, error: '' }) + } + keyboardType="numeric" + placeholder="0" + error={!!minerFee && !isValidMinerFee} + /> + + )}