chore: lnurl channel

This commit is contained in:
apotdevin 2021-06-08 10:58:43 +02:00
parent 4399a046c6
commit 396074fb7a
No known key found for this signature in database
GPG key ID: 4403F1DFBE779457
10 changed files with 214 additions and 6 deletions

View file

@ -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';
},
},

View file

@ -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!

View file

@ -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!

View file

@ -2,7 +2,8 @@
"possibleTypes": {
"LnUrlRequest": [
"WithdrawRequest",
"PayRequest"
"PayRequest",
"ChannelRequest"
],
"Transaction": [
"InvoiceType",

View file

@ -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>;

View file

@ -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)
}
`;

View file

@ -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'];

View 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>
</>
);
};

View file

@ -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()}

View file

@ -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>