Skip to content
5 changes: 3 additions & 2 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1050,10 +1050,11 @@ func (api *api) InitiateSwapIn(ctx context.Context, initiateSwapInRequest *Initi
return nil, errors.New("invalid swap amount")
}

swapInResponse, err := api.svc.GetSwapsService().SwapIn(amountSat, false)
swapInResponse, err := api.svc.GetSwapsService().SwapIn(amountSat, false, initiateSwapInRequest.InternalPayment, initiateSwapInRequest.FeeRate)
if err != nil {
logger.Logger.WithFields(logrus.Fields{
"amount_sat": amountSat,
"amount_sat": amountSat,
"internal_payment": initiateSwapInRequest.InternalPayment,
}).WithError(err).Error("Failed to initiate swap in")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return nil, err
}
Expand Down
72 changes: 37 additions & 35 deletions api/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,11 @@ type CreateLightningAddressRequest struct {
}

type InitiateSwapRequest struct {
SwapAmount *uint64 `json:"swapAmount"` // deprecated
SwapAmountSat *uint64 `json:"swapAmountSat"`
Destination string `json:"destination"`
SwapAmount *uint64 `json:"swapAmount"` // deprecated
Comment thread
im-adithya marked this conversation as resolved.
SwapAmountSat *uint64 `json:"swapAmountSat"`
Destination string `json:"destination"`
InternalPayment bool `json:"internalPayment"`
FeeRate *uint64 `json:"feeRate"`
}

type RefundSwapRequest struct {
Expand Down Expand Up @@ -304,38 +306,38 @@ type InfoResponseRelay struct {
}

type InfoResponse struct {
BackendType string `json:"backendType"`
SetupCompleted bool `json:"setupCompleted"`
OAuthRedirect bool `json:"oauthRedirect"`
Running bool `json:"running"`
Unlocked bool `json:"unlocked"`
AlbyAuthUrl string `json:"albyAuthUrl"`
NextBackupReminder string `json:"nextBackupReminder"`
AlbyUserIdentifier string `json:"albyUserIdentifier"`
AlbyAccountConnected bool `json:"albyAccountConnected"`
Version string `json:"version"`
Network string `json:"network"`
EnableAdvancedSetup bool `json:"enableAdvancedSetup"`
LdkVssEnabled bool `json:"ldkVssEnabled"`
VssSupported bool `json:"vssSupported"`
StartupState string `json:"startupState"`
StartupError string `json:"startupError"`
StartupErrorTime time.Time `json:"startupErrorTime"`
AutoUnlockPasswordSupported bool `json:"autoUnlockPasswordSupported"`
AutoUnlockPasswordEnabled bool `json:"autoUnlockPasswordEnabled"`
Currency string `json:"currency"`
BitcoinDisplayFormat string `json:"bitcoinDisplayFormat"`
Relays []InfoResponseRelay `json:"relays"`
NodeAlias string `json:"nodeAlias"`
MempoolUrl string `json:"mempoolUrl"`
ChainDataSourceType string `json:"chainDataSourceType,omitempty"`
ChainDataSourceAddress string `json:"chainDataSourceAddress,omitempty"`
JitChannelsLiquiditySource string `json:"jitChannelsLiquiditySource,omitempty"`
JitChannelsMinPaymentSizeMsat *uint64 `json:"jitChannelsMinPaymentSizeMsat,omitempty"`
JitChannelsMaxPaymentSizeMsat *uint64 `json:"jitChannelsMaxPaymentSizeMsat,omitempty"`
JitChannelsEnabled bool `json:"jitChannelsEnabled"`
HideUpdateBanner bool `json:"hideUpdateBanner"`
SupportsBolt12 bool `json:"supportsBolt12"`
BackendType string `json:"backendType"`
SetupCompleted bool `json:"setupCompleted"`
OAuthRedirect bool `json:"oauthRedirect"`
Running bool `json:"running"`
Unlocked bool `json:"unlocked"`
AlbyAuthUrl string `json:"albyAuthUrl"`
NextBackupReminder string `json:"nextBackupReminder"`
AlbyUserIdentifier string `json:"albyUserIdentifier"`
AlbyAccountConnected bool `json:"albyAccountConnected"`
Version string `json:"version"`
Network string `json:"network"`
EnableAdvancedSetup bool `json:"enableAdvancedSetup"`
LdkVssEnabled bool `json:"ldkVssEnabled"`
VssSupported bool `json:"vssSupported"`
StartupState string `json:"startupState"`
StartupError string `json:"startupError"`
StartupErrorTime time.Time `json:"startupErrorTime"`
AutoUnlockPasswordSupported bool `json:"autoUnlockPasswordSupported"`
AutoUnlockPasswordEnabled bool `json:"autoUnlockPasswordEnabled"`
Currency string `json:"currency"`
BitcoinDisplayFormat string `json:"bitcoinDisplayFormat"`
Relays []InfoResponseRelay `json:"relays"`
NodeAlias string `json:"nodeAlias"`
MempoolUrl string `json:"mempoolUrl"`
ChainDataSourceType string `json:"chainDataSourceType,omitempty"`
ChainDataSourceAddress string `json:"chainDataSourceAddress,omitempty"`
JitChannelsLiquiditySource string `json:"jitChannelsLiquiditySource,omitempty"`
JitChannelsMinPaymentSizeMsat *uint64 `json:"jitChannelsMinPaymentSizeMsat,omitempty"`
JitChannelsMaxPaymentSizeMsat *uint64 `json:"jitChannelsMaxPaymentSizeMsat,omitempty"`
JitChannelsEnabled bool `json:"jitChannelsEnabled"`
HideUpdateBanner bool `json:"hideUpdateBanner"`
SupportsBolt12 bool `json:"supportsBolt12"`
}

type UpdateSettingsRequest struct {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/CloseChannelDialogContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function CloseChannelDialogContent({ alias, channel }: Props) {
Are you sure you want to close the channel with {alias}?
</AlertDialogTitle>
<AlertDialogDescription>
This channel is inactive. Some channels require up to 6 onchain
This channel is inactive. Some channels require up to 6 on-chain
confirmations before they are usable.
</AlertDialogDescription>
</AlertDialogHeader>
Expand Down
131 changes: 131 additions & 0 deletions frontend/src/components/FeeRateField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { AlertTriangleIcon, ExternalLinkIcon, PencilIcon } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import ExternalLink from "src/components/ExternalLink";
import Loading from "src/components/Loading";
import { Button } from "src/components/ui/button";
import { Input } from "src/components/ui/input";
import { Label } from "src/components/ui/label";
import { useInfo } from "src/hooks/useInfo";
import { useMempoolApi } from "src/hooks/useMempoolApi";

type RecommendedFees = {
fastestFee: number;
halfHourFee: number;
economyFee: number;
minimumFee: number;
};

type FeeRateFieldProps = {
feeRate: string;
onFeeRateChange: (value: string) => void;
};

export function FeeRateField({ feeRate, onFeeRateChange }: FeeRateFieldProps) {
const { data: info } = useInfo();
const { data: recommendedFees, error: mempoolError } =
useMempoolApi<RecommendedFees>("/v1/fees/recommended");
const [isEditing, setIsEditing] = useState(false);
const hasInitializedDefaultFee = useRef(false);

useEffect(() => {
if (
recommendedFees?.fastestFee &&
!hasInitializedDefaultFee.current &&
!feeRate
) {
hasInitializedDefaultFee.current = true;
onFeeRateChange(recommendedFees.fastestFee.toString());
}
}, [feeRate, onFeeRateChange, recommendedFees]);

useEffect(() => {
if (mempoolError) {
setIsEditing(true);
}
}, [mempoolError]);

if (!info || (!recommendedFees && !mempoolError)) {
return (
<div className="flex items-center justify-between">
<Label>On-chain Fee Rate (sat/vB)</Label>
<Loading className="w-4 h-4" />
</div>
);
}

if (!isEditing) {
return (
<div className="flex items-center justify-between">
<Label>On-chain Fee Rate (sat/vB)</Label>
{feeRate ? (
<button
type="button"
className="flex items-center gap-2 cursor-pointer"
onClick={() => setIsEditing(true)}
>
<p className="text-sm">{feeRate} sat/vB</p>
<PencilIcon className="w-4 h-4" />
</button>
) : (
<Loading className="w-4 h-4" />
)}
</div>
);
}

return (
<div className="grid gap-2">
<Label htmlFor="fee-rate">On-chain Fee Rate (sat/vB)</Label>
{mempoolError && (
<div className="text-muted-foreground text-xs flex gap-1 items-center">
<AlertTriangleIcon className="h-3 w-3" />
Failed to fetch fee estimates. Try refreshing the page.
</div>
)}
<Input
id="fee-rate"
type="number"
value={feeRate}
step={1}
required
min={recommendedFees?.minimumFee || 1}
onChange={(e) => {
onFeeRateChange(e.target.value);
}}
/>
{recommendedFees && (
<div className="flex items-center mt-2 gap-4">
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
onFeeRateChange(recommendedFees.economyFee.toString())
}
>
Low priority: {recommendedFees.economyFee}
</Button>
<Button
variant="positive"
className="rounded-full"
type="button"
onClick={() =>
onFeeRateChange(recommendedFees.fastestFee.toString())
}
>
High priority: {recommendedFees.fastestFee}
</Button>
{info.mempoolUrl && (
<ExternalLink
to={info.mempoolUrl}
className="text-muted-foreground text-sm underline flex items-center gap-2"
>
View on Mempool
<ExternalLinkIcon className="w-4 h-4" />
</ExternalLink>
)}
</div>
)}
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/src/screens/channels/CurrentChannelOrder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ function PayBitcoinChannelOrderTopup({ order }: { order: NewChannelOrder }) {
</p>
<p className="text-xs text-muted-foreground">
This amount includes cost for the channel opening and potential
channel onchain reserves.
channel on-chain reserves.
</p>
<div className="flex flex-row gap-2 items-center">
<Input
Expand Down
Loading
Loading