Skip to content

Commit

Permalink
feat: add option to edit fee while withdrawing
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Jan 29, 2025
1 parent d64233f commit aa32bcd
Show file tree
Hide file tree
Showing 13 changed files with 77 additions and 22 deletions.
4 changes: 2 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -637,11 +637,11 @@ func (api *api) SignMessage(ctx context.Context, message string) (*SignMessageRe
}, nil
}

func (api *api) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (*RedeemOnchainFundsResponse, error) {
func (api *api) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (*RedeemOnchainFundsResponse, error) {
if api.svc.GetLNClient() == nil {
return nil, errors.New("LNClient not started")
}
txId, err := api.svc.GetLNClient().RedeemOnchainFunds(ctx, toAddress, amount, sendAll)
txId, err := api.svc.GetLNClient().RedeemOnchainFunds(ctx, toAddress, amount, feeRate, sendAll)
if err != nil {
return nil, err
}
Expand Down
9 changes: 5 additions & 4 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type API interface {
GetNewOnchainAddress(ctx context.Context) (string, error)
GetUnusedOnchainAddress(ctx context.Context) (string, error)
SignMessage(ctx context.Context, message string) (*SignMessageResponse, error)
RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (*RedeemOnchainFundsResponse, error)
RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (*RedeemOnchainFundsResponse, error)
GetBalances(ctx context.Context) (*BalancesResponse, error)
ListTransactions(ctx context.Context, appId *uint, limit uint64, offset uint64) (*ListTransactionsResponse, error)
SendPayment(ctx context.Context, invoice string, amountMsat *uint64) (*SendPaymentResponse, error)
Expand Down Expand Up @@ -204,9 +204,10 @@ type CloseChannelResponse = lnclient.CloseChannelResponse
type UpdateChannelRequest = lnclient.UpdateChannelRequest

type RedeemOnchainFundsRequest struct {
ToAddress string `json:"toAddress"`
Amount uint64 `json:"amount"`
SendAll bool `json:"sendAll"`
ToAddress string `json:"toAddress"`
Amount uint64 `json:"amount"`
FeeRate float64 `json:"feeRate"`
SendAll bool `json:"sendAll"`
}

type RedeemOnchainFundsResponse struct {
Expand Down
59 changes: 56 additions & 3 deletions frontend/src/screens/wallet/WithdrawOnchainFunds.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { AlertTriangleIcon, CopyIcon, ExternalLinkIcon } from "lucide-react";
import {
AlertTriangleIcon,
ChevronDown,
CopyIcon,
ExternalLinkIcon,
} from "lucide-react";
import React from "react";
import AppHeader from "src/components/AppHeader";
import ExternalLink from "src/components/ExternalLink";
Expand All @@ -21,6 +26,8 @@ import { LoadingButton } from "src/components/ui/loading-button";
import { useToast } from "src/components/ui/use-toast";
import { ONCHAIN_DUST_SATS } from "src/constants";
import { useBalances } from "src/hooks/useBalances";
import { useInfo } from "src/hooks/useInfo";
import { useMempoolApi } from "src/hooks/useMempoolApi";

import { copyToClipboard } from "src/lib/clipboard";
import { RedeemOnchainFundsResponse } from "src/types";
Expand All @@ -29,13 +36,28 @@ import { request } from "src/utils/request";
export default function WithdrawOnchainFunds() {
const [isLoading, setLoading] = React.useState(false);
const { toast } = useToast();
const { data: info } = useInfo();
const { data: balances } = useBalances();
const { data: recommendedFees } = useMempoolApi<{
fastestFee: number;
halfHourFee: number;
economyFee: number;
minimumFee: number;
}>("/v1/fees/recommended");
const [onchainAddress, setOnchainAddress] = React.useState("");
const [amount, setAmount] = React.useState("");
const [feeRate, setFeeRate] = React.useState("");
const [sendAll, setSendAll] = React.useState(false);
const [showAdvanced, setShowAdvanced] = React.useState(false);
const [transactionId, setTransactionId] = React.useState("");
const [confirmDialogOpen, setConfirmDialogOpen] = React.useState(false);

React.useEffect(() => {
if (recommendedFees) {
setFeeRate(recommendedFees.fastestFee.toString());
}
}, [recommendedFees]);

const copy = (text: string) => {
copyToClipboard(text, toast);
};
Expand Down Expand Up @@ -69,6 +91,7 @@ export default function WithdrawOnchainFunds() {
body: JSON.stringify({
toAddress: onchainAddress,
amount: +amount,
feeRate: +feeRate,
sendAll,
}),
}
Expand All @@ -87,7 +110,7 @@ export default function WithdrawOnchainFunds() {
});
}
setLoading(false);
}, [amount, onchainAddress, sendAll, toast]);
}, [amount, feeRate, onchainAddress, sendAll, toast]);

if (transactionId) {
return (
Expand Down Expand Up @@ -120,7 +143,7 @@ export default function WithdrawOnchainFunds() {
);
}

if (!balances) {
if (!info || !balances || !recommendedFees) {
return <Loading />;
}

Expand Down Expand Up @@ -239,6 +262,36 @@ export default function WithdrawOnchainFunds() {
}}
/>
</div>
{info?.backendType === "LND" && (
<>
{showAdvanced && (
<div className="">
<Label htmlFor="fee-rate">Fee Rate (Sat/Vbyte)</Label>
<Input
id="fee-rate"
type="number"
value={feeRate}
required
min={recommendedFees.minimumFee}
onChange={(e) => {
setFeeRate(e.target.value);
}}
/>
</div>
)}
{!showAdvanced && (
<Button
type="button"
variant="link"
className="text-muted-foreground text-xs"
onClick={() => setShowAdvanced((current) => !current)}
>
<ChevronDown className="w-4 h-4 mr-2" />
Advanced Options
</Button>
)}
</>
)}

<p className="text-sm text-muted-foreground">
Please double-check the destination address. This transaction cannot
Expand Down
2 changes: 1 addition & 1 deletion http/http_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ func (httpSvc *HttpService) redeemOnchainFundsHandler(c echo.Context) error {
})
}

redeemOnchainFundsResponse, err := httpSvc.api.RedeemOnchainFunds(ctx, redeemOnchainFundsRequest.ToAddress, redeemOnchainFundsRequest.Amount, redeemOnchainFundsRequest.SendAll)
redeemOnchainFundsResponse, err := httpSvc.api.RedeemOnchainFunds(ctx, redeemOnchainFundsRequest.ToAddress, redeemOnchainFundsRequest.Amount, redeemOnchainFundsRequest.FeeRate, redeemOnchainFundsRequest.SendAll)

if err != nil {
return c.JSON(http.StatusInternalServerError, ErrorResponse{
Expand Down
2 changes: 1 addition & 1 deletion lnclient/breez/breez.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ func (bs *BreezService) GetOnchainBalance(ctx context.Context) (*lnclient.Onchai
}, nil
}

func (bs *BreezService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (txId string, err error) {
func (bs *BreezService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (txId string, err error) {
if !sendAll {
return "", errors.New("only send all is supported")
}
Expand Down
2 changes: 1 addition & 1 deletion lnclient/cashu/cashu.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ func (cs *CashuService) GetOnchainBalance(ctx context.Context) (*lnclient.Onchai
}, nil
}

func (cs *CashuService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (string, error) {
func (cs *CashuService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (string, error) {
return "", nil
}

Expand Down
2 changes: 1 addition & 1 deletion lnclient/greenlight/greenlight.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ func (gs *GreenlightService) GetOnchainBalance(ctx context.Context) (*lnclient.O
}, nil
}

func (gs *GreenlightService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (string, error) {
func (gs *GreenlightService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (string, error) {
if !sendAll {
return "", errors.New("only send all is supported")
}
Expand Down
2 changes: 1 addition & 1 deletion lnclient/ldk/ldk.go
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@ func (ls *LDKService) GetOnchainBalance(ctx context.Context) (*lnclient.OnchainB
}, nil
}

func (ls *LDKService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (string, error) {
func (ls *LDKService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (string, error) {
if !sendAll {
// NOTE: this may fail if user does not reserve enough for the onchain transaction
// and can also drain the anchor reserves if the user provides a too high amount.
Expand Down
9 changes: 5 additions & 4 deletions lnclient/lnd/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -913,11 +913,12 @@ func (svc *LNDService) GetOnchainBalance(ctx context.Context) (*lnclient.Onchain
}, nil
}

func (svc *LNDService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (txId string, err error) {
func (svc *LNDService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (txId string, err error) {
resp, err := svc.client.SendCoins(ctx, &lnrpc.SendCoinsRequest{
Addr: toAddress,
SendAll: sendAll,
Amount: int64(amount),
Addr: toAddress,
SendAll: sendAll,
Amount: int64(amount),
SatPerVbyte: uint64(feeRate),
})
if err != nil {
return "", err
Expand Down
2 changes: 1 addition & 1 deletion lnclient/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ type LNClient interface {
ResetRouter(key string) error
GetOnchainBalance(ctx context.Context) (*OnchainBalanceResponse, error)
GetBalances(ctx context.Context) (*BalancesResponse, error)
RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (txId string, err error)
RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (txId string, err error)
SendPaymentProbes(ctx context.Context, invoice string) error
SendSpontaneousPaymentProbes(ctx context.Context, amountMsat uint64, nodeId string) error
ListPeers(ctx context.Context) ([]PeerDetails, error)
Expand Down
2 changes: 1 addition & 1 deletion lnclient/phoenixd/phoenixd.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ func (svc *PhoenixService) SendKeysend(ctx context.Context, amount uint64, desti
return nil, errors.New("not implemented")
}

func (svc *PhoenixService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (txId string, err error) {
func (svc *PhoenixService) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (txId string, err error) {
return "", errors.New("not implemented")
}

Expand Down
2 changes: 1 addition & 1 deletion tests/mock_ln_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ func (mln *MockLn) GetBalances(ctx context.Context) (*lnclient.BalancesResponse,
func (mln *MockLn) GetOnchainBalance(ctx context.Context) (*lnclient.OnchainBalanceResponse, error) {
return nil, nil
}
func (mln *MockLn) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, sendAll bool) (txId string, err error) {
func (mln *MockLn) RedeemOnchainFunds(ctx context.Context, toAddress string, amount uint64, feeRate float64, sendAll bool) (txId string, err error) {
return "", nil
}
func (mln *MockLn) ResetRouter(key string) error {
Expand Down
2 changes: 1 addition & 1 deletion wails/wails_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}

redeemOnchainFundsResponse, err := app.api.RedeemOnchainFunds(ctx, redeemOnchainFundsRequest.ToAddress, redeemOnchainFundsRequest.Amount, redeemOnchainFundsRequest.SendAll)
redeemOnchainFundsResponse, err := app.api.RedeemOnchainFunds(ctx, redeemOnchainFundsRequest.ToAddress, redeemOnchainFundsRequest.Amount, redeemOnchainFundsRequest.FeeRate, redeemOnchainFundsRequest.SendAll)
if err != nil {
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
Expand Down

0 comments on commit aa32bcd

Please sign in to comment.