diff --git a/src/components/AmountCard.tsx b/src/components/AmountCard.tsx index c87ccaf..c761f5a 100644 --- a/src/components/AmountCard.tsx +++ b/src/components/AmountCard.tsx @@ -73,7 +73,21 @@ export function AmountCard(props: { isAmountEditable?: boolean; setAmountSats?: (amount: bigint) => void; skipWarnings?: boolean; + maxAmountSats?: bigint; }) { + // Normally we want to add the fee to the amount, but for max amount we just show the max + const totalOrTotalLessFee = () => { + console.log(props.amountSats, props.fee, props.maxAmountSats); + if ( + props.fee && + props.maxAmountSats && + props.amountSats === props.maxAmountSats?.toString() + ) { + return props.maxAmountSats.toLocaleString(); + } else { + return add(props.amountSats, props.fee).toString(); + } + }; return ( @@ -98,6 +112,8 @@ export function AmountCard(props: { : noop } skipWarnings={props.skipWarnings} + maxAmountSats={props.maxAmountSats} + fee={props.fee} /> @@ -108,12 +124,7 @@ export function AmountCard(props: {
- + diff --git a/src/components/AmountEditable.tsx b/src/components/AmountEditable.tsx index 93300d0..55cd00b 100644 --- a/src/components/AmountEditable.tsx +++ b/src/components/AmountEditable.tsx @@ -138,6 +138,8 @@ export const AmountEditable: ParentComponent<{ initialOpen: boolean; setAmountSats: (s: bigint) => void; skipWarnings?: boolean; + maxAmountSats?: bigint; + fee?: string; }> = (props) => { const [isOpen, setIsOpen] = createSignal(props.initialOpen); const [state, _actions] = useMegaStore(); @@ -280,6 +282,22 @@ export const AmountEditable: ParentComponent<{ } } + // If the user is trying to send the max amount we want to show max minus fee + // Otherwise we just the actual amount they've entered + const maxOrLocalSats = () => { + if ( + props.maxAmountSats && + props.fee && + props.maxAmountSats === BigInt(localSats()) + ) { + return ( + Number(props.maxAmountSats) - Number(props.fee) + ).toLocaleString(); + } else { + return localSats(); + } + }; + return (
} > - + Edit {/* {props.children} */} @@ -383,6 +401,19 @@ export const AmountEditable: ParentComponent<{ )} + + +
diff --git a/src/routes/Send.tsx b/src/routes/Send.tsx index 1287e11..33c6d1c 100644 --- a/src/routes/Send.tsx +++ b/src/routes/Send.tsx @@ -232,17 +232,26 @@ export default function Send() { setFieldDestination(""); } + const maxOnchain = createMemo(() => { + return ( + (state.balance?.confirmed ?? 0n) + + (state.balance?.unconfirmed ?? 0n) + ); + }); + + const isMax = () => { + if (source() === "onchain") { + return amountSats() === maxOnchain(); + } + }; + const insufficientFunds = () => { if (source() === "onchain") { - return ( - (state.balance?.confirmed ?? 0n) + - (state.balance?.unconfirmed ?? 0n) < - amountSats() - ); + return maxOnchain() < amountSats(); } if (source() === "lightning") { return ( - (state.balance?.lightning ?? 0n) < amountSats() && + (state.balance?.lightning ?? 0n) <= amountSats() && setError( "We do not have enough balance to pay the given amount." ) @@ -251,7 +260,10 @@ export default function Send() { }; const feeEstimate = () => { - if (source() === "lightning") return undefined; + if (source() === "lightning") { + setError(undefined); + return undefined; + } if ( source() === "onchain" && @@ -260,7 +272,15 @@ export default function Send() { address() ) { setError(undefined); + try { + // If max we want to use the sweep fee estimator + if (isMax()) { + return state.mutiny_wallet?.estimate_sweep_tx_fee( + address()! + ); + } + return state.mutiny_wallet?.estimate_tx_fee( address()!, amountSats(), @@ -465,15 +485,28 @@ export default function Send() { sentDetails.amount = amountSats(); } } else if (source() === "onchain" && address()) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const txid = await state.mutiny_wallet?.send_to_address( - address()!, - amountSats(), - tags - ); - sentDetails.amount = amountSats(); - sentDetails.destination = address(); - sentDetails.txid = txid; + if (isMax()) { + // If we're trying to send the max amount, use the sweep method instead of regular send + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const txid = await state.mutiny_wallet?.sweep_wallet( + address()!, + tags + ); + + sentDetails.amount = amountSats(); + sentDetails.destination = address(); + sentDetails.txid = txid; + } else { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const txid = await state.mutiny_wallet?.send_to_address( + address()!, + amountSats(), + tags + ); + sentDetails.amount = amountSats(); + sentDetails.destination = address(); + sentDetails.txid = txid; + } } setSentDetails(sentDetails as SentDetails); clearAll(); @@ -500,6 +533,10 @@ export default function Send() { const network = state.mutiny_wallet?.get_network() as Network; + const maxAmountSats = createMemo(() => { + return source() === "onchain" ? maxOnchain() : undefined; + }); + return ( @@ -611,6 +648,8 @@ export default function Send() { setAmountSats={setAmountSats} fee={feeEstimate()?.toString()} isAmountEditable={!invoice()?.amount_sats} + maxAmountSats={maxAmountSats()} + skipWarnings={true} />