mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 06:21:37 +01:00
chore: lnurl channel
This commit is contained in:
parent
4399a046c6
commit
396074fb7a
10 changed files with 214 additions and 6 deletions
|
@ -3,10 +3,17 @@ import { to } from 'server/helpers/async';
|
|||
import { logger } from 'server/helpers/logger';
|
||||
import { requestLimiter } from 'server/helpers/rateLimiter';
|
||||
import { ContextType } from 'server/types/apiTypes';
|
||||
import { createInvoice, decodePaymentRequest, pay } from 'ln-service';
|
||||
import {
|
||||
createInvoice,
|
||||
decodePaymentRequest,
|
||||
pay,
|
||||
addPeer,
|
||||
getWalletInfo,
|
||||
} from 'ln-service';
|
||||
import {
|
||||
CreateInvoiceType,
|
||||
DecodedType,
|
||||
GetWalletInfoType,
|
||||
PayInvoiceType,
|
||||
} from 'server/types/ln-service.types';
|
||||
import { lnAuthUrlGenerator } from 'server/helpers/lnAuth';
|
||||
|
@ -28,6 +35,7 @@ type FetchLnUrlParams = {
|
|||
url: string;
|
||||
};
|
||||
|
||||
type LnUrlChannelType = { callback: string; k1: string; uri: string };
|
||||
type LnUrlPayType = { callback: string; amount: number; comment: string };
|
||||
type LnUrlWithdrawType = {
|
||||
callback: string;
|
||||
|
@ -238,6 +246,49 @@ export const lnUrlResolvers = {
|
|||
throw new Error('ProblemWithdrawingFromLnUrlService');
|
||||
}
|
||||
},
|
||||
lnUrlChannel: async (
|
||||
_: undefined,
|
||||
{ callback, k1, uri }: LnUrlChannelType,
|
||||
context: ContextType
|
||||
) => {
|
||||
await requestLimiter(context.ip, 'lnUrlChannel');
|
||||
const { lnd } = context;
|
||||
|
||||
logger.debug('LnUrlChannel initiated with params: %o', {
|
||||
callback,
|
||||
uri,
|
||||
k1,
|
||||
});
|
||||
|
||||
const split = uri.split('@');
|
||||
|
||||
await to(addPeer({ lnd, socket: split[1], public_key: split[0] }));
|
||||
|
||||
const info = await to<GetWalletInfoType>(getWalletInfo({ lnd }));
|
||||
|
||||
// If the callback url already has an initial query '?' identifier we don't need to add it again.
|
||||
const initialIdentifier = callback.indexOf('?') != -1 ? '&' : '?';
|
||||
|
||||
const finalUrl = `${callback}${initialIdentifier}k1=${k1}&remoteid=${info.public_key}&private=0`;
|
||||
|
||||
try {
|
||||
const response = await fetchWithProxy(finalUrl);
|
||||
const json = await response.json();
|
||||
|
||||
logger.debug('LnUrlChannel response: %o', json);
|
||||
|
||||
if (json.status === 'ERROR') {
|
||||
throw new Error(json.reason || 'LnServiceError');
|
||||
}
|
||||
|
||||
return 'Successfully requested a channel open';
|
||||
} catch (error) {
|
||||
logger.error('Error requesting channel from LnUrl service: %o', error);
|
||||
throw new Error(
|
||||
`Error requesting channel from LnUrl service: ${error}`
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
LnUrlRequest: {
|
||||
__resolveType(parent: RequestWithType) {
|
||||
|
@ -247,6 +298,9 @@ export const lnUrlResolvers = {
|
|||
if (parent.tag === 'withdrawRequest') {
|
||||
return 'WithdrawRequest';
|
||||
}
|
||||
if (parent.tag === 'channelRequest') {
|
||||
return 'ChannelRequest';
|
||||
}
|
||||
return 'Unknown';
|
||||
},
|
||||
},
|
||||
|
|
|
@ -19,7 +19,14 @@ export const lnUrlTypes = gql`
|
|||
tag: String
|
||||
}
|
||||
|
||||
union LnUrlRequest = WithdrawRequest | PayRequest
|
||||
type ChannelRequest {
|
||||
tag: String
|
||||
k1: String
|
||||
callback: String
|
||||
uri: String
|
||||
}
|
||||
|
||||
union LnUrlRequest = WithdrawRequest | PayRequest | ChannelRequest
|
||||
|
||||
type AuthResponse {
|
||||
status: String!
|
||||
|
|
|
@ -117,6 +117,7 @@ export const mutationTypes = gql`
|
|||
lnMarketsLogout: Boolean!
|
||||
lnUrlAuth(url: String!): AuthResponse!
|
||||
lnUrlPay(callback: String!, amount: Int!, comment: String): PaySuccess!
|
||||
lnUrlChannel(callback: String!, k1: String!, uri: String!): String!
|
||||
lnUrlWithdraw(
|
||||
callback: String!
|
||||
amount: Int!
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
"possibleTypes": {
|
||||
"LnUrlRequest": [
|
||||
"WithdrawRequest",
|
||||
"PayRequest"
|
||||
"PayRequest",
|
||||
"ChannelRequest"
|
||||
],
|
||||
"Transaction": [
|
||||
"InvoiceType",
|
||||
|
|
|
@ -17,6 +17,9 @@ export type FetchLnUrlMutation = (
|
|||
) | (
|
||||
{ __typename?: 'PayRequest' }
|
||||
& Pick<Types.PayRequest, 'callback' | 'maxSendable' | 'minSendable' | 'metadata' | 'commentAllowed' | 'tag'>
|
||||
) | (
|
||||
{ __typename?: 'ChannelRequest' }
|
||||
& Pick<Types.ChannelRequest, 'tag' | 'k1' | 'callback' | 'uri'>
|
||||
)> }
|
||||
);
|
||||
|
||||
|
@ -61,6 +64,18 @@ export type WithdrawLnUrlMutation = (
|
|||
& Pick<Types.Mutation, 'lnUrlWithdraw'>
|
||||
);
|
||||
|
||||
export type ChannelLnUrlMutationVariables = Types.Exact<{
|
||||
callback: Types.Scalars['String'];
|
||||
k1: Types.Scalars['String'];
|
||||
uri: Types.Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ChannelLnUrlMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& Pick<Types.Mutation, 'lnUrlChannel'>
|
||||
);
|
||||
|
||||
|
||||
export const FetchLnUrlDocument = gql`
|
||||
mutation FetchLnUrl($url: String!) {
|
||||
|
@ -81,6 +96,12 @@ export const FetchLnUrlDocument = gql`
|
|||
commentAllowed
|
||||
tag
|
||||
}
|
||||
... on ChannelRequest {
|
||||
tag
|
||||
k1
|
||||
callback
|
||||
uri
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -222,4 +243,37 @@ export function useWithdrawLnUrlMutation(baseOptions?: Apollo.MutationHookOption
|
|||
}
|
||||
export type WithdrawLnUrlMutationHookResult = ReturnType<typeof useWithdrawLnUrlMutation>;
|
||||
export type WithdrawLnUrlMutationResult = Apollo.MutationResult<WithdrawLnUrlMutation>;
|
||||
export type WithdrawLnUrlMutationOptions = Apollo.BaseMutationOptions<WithdrawLnUrlMutation, WithdrawLnUrlMutationVariables>;
|
||||
export type WithdrawLnUrlMutationOptions = Apollo.BaseMutationOptions<WithdrawLnUrlMutation, WithdrawLnUrlMutationVariables>;
|
||||
export const ChannelLnUrlDocument = gql`
|
||||
mutation ChannelLnUrl($callback: String!, $k1: String!, $uri: String!) {
|
||||
lnUrlChannel(callback: $callback, k1: $k1, uri: $uri)
|
||||
}
|
||||
`;
|
||||
export type ChannelLnUrlMutationFn = Apollo.MutationFunction<ChannelLnUrlMutation, ChannelLnUrlMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useChannelLnUrlMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useChannelLnUrlMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useChannelLnUrlMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [channelLnUrlMutation, { data, loading, error }] = useChannelLnUrlMutation({
|
||||
* variables: {
|
||||
* callback: // value for 'callback'
|
||||
* k1: // value for 'k1'
|
||||
* uri: // value for 'uri'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useChannelLnUrlMutation(baseOptions?: Apollo.MutationHookOptions<ChannelLnUrlMutation, ChannelLnUrlMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<ChannelLnUrlMutation, ChannelLnUrlMutationVariables>(ChannelLnUrlDocument, options);
|
||||
}
|
||||
export type ChannelLnUrlMutationHookResult = ReturnType<typeof useChannelLnUrlMutation>;
|
||||
export type ChannelLnUrlMutationResult = Apollo.MutationResult<ChannelLnUrlMutation>;
|
||||
export type ChannelLnUrlMutationOptions = Apollo.BaseMutationOptions<ChannelLnUrlMutation, ChannelLnUrlMutationVariables>;
|
|
@ -19,6 +19,12 @@ export const FETCH_LN_URL = gql`
|
|||
commentAllowed
|
||||
tag
|
||||
}
|
||||
... on ChannelRequest {
|
||||
tag
|
||||
k1
|
||||
callback
|
||||
uri
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -60,3 +66,9 @@ export const WITHDRAW_LN_URL = gql`
|
|||
)
|
||||
}
|
||||
`;
|
||||
|
||||
export const CHANNEL_LN_URL = gql`
|
||||
mutation ChannelLnUrl($callback: String!, $k1: String!, $uri: String!) {
|
||||
lnUrlChannel(callback: $callback, k1: $k1, uri: $uri)
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -72,6 +72,14 @@ export type BosScoreResponse = {
|
|||
scores: Array<BosScore>;
|
||||
};
|
||||
|
||||
export type ChannelRequest = {
|
||||
__typename?: 'ChannelRequest';
|
||||
tag?: Maybe<Scalars['String']>;
|
||||
k1?: Maybe<Scalars['String']>;
|
||||
callback?: Maybe<Scalars['String']>;
|
||||
uri?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type CreateBoltzReverseSwapType = {
|
||||
__typename?: 'CreateBoltzReverseSwapType';
|
||||
id: Scalars['String'];
|
||||
|
@ -175,7 +183,7 @@ export type LnMarketsUserInfo = {
|
|||
last_ip?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type LnUrlRequest = WithdrawRequest | PayRequest;
|
||||
export type LnUrlRequest = WithdrawRequest | PayRequest | ChannelRequest;
|
||||
|
||||
export type MessageType = {
|
||||
__typename?: 'MessageType';
|
||||
|
@ -194,6 +202,7 @@ export type Mutation = {
|
|||
lnMarketsLogout: Scalars['Boolean'];
|
||||
lnUrlAuth: AuthResponse;
|
||||
lnUrlPay: PaySuccess;
|
||||
lnUrlChannel: Scalars['String'];
|
||||
lnUrlWithdraw: Scalars['String'];
|
||||
fetchLnUrl?: Maybe<LnUrlRequest>;
|
||||
createBaseTokenInvoice?: Maybe<BaseInvoiceType>;
|
||||
|
@ -270,6 +279,13 @@ export type MutationLnUrlPayArgs = {
|
|||
};
|
||||
|
||||
|
||||
export type MutationLnUrlChannelArgs = {
|
||||
callback: Scalars['String'];
|
||||
k1: Scalars['String'];
|
||||
uri: Scalars['String'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationLnUrlWithdrawArgs = {
|
||||
callback: Scalars['String'];
|
||||
amount: Scalars['Int'];
|
||||
|
|
58
src/views/home/quickActions/lnurl/LnChannel.tsx
Normal file
58
src/views/home/quickActions/lnurl/LnChannel.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { FC } from 'react';
|
||||
import { ChannelRequest } from 'src/graphql/types';
|
||||
import styled from 'styled-components';
|
||||
import { Title } from 'src/components/typography/Styled';
|
||||
import { Separation } from 'src/components/generic/Styled';
|
||||
import { getNodeLink, renderLine } from 'src/components/generic/helpers';
|
||||
import { ColorButton } from 'src/components/buttons/colorButton/ColorButton';
|
||||
import { useChannelLnUrlMutation } from 'src/graphql/mutations/__generated__/lnUrl.generated';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
|
||||
const ModalText = styled.div`
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
type LnChannelProps = {
|
||||
request: ChannelRequest;
|
||||
};
|
||||
|
||||
export const LnChannel: FC<LnChannelProps> = ({ request }) => {
|
||||
const { k1, callback, uri } = request;
|
||||
|
||||
const split = uri?.split('@');
|
||||
|
||||
const [channelLnUrl, { data, loading }] = useChannelLnUrlMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
onCompleted: data => toast.success(data.lnUrlChannel),
|
||||
});
|
||||
|
||||
if (!callback || !k1 || !uri) {
|
||||
return <ModalText>Missing information from LN Service</ModalText>;
|
||||
}
|
||||
|
||||
const callbackUrl = new URL(callback);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Channel</Title>
|
||||
<Separation />
|
||||
<ModalText>{`Request from ${callbackUrl.host}`}</ModalText>
|
||||
<Separation />
|
||||
{split?.[0] && renderLine('Peer', getNodeLink(split[0]))}
|
||||
<Separation />
|
||||
<ColorButton
|
||||
loading={loading}
|
||||
disabled={loading || !!data?.lnUrlChannel}
|
||||
fullWidth={true}
|
||||
withMargin={'16px 0 0'}
|
||||
onClick={() => {
|
||||
channelLnUrl({ variables: { uri, k1, callback } });
|
||||
}}
|
||||
>
|
||||
{`Initiate Channel Request`}
|
||||
</ColorButton>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -63,7 +63,7 @@ export const LnUrlCard = () => {
|
|||
<Card>
|
||||
<InputWithDeco
|
||||
value={lnurl}
|
||||
placeholder={'LnPay / LnWithdraw / LnAuth'}
|
||||
placeholder={'LnPay / LnWithdraw / LnChannel / LnAuth'}
|
||||
title={'LNURL'}
|
||||
inputCallback={value => setLnUrl(value)}
|
||||
onEnter={() => handleDecode()}
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Title } from 'src/components/typography/Styled';
|
|||
import { useFetchLnUrlMutation } from 'src/graphql/mutations/__generated__/lnUrl.generated';
|
||||
import { getErrorContent } from 'src/utils/error';
|
||||
import styled from 'styled-components';
|
||||
import { LnChannel } from './LnChannel';
|
||||
import { LnPay } from './LnPay';
|
||||
import { LnWithdraw } from './LnWithdraw';
|
||||
|
||||
|
@ -49,6 +50,10 @@ export const LnUrlModal: FC<lnUrlProps> = ({ url, type }) => {
|
|||
return <LnWithdraw request={data.fetchLnUrl} />;
|
||||
}
|
||||
|
||||
if (data?.fetchLnUrl?.__typename === 'ChannelRequest') {
|
||||
return <LnChannel request={data.fetchLnUrl} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title>Login</Title>
|
||||
|
|
Loading…
Add table
Reference in a new issue